UNPKG

5.44 kBJavaScriptView Raw
1import Vue from 'vue';
2import {
3 PopupManager
4} from 'element-ui/src/utils/popup';
5
6const PopperJS = Vue.prototype.$isServer ? function() {} : require('./popper');
7const stop = e => e.stopPropagation();
8
9/**
10 * @param {HTMLElement} [reference=$refs.reference] - The reference element used to position the popper.
11 * @param {HTMLElement} [popper=$refs.popper] - The HTML element used as popper, or a configuration used to generate the popper.
12 * @param {String} [placement=button] - Placement of the popper accepted values: top(-start, -end), right(-start, -end), bottom(-start, -end), left(-start, -end)
13 * @param {Number} [offset=0] - Amount of pixels the popper will be shifted (can be negative).
14 * @param {Boolean} [visible=false] Visibility of the popup element.
15 * @param {Boolean} [visible-arrow=false] Visibility of the arrow, no style.
16 */
17export default {
18 props: {
19 transformOrigin: {
20 type: [Boolean, String],
21 default: true
22 },
23 placement: {
24 type: String,
25 default: 'bottom'
26 },
27 boundariesPadding: {
28 type: Number,
29 default: 5
30 },
31 reference: {},
32 popper: {},
33 offset: {
34 default: 0
35 },
36 value: Boolean,
37 visibleArrow: Boolean,
38 arrowOffset: {
39 type: Number,
40 default: 35
41 },
42 appendToBody: {
43 type: Boolean,
44 default: true
45 },
46 popperOptions: {
47 type: Object,
48 default() {
49 return {
50 gpuAcceleration: false
51 };
52 }
53 }
54 },
55
56 data() {
57 return {
58 showPopper: false,
59 currentPlacement: ''
60 };
61 },
62
63 watch: {
64 value: {
65 immediate: true,
66 handler(val) {
67 this.showPopper = val;
68 this.$emit('input', val);
69 }
70 },
71
72 showPopper(val) {
73 if (this.disabled) return;
74 val ? this.updatePopper() : this.destroyPopper();
75 this.$emit('input', val);
76 }
77 },
78
79 methods: {
80 createPopper() {
81 if (this.$isServer) return;
82 this.currentPlacement = this.currentPlacement || this.placement;
83 if (!/^(top|bottom|left|right)(-start|-end)?$/g.test(this.currentPlacement)) {
84 return;
85 }
86
87 const options = this.popperOptions;
88 const popper = this.popperElm = this.popperElm || this.popper || this.$refs.popper;
89 let reference = this.referenceElm = this.referenceElm || this.reference || this.$refs.reference;
90
91 if (!reference &&
92 this.$slots.reference &&
93 this.$slots.reference[0]) {
94 reference = this.referenceElm = this.$slots.reference[0].elm;
95 }
96
97 if (!popper || !reference) return;
98 if (this.visibleArrow) this.appendArrow(popper);
99 if (this.appendToBody) document.body.appendChild(this.popperElm);
100 if (this.popperJS && this.popperJS.destroy) {
101 this.popperJS.destroy();
102 }
103
104 options.placement = this.currentPlacement;
105 options.offset = this.offset;
106 options.arrowOffset = this.arrowOffset;
107 this.popperJS = new PopperJS(reference, popper, options);
108 this.popperJS.onCreate(_ => {
109 this.$emit('created', this);
110 this.resetTransformOrigin();
111 this.$nextTick(this.updatePopper);
112 });
113 if (typeof options.onUpdate === 'function') {
114 this.popperJS.onUpdate(options.onUpdate);
115 }
116 this.popperJS._popper.style.zIndex = PopupManager.nextZIndex();
117 this.popperElm.addEventListener('click', stop);
118 },
119
120 updatePopper() {
121 const popperJS = this.popperJS;
122 if (popperJS) {
123 popperJS.update();
124 if (popperJS._popper) {
125 popperJS._popper.style.zIndex = PopupManager.nextZIndex();
126 }
127 } else {
128 this.createPopper();
129 }
130 },
131
132 doDestroy(forceDestroy) {
133 /* istanbul ignore if */
134 if (!this.popperJS || (this.showPopper && !forceDestroy)) return;
135 this.popperJS.destroy();
136 this.popperJS = null;
137 },
138
139 destroyPopper() {
140 if (this.popperJS) {
141 this.resetTransformOrigin();
142 }
143 },
144
145 resetTransformOrigin() {
146 if (!this.transformOrigin) return;
147 let placementMap = {
148 top: 'bottom',
149 bottom: 'top',
150 left: 'right',
151 right: 'left'
152 };
153 let placement = this.popperJS._popper.getAttribute('x-placement').split('-')[0];
154 let origin = placementMap[placement];
155 this.popperJS._popper.style.transformOrigin = typeof this.transformOrigin === 'string'
156 ? this.transformOrigin
157 : ['top', 'bottom'].indexOf(placement) > -1 ? `center ${ origin }` : `${ origin } center`;
158 },
159
160 appendArrow(element) {
161 let hash;
162 if (this.appended) {
163 return;
164 }
165
166 this.appended = true;
167
168 for (let item in element.attributes) {
169 if (/^_v-/.test(element.attributes[item].name)) {
170 hash = element.attributes[item].name;
171 break;
172 }
173 }
174
175 const arrow = document.createElement('div');
176
177 if (hash) {
178 arrow.setAttribute(hash, '');
179 }
180 arrow.setAttribute('x-arrow', '');
181 arrow.className = 'popper__arrow';
182 element.appendChild(arrow);
183 }
184 },
185
186 beforeDestroy() {
187 this.doDestroy(true);
188 if (this.popperElm && this.popperElm.parentNode === document.body) {
189 this.popperElm.removeEventListener('click', stop);
190 document.body.removeChild(this.popperElm);
191 }
192 },
193
194 // call destroy in keep-alive mode
195 deactivated() {
196 this.$options.beforeDestroy[0].call(this);
197 }
198};