kingdot
Version:
A UI Components Library For Vue
177 lines (174 loc) • 5.89 kB
JavaScript
import { createPopper } from '@popperjs/core/dist/umd/popper.js';
// 实际使用的位置和popper.js的位置
const placements = [
'top', 'top-start', 'top-end',
'right', 'right-start', 'right-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end'
];
export default {
props: {
placement: {
type: String,
default: 'top',
validator(v) {
return placements.indexOf(v) !== -1;
}
},
popperModifiers: {
type: Array,
default: () => []
},
hideDestroy: {
type: Boolean,
default: true
},
sameWidth: {
type: Boolean,
default: false
}
},
data() {
return {
placements: placements,
popper: null,
reference: null,
visible: false,
visibleArrow: false,
arrowEl: null,
currentPlacement: ''
};
},
watch: {
visible(val) {
if (val) {
this.updatePopper();
} else {
this.destroyPopper();
}
},
placement() {
this.currentPlacement = this.placement;
if (this.popperJS) {
this.popperJS.setOptions({
placement: this.currentPlacement
});
}
}
},
computed: {
placementCls() {
return [`kd-tooltip-placement-${this.currentPlacement}`];
}
},
created() {
this.currentPlacement = this.placement;
},
methods: {
_createPopper() {
const self = this;
if (this.placements.indexOf(this.placement) === -1) return;
this.popper = this.popper || this.$refs.popper;
const options = Object.create(null);
if (!this.popper || !this.reference) return;
if (this.visibleArrow) this.appendArrow(this.popper);
document.body.appendChild(this.popper);
if (this.popperJS && this.popperJS.destroy) {
this.popperJS.destroy();
}
options.placement = this.currentPlacement;
options.strategy = 'fixed';
const defaultModifiers = [
// computeStyle
{
name: 'computeStyle',
options: {
gpuAcceleration: true,
adaptive: false
}
},
// arrow
{
name: 'arrow',
enabled: true,
options: {
element: this.arrowEl
}
},
// applyArrowHide: hide the arrow once it reaches the edge of its popper
{
name: 'applyArrowHide',
enabled: true,
phase: 'write',
fn({ state }) {
const { arrow } = state.elements;
if (arrow) {
if (state.modifiersData.arrow.centerOffset !== 0) {
arrow.setAttribute('data-hide', '');
} else {
arrow.removeAttribute('data-hide');
}
}
}
},
// update current placement
{
name: 'updateCurrentPlacementModifiers',
enabled: true,
phase: 'main',
fn({ state }) {
self.currentPlacement = state.placement;
}
},
// make your popper the same width as the reference
{
name: 'sameWidth',
enabled: this.sameWidth,
phase: 'beforeWrite',
requires: ['computeStyles'],
fn: ({ state }) => {
state.styles.popper.width = `${state.rects.reference.width}px`;
},
effect: ({ state }) => {
state.elements.popper.style.width = `${
state.elements.reference.offsetWidth
}px`;
}
}
];
options.modifiers = [...defaultModifiers, ...this.popperModifiers];
options.onFirstUpdate = () => {
this.currentPlacement = this.popper.getAttribute('data-popper-placement');
};
this.popperJS = createPopper(this.reference, this.popper, options);
},
updatePopper() {
if (this.popperJS) {
this.popperJS.update();
} else {
this._createPopper();
}
},
destroyPopper(remove) {
if (!this.popperJS) return;
this.popperJS.destroy();
this.popperJS = null;
if (this.popper && this.popper.parentNode === document.body && (remove || this.hideDestroy)) {
document.body.removeChild(this.popper);
}
this.currentPlacement = this.placement; // reset
},
appendArrow(element) {
if (this.appended) return;
this.appended = true;
const arrow = document.createElement('div');
arrow.className = 'kd-tooltip-arrow';
arrow.setAttribute('data-popper-arrow', '');
this.arrowEl = arrow;
element.querySelector(`.kd-tooltip-content`).appendChild(arrow);
}
},
beforeDestroy() {
this.destroyPopper(true);
}
};