• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

uiv-lib / uiv / 9515230746

14 Jun 2024 11:17AM UTC coverage: 86.79% (-0.07%) from 86.862%
9515230746

Pull #849

github

web-flow
Merge 438781419 into e3c11752f
Pull Request #849: chore(deps): update dependency vite to v5

859 of 1065 branches covered (80.66%)

Branch coverage included in aggregate %.

1539 of 1698 relevant lines covered (90.64%)

185.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.11
/src/mixins/popup.mixin.js
1
import {
2
  PLACEMENTS,
3
  on,
4
  off,
5
  EVENTS,
6
  TRIGGERS,
7
  removeFromDom,
8
  removeClass,
9
  hasClass,
10
  setTooltipPosition,
11
  addClass,
12
  getOpenModalNum,
13
  getElementBySelectorOrRef,
14
} from '../utils/dom.utils';
15
import { isFunction } from '../utils/object.utils';
16

17
const SHOW_CLASS = 'in';
27✔
18

19
export default {
20
  props: {
21
    modelValue: {
22
      type: Boolean,
23
      default: false,
24
    },
25
    tag: {
26
      type: String,
27
      default: 'span',
28
    },
29
    placement: {
30
      type: String,
31
      default: PLACEMENTS.TOP,
32
    },
33
    autoPlacement: {
34
      type: Boolean,
35
      default: true,
36
    },
37
    appendTo: {
38
      type: null,
39
      default: 'body',
40
    },
41
    positionBy: {
42
      type: null,
43
      default: null,
44
    },
45
    transition: {
46
      type: Number,
47
      default: 150,
48
    },
49
    hideDelay: {
50
      type: Number,
51
      default: 0,
52
    },
53
    showDelay: {
54
      type: Number,
55
      default: 0,
56
    },
57
    enable: {
58
      type: Boolean,
59
      default: true,
60
    },
61
    enterable: {
62
      type: Boolean,
63
      default: true,
64
    },
65
    target: null,
66
    viewport: null,
67
    customClass: String,
68
  },
69
  data() {
70
    return {
76✔
71
      triggerEl: null,
72
      hideTimeoutId: 0,
73
      showTimeoutId: 0,
74
      transitionTimeoutId: 0,
75
      autoTimeoutId: 0,
76
    };
77
  },
78
  watch: {
79
    modelValue(v) {
80
      v ? this.show() : this.hide();
6✔
81
    },
82
    trigger() {
83
      this.clearListeners();
2✔
84
      this.initListeners();
2✔
85
    },
86
    target(value) {
87
      this.clearListeners();
2✔
88
      this.initTriggerElByTarget(value);
2✔
89
      this.initListeners();
2✔
90
    },
91
    allContent(value) {
92
      // can not use value because it can not detect slot changes
93
      if (this.isNotEmpty()) {
6✔
94
        // reset position while content changed & is shown
95
        // nextTick is required
96
        this.$nextTick(() => {
4✔
97
          /* istanbul ignore else */
98
          if (this.isShown()) {
4✔
99
            this.resetPosition();
2✔
100
          }
101
        });
102
      } else {
103
        this.hide();
2✔
104
      }
105
    },
106
    enable(value) {
107
      // hide if enable changed to false
108
      /* istanbul ignore else */
109
      if (!value) {
3✔
110
        this.hide();
2✔
111
      }
112
    },
113
  },
114
  mounted() {
115
    removeFromDom(this.$refs.popup);
76✔
116
    this.$nextTick(() => {
76✔
117
      this.initTriggerElByTarget(this.target);
76✔
118
      this.initListeners();
76✔
119
      if (this.modelValue) {
76✔
120
        this.show();
12✔
121
      }
122
    });
123
  },
124
  beforeUnmount() {
125
    this.clearListeners();
76✔
126
    removeFromDom(this.$refs.popup);
76✔
127
  },
128
  methods: {
129
    initTriggerElByTarget(target) {
130
      if (target) {
78✔
131
        // target exist
132
        this.triggerEl = getElementBySelectorOrRef(target);
54✔
133
      } else {
134
        // find special element
135
        const trigger = this.$el.querySelector('[data-role="trigger"]');
24✔
136
        if (trigger) {
24✔
137
          this.triggerEl = trigger;
9✔
138
        } else {
139
          // use the first child
140
          const firstChild = this.$el.querySelector('*');
15✔
141
          this.triggerEl = firstChild === this.$refs.popup ? null : firstChild;
15!
142
        }
143
      }
144
    },
145
    initListeners() {
146
      if (this.triggerEl) {
80✔
147
        if (this.trigger === TRIGGERS.HOVER) {
78✔
148
          on(this.triggerEl, EVENTS.MOUSE_ENTER, this.show);
4✔
149
          on(this.triggerEl, EVENTS.MOUSE_LEAVE, this.hide);
4✔
150
        } else if (this.trigger === TRIGGERS.FOCUS) {
74✔
151
          on(this.triggerEl, EVENTS.FOCUS, this.show);
7✔
152
          on(this.triggerEl, EVENTS.BLUR, this.hide);
7✔
153
        } else if (this.trigger === TRIGGERS.HOVER_FOCUS) {
67✔
154
          on(this.triggerEl, EVENTS.MOUSE_ENTER, this.handleAuto);
19✔
155
          on(this.triggerEl, EVENTS.MOUSE_LEAVE, this.handleAuto);
19✔
156
          on(this.triggerEl, EVENTS.FOCUS, this.handleAuto);
19✔
157
          on(this.triggerEl, EVENTS.BLUR, this.handleAuto);
19✔
158
        } else if (
48✔
159
          this.trigger === TRIGGERS.CLICK ||
84✔
160
          this.trigger === TRIGGERS.OUTSIDE_CLICK
161
        ) {
162
          on(this.triggerEl, EVENTS.CLICK, this.toggle);
46✔
163
        }
164
      }
165
      on(window, EVENTS.CLICK, this.windowClicked);
80✔
166
    },
167
    clearListeners() {
168
      if (this.triggerEl) {
80✔
169
        off(this.triggerEl, EVENTS.FOCUS, this.show);
71✔
170
        off(this.triggerEl, EVENTS.BLUR, this.hide);
71✔
171
        off(this.triggerEl, EVENTS.MOUSE_ENTER, this.show);
71✔
172
        off(this.triggerEl, EVENTS.MOUSE_LEAVE, this.hide);
71✔
173
        off(this.triggerEl, EVENTS.CLICK, this.toggle);
71✔
174
        off(this.triggerEl, EVENTS.MOUSE_ENTER, this.handleAuto);
71✔
175
        off(this.triggerEl, EVENTS.MOUSE_LEAVE, this.handleAuto);
71✔
176
        off(this.triggerEl, EVENTS.FOCUS, this.handleAuto);
71✔
177
        off(this.triggerEl, EVENTS.BLUR, this.handleAuto);
71✔
178
      }
179
      off(window, EVENTS.CLICK, this.windowClicked);
80✔
180
      this.clearTimeouts();
80✔
181
    },
182
    clearTimeouts() {
183
      if (this.hideTimeoutId) {
82✔
184
        clearTimeout(this.hideTimeoutId);
2✔
185
        this.hideTimeoutId = 0;
2✔
186
      }
187
      if (this.showTimeoutId) {
82✔
188
        clearTimeout(this.showTimeoutId);
3✔
189
        this.showTimeoutId = 0;
3✔
190
      }
191
      if (this.transitionTimeoutId) {
82✔
192
        clearTimeout(this.transitionTimeoutId);
2✔
193
        this.transitionTimeoutId = 0;
2✔
194
      }
195
      if (this.autoTimeoutId) {
82✔
196
        clearTimeout(this.autoTimeoutId);
2✔
197
        this.autoTimeoutId = 0;
2✔
198
      }
199
    },
200
    resetPosition() {
201
      const popup = this.$refs.popup;
48✔
202
      /* istanbul ignore else */
203
      if (popup) {
48✔
204
        setTooltipPosition(
48✔
205
          popup,
206
          this.triggerEl,
207
          this.placement,
208
          this.autoPlacement,
209
          this.appendTo,
210
          this.positionBy,
211
          this.viewport
212
        );
213
        popup.offsetHeight;
48✔
214
      }
215
    },
216
    hideOnLeave() {
217
      if (
×
218
        this.trigger === TRIGGERS.HOVER ||
×
219
        (this.trigger === TRIGGERS.HOVER_FOCUS &&
220
          !this.triggerEl.matches(':focus'))
221
      ) {
222
        this.$hide();
×
223
      }
224
    },
225
    toggle() {
226
      if (this.isShown()) {
51✔
227
        this.hide();
23✔
228
      } else {
229
        this.show();
28✔
230
      }
231
    },
232
    show() {
233
      if (
58✔
234
        this.enable &&
220✔
235
        this.triggerEl &&
236
        this.isNotEmpty() &&
237
        !this.isShown()
238
      ) {
239
        const popUpAppendedContainer = this.hideTimeoutId > 0; // weird condition
51✔
240
        if (popUpAppendedContainer) {
51!
241
          clearTimeout(this.hideTimeoutId);
×
242
          this.hideTimeoutId = 0;
×
243
        }
244
        if (this.transitionTimeoutId > 0) {
51!
245
          clearTimeout(this.transitionTimeoutId);
×
246
          this.transitionTimeoutId = 0;
×
247
        }
248
        clearTimeout(this.showTimeoutId);
51✔
249
        this.showTimeoutId = setTimeout(() => {
51✔
250
          this.showTimeoutId = 0;
46✔
251
          const popup = this.$refs.popup;
46✔
252
          if (popup) {
46!
253
            const alreadyOpenModalNum = getOpenModalNum();
46✔
254
            if (alreadyOpenModalNum > 1) {
46!
255
              const defaultZ = this.name === 'popover' ? 1060 : 1070;
×
256
              const offset = (alreadyOpenModalNum - 1) * 20;
×
257
              popup.style.zIndex = `${defaultZ + offset}`;
×
258
            }
259
            // add to dom
260
            if (!popUpAppendedContainer) {
46!
261
              popup.className = `${this.name} ${this.placement} ${
46✔
262
                this.customClass ? this.customClass : ''
46!
263
              } fade`;
264
              const container = getElementBySelectorOrRef(this.appendTo);
46✔
265
              container.appendChild(popup);
46✔
266
              this.resetPosition();
46✔
267
            }
268
            addClass(popup, SHOW_CLASS);
46✔
269
            this.$emit('update:modelValue', true);
46✔
270
            this.$emit('show');
46✔
271
          }
272
        }, this.showDelay);
273
      }
274
    },
275
    hide() {
276
      if (this.showTimeoutId > 0) {
40!
277
        clearTimeout(this.showTimeoutId);
×
278
        this.showTimeoutId = 0;
×
279
      }
280

281
      if (!this.isShown()) {
40✔
282
        return;
2✔
283
      }
284
      if (
38✔
285
        this.enterable &&
113✔
286
        (this.trigger === TRIGGERS.HOVER ||
287
          this.trigger === TRIGGERS.HOVER_FOCUS)
288
      ) {
289
        clearTimeout(this.hideTimeoutId);
7✔
290
        this.hideTimeoutId = setTimeout(() => {
7✔
291
          this.hideTimeoutId = 0;
7✔
292
          const popup = this.$refs.popup;
7✔
293
          if (popup && !popup.matches(':hover')) {
7✔
294
            this.$hide();
6✔
295
          }
296
        }, 100);
297
      } else {
298
        this.$hide();
31✔
299
      }
300
    },
301
    $hide() {
302
      if (this.isShown()) {
37!
303
        clearTimeout(this.hideTimeoutId);
37✔
304
        this.hideTimeoutId = setTimeout(() => {
37✔
305
          this.hideTimeoutId = 0;
33✔
306
          removeClass(this.$refs.popup, SHOW_CLASS);
33✔
307
          // gives fade out time
308
          this.transitionTimeoutId = setTimeout(() => {
33✔
309
            this.transitionTimeoutId = 0;
33✔
310
            removeFromDom(this.$refs.popup);
33✔
311
            this.$emit('update:modelValue', false);
33✔
312
            this.$emit('hide');
33✔
313
          }, this.transition);
314
        }, this.hideDelay);
315
      }
316
    },
317
    isShown() {
318
      return hasClass(this.$refs.popup, SHOW_CLASS);
305✔
319
    },
320
    windowClicked(event) {
321
      if (
127✔
322
        this.triggerEl &&
750✔
323
        isFunction(this.triggerEl.contains) &&
324
        !this.triggerEl.contains(event.target) &&
325
        this.trigger === TRIGGERS.OUTSIDE_CLICK &&
326
        !(this.$refs.popup && this.$refs.popup.contains(event.target)) &&
126✔
327
        this.isShown()
328
      ) {
329
        this.hide();
2✔
330
      }
331
    },
332
    handleAuto() {
333
      clearTimeout(this.autoTimeoutId);
13✔
334
      this.autoTimeoutId = setTimeout(() => {
13✔
335
        this.autoTimeoutId = 0;
13✔
336
        if (this.triggerEl.matches(':hover, :focus')) {
13✔
337
          this.show();
7✔
338
        } else {
339
          this.hide();
6✔
340
        }
341
      }, 20); // 20ms make firefox happy
342
    },
343
  },
344
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc