2
0
mirror of https://github.com/frappe/books.git synced 2025-02-06 05:58:35 +00:00
books/src/components/Popover.vue

160 lines
3.3 KiB
Vue
Raw Normal View History

2019-11-22 00:50:30 +05:30
<template>
<div ref="reference">
2019-11-22 00:50:30 +05:30
<div class="h-full">
<slot name="target" :togglePopover="togglePopover"></slot>
2019-11-22 00:50:30 +05:30
</div>
<portal to="popovers">
<div
ref="popover"
:class="popoverClass"
class="bg-white rounded border min-w-40 shadow-md popover-container relative"
v-show="isOpen"
>
<div class="popover-arrow" ref="popover-arrow"></div>
<slot name="content" :togglePopover="togglePopover"></slot>
</div>
</portal>
2019-11-22 00:50:30 +05:30
</div>
</template>
<script>
import { createPopper } from '@popperjs/core';
2019-11-22 00:50:30 +05:30
export default {
name: 'Popover',
props: {
show: {
default: null
},
right: Boolean,
placement: {
type: String,
default: 'bottom-start'
},
popoverClass: [String, Object, Array]
},
watch: {
show(value) {
if (value === true) {
this.open();
}
if (value === false) {
this.close();
}
}
},
2019-11-22 00:50:30 +05:30
data() {
return {
isOpen: false
2019-11-22 00:50:30 +05:30
};
},
mounted() {
let listener = e => {
let $els = [this.$refs.reference, this.$refs.popover];
let insideClick = $els.some(
$el => $el && (e.target === $el || $el.contains(e.target))
);
if (insideClick) {
return;
2019-11-22 00:50:30 +05:30
}
this.close();
};
if (this.show == null) {
document.addEventListener('click', listener);
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('click', listener);
});
}
},
beforeDestroy() {
this.popper && this.popper.destroy();
2019-11-22 00:50:30 +05:30
},
methods: {
setupPopper() {
if (!this.popper) {
this.popper = createPopper(this.$refs.reference, this.$refs.popover, {
placement: this.placement,
modifiers: [
{
name: 'arrow',
options: {
element: this.$refs['popover-arrow']
}
},
{
name: 'offset',
options: {
offset: [0, 10]
}
}
]
});
} else {
this.popper.update();
}
},
togglePopover(flag) {
2019-11-22 00:50:30 +05:30
if (flag == null) {
flag = !this.isOpen;
}
flag = Boolean(flag);
if (flag) {
this.open();
} else {
this.close();
}
},
open() {
if (this.isOpen) {
return;
}
this.isOpen = true;
this.$nextTick(() => {
this.setupPopper();
});
this.$emit('open');
},
close() {
if (!this.isOpen) {
return;
2019-11-22 00:50:30 +05:30
}
this.isOpen = false;
this.$emit('close');
2019-11-22 00:50:30 +05:30
}
}
};
</script>
<style scoped>
.popover-arrow,
.popover-arrow::after {
position: absolute;
width: theme('spacing.3');
height: theme('spacing.3');
z-index: -1;
}
.popover-arrow::after {
content: '';
background: white;
transform: rotate(45deg);
border-top: 1px solid theme('borderColor.gray.400');
border-left: 1px solid theme('borderColor.gray.400');
}
.popover-container[data-popper-placement^='top'] > .popover-arrow {
bottom: -6px;
}
.popover-container[data-popper-placement^='bottom'] > .popover-arrow {
top: -6px;
}
.popover-container[data-popper-placement^='left'] > .popover-arrow {
right: -6px;
}
.popover-container[data-popper-placement^='right'] > .popover-arrow {
left: -6px;
}
</style>