wave-ui 1.45.15 → 1.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  <template lang="pug">
2
- .w-tooltip-wrap(:class="{ 'w-tooltip-wrap--attached': !detachTo }")
2
+ .w-tooltip-wrap
3
3
  slot(name="activator" :on="eventHandlers")
4
4
  transition(:name="transitionName" appear)
5
- .w-tooltip(ref="tooltip" v-show="showTooltip" :class="classes" :style="styles")
5
+ .w-tooltip(v-if="detachableVisible" ref="detachable" :class="classes" :style="styles")
6
6
  slot
7
7
  </template>
8
8
 
@@ -17,12 +17,13 @@
17
17
  */
18
18
 
19
19
  import { objectifyClasses } from '../utils/index'
20
- import { consoleWarn } from '../utils/console'
20
+ import DetachableMixin from '../mixins/detachable'
21
21
 
22
- const marginFromWindowSide = 4 // Amount of px from a window side, instead of overflowing.
22
+ // const marginFromWindowSide = 4 // Amount of px from a window side, instead of overflowing.
23
23
 
24
24
  export default {
25
25
  name: 'w-tooltip',
26
+ mixins: [DetachableMixin],
26
27
 
27
28
  props: {
28
29
  value: {},
@@ -36,32 +37,44 @@ export default {
36
37
  transition: { type: String },
37
38
  tooltipClass: { type: [String, Object, Array] },
38
39
  // Position.
39
- detachTo: {},
40
+ detachTo: { type: [String, Boolean, Object], deprecated: true },
41
+ appendTo: { type: [String, Boolean, Object] },
40
42
  fixed: { type: Boolean },
41
43
  top: { type: Boolean },
42
44
  bottom: { type: Boolean },
43
45
  left: { type: Boolean },
44
46
  right: { type: Boolean },
45
- zIndex: { type: [Number, String, Boolean] }
47
+ alignTop: { type: Boolean },
48
+ alignBottom: { type: Boolean },
49
+ alignLeft: { type: Boolean },
50
+ alignRight: { type: Boolean },
51
+ zIndex: { type: [Number, String, Boolean] },
52
+ persistent: { type: Boolean },
53
+ noPosition: { type: Boolean }
46
54
  },
47
55
 
48
56
  emits: ['input', 'update:modelValue', 'open', 'close'],
49
57
 
50
58
  data: () => ({
51
- showTooltip: false,
59
+ detachableVisible: false,
60
+ hoveringActivator: false,
52
61
  // The activator coordinates.
53
- coordinates: {
62
+ detachableCoords: {
54
63
  top: 0,
55
- left: 0,
56
- width: 0,
57
- height: 0
64
+ left: 0
58
65
  },
59
66
  activatorEl: null,
60
- tooltipEl: null,
67
+ detachableEl: null,
61
68
  timeoutId: null
62
69
  }),
63
70
 
64
71
  computed: {
72
+ /**
73
+ * Other computed in the detachable mixin:
74
+ * - `appendToTarget`
75
+ * - `detachableParentEl`
76
+ **/
77
+
65
78
  tooltipClasses () {
66
79
  return objectifyClasses(this.tooltipClass)
67
80
  },
@@ -71,34 +84,6 @@ export default {
71
84
  return this.transition || `w-tooltip-slide-fade-${direction}`
72
85
  },
73
86
 
74
- // DOM element to attach tooltip to.
75
- // ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
76
- detachToTarget () {
77
- const defaultTarget = '.w-app'
78
-
79
- let target = this.detachTo || defaultTarget
80
- if (target === true) target = defaultTarget
81
- else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
82
- else if (typeof target === 'object' && !target.nodeType) {
83
- target = defaultTarget
84
- consoleWarn('Invalid node provided in w-tooltip `attach-to`. Falling back to .w-app.', this)
85
- }
86
- if (typeof target === 'string') target = document.querySelector(target)
87
-
88
- if (!target) {
89
- consoleWarn(`Unable to locate ${this.detachTo ? `target ${this.detachTo}` : defaultTarget}`, this)
90
- target = document.querySelector(defaultTarget)
91
- }
92
-
93
- return target
94
- },
95
-
96
- // DOM element that will receive the tooltip.
97
- // ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
98
- tooltipParentEl () {
99
- return this.detachTo ? this.detachToTarget : this.$el
100
- },
101
-
102
87
  position () {
103
88
  return (
104
89
  (this.top && 'top') ||
@@ -109,34 +94,14 @@ export default {
109
94
  )
110
95
  },
111
96
 
112
- tooltipCoordinates () {
113
- const coords = {}
114
- const { top, left, width, height } = this.coordinates
115
-
116
- switch (this.position) {
117
- case 'top': {
118
- coords.top = top
119
- coords.left = left + width / 2 // left: 50%.
120
- break
121
- }
122
- case 'bottom': {
123
- coords.top = top + height
124
- coords.left = left + width / 2 // left: 50%.
125
- break
126
- }
127
- case 'left': {
128
- coords.top = top + height / 2 // top: 50%.
129
- coords.left = left
130
- break
131
- }
132
- case 'right': {
133
- coords.top = top + height / 2 // top: 50%.
134
- coords.left = left + width
135
- break
136
- }
137
- }
138
-
139
- return coords
97
+ alignment () {
98
+ return (
99
+ (['top', 'bottom'].includes(this.position) && this.alignLeft && 'left') ||
100
+ (['top', 'bottom'].includes(this.position) && this.alignRight && 'right') ||
101
+ (['left', 'right'].includes(this.position) && this.alignTop && 'top') ||
102
+ (['left', 'right'].includes(this.position) && this.alignBottom && 'bottom') ||
103
+ ''
104
+ )
140
105
  },
141
106
 
142
107
  classes () {
@@ -144,12 +109,12 @@ export default {
144
109
  [this.color]: this.color,
145
110
  [`${this.bgColor}--bg`]: this.bgColor,
146
111
  ...this.tooltipClasses,
147
- [`w-tooltip--${this.position}`]: true,
112
+ [`w-tooltip--${this.position}`]: !this.noPosition,
113
+ [`w-tooltip--align-${this.alignment}`]: !this.noPosition && this.alignment,
148
114
  'w-tooltip--tile': this.tile,
149
115
  'w-tooltip--round': this.round,
150
116
  'w-tooltip--shadow': this.shadow,
151
117
  'w-tooltip--fixed': this.fixed,
152
- 'w-tooltip--active': this.showTooltip,
153
118
  'w-tooltip--no-border': this.noBorder || this.bgColor,
154
119
  'w-tooltip--custom-transition': this.transition
155
120
  }
@@ -159,8 +124,8 @@ export default {
159
124
  styles () {
160
125
  return {
161
126
  zIndex: this.zIndex || this.zIndex === 0 || null,
162
- top: (this.tooltipCoordinates.top && `${~~this.tooltipCoordinates.top}px`) || null,
163
- left: (this.tooltipCoordinates.left && `${~~this.tooltipCoordinates.left}px`) || null,
127
+ top: (this.detachableCoords.top && `${~~this.detachableCoords.top}px`) || null,
128
+ left: (this.detachableCoords.left && `${~~this.detachableCoords.left}px`) || null,
164
129
  '--w-tooltip-bg-color': this.$waveui.colors[this.bgColor || 'white']
165
130
  }
166
131
  },
@@ -172,8 +137,14 @@ export default {
172
137
  handlers = {
173
138
  focus: this.toggle,
174
139
  blur: this.toggle,
175
- mouseenter: this.toggle,
176
- mouseleave: this.toggle
140
+ mouseenter: e => {
141
+ this.hoveringActivator = true
142
+ this.open(e)
143
+ },
144
+ mouseleave: e => {
145
+ this.hoveringActivator = false
146
+ this.close()
147
+ }
177
148
  }
178
149
 
179
150
  // Check the window exists: SSR-proof.
@@ -184,9 +155,19 @@ export default {
184
155
  },
185
156
 
186
157
  methods: {
158
+ /**
159
+ * Other methods in the `detachable` mixin:
160
+ * - `getActivatorCoordinates`
161
+ * - `computeDetachableCoords`
162
+ * - `onResize`
163
+ * - `onOutsideMousedown`
164
+ * - `insertInDOM`
165
+ * - `removeFromDOM`
166
+ **/
167
+
187
168
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
188
169
  toggle (e) {
189
- let shouldShowTooltip = this.showTooltip
170
+ let shouldShowTooltip = this.detachableVisible
190
171
  if (typeof window !== 'undefined' && 'ontouchstart' in window) {
191
172
  if (e.type === 'click') shouldShowTooltip = !shouldShowTooltip
192
173
  }
@@ -196,131 +177,101 @@ export default {
196
177
 
197
178
  this.timeoutId = clearTimeout(this.timeoutId)
198
179
  if (shouldShowTooltip) {
199
- this.coordinates = this.getCoordinates(e)
200
- // In `getCoordinates` accessing the tooltip computed styles takes a few ms (less than 10ms),
201
- // if we don't postpone the tooltip apparition it will start transition from a visible tooltip and
202
- // thus will not transition.
203
- this.timeoutId = setTimeout(() => {
204
- this.showTooltip = true
205
- this.$emit('update:modelValue', true)
206
- this.$emit('input', true)
207
- this.$emit('open')
208
- }, 10)
209
- }
210
- else {
211
- this.showTooltip = false
212
- this.$emit('update:modelValue', false)
213
- this.$emit('input', false)
214
- this.$emit('close')
180
+ this.$emit('update:modelValue', (this.detachableVisible = true))
181
+ this.$emit('input', true)
182
+ this.$emit('open')
183
+
184
+ this.open(e)
215
185
  }
186
+ else this.close()
216
187
  },
217
188
 
218
189
  // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
219
- getCoordinates () {
220
- const { top, left, width, height } = this.activatorEl.getBoundingClientRect()
221
- let coords = { top, left, width, height }
190
+ async open (e) {
191
+ this.detachableVisible = true
192
+ await this.insertInDOM()
222
193
 
223
- if (!this.fixed) {
224
- const { top: targetTop, left: targetLeft } = this.tooltipParentEl.getBoundingClientRect()
225
- coords = { ...coords, top: top - targetTop, left: left - targetLeft }
226
- }
194
+ if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
227
195
 
228
- const tooltipEl = this.$refs.tooltip
196
+ if (!this.noPosition) this.computeDetachableCoords(e)
229
197
 
230
- // 1. First display the tooltip but hide it (So we can get its dimension).
231
- tooltipEl.style.visibility = 'hidden'
232
- tooltipEl.style.display = 'table'
233
- const computedStyles = window.getComputedStyle(tooltipEl, null)
198
+ // In `getActivatorCoordinates` accessing the tooltip computed styles takes a few ms (less than 10ms),
199
+ // if we don't postpone the Tooltip apparition it will start transition from a visible tooltip and
200
+ // thus will not transition.
201
+ this.timeoutId = setTimeout(() => {
202
+ this.$emit('update:modelValue', true)
203
+ this.$emit('input', true)
204
+ this.$emit('open')
205
+ }, 0)
234
206
 
235
- // Keep fully in viewport.
236
- // --------------------------------------------------
237
- if (this.position === 'top' && ((top - tooltipEl.offsetHeight) < 0)) {
238
- const margin = -parseInt(computedStyles.getPropertyValue('margin-top'))
239
- coords.top -= top - tooltipEl.offsetHeight - margin - marginFromWindowSide
240
- }
241
- else if (this.position === 'left' && left - tooltipEl.offsetWidth < 0) {
242
- const margin = -parseInt(computedStyles.getPropertyValue('margin-left'))
243
- coords.left -= left - tooltipEl.offsetWidth - margin - marginFromWindowSide
244
- }
245
- else if (this.position === 'right' && left + width + tooltipEl.offsetWidth > window.innerWidth) {
246
- const margin = parseInt(computedStyles.getPropertyValue('margin-left'))
247
- coords.left -= left + width + tooltipEl.offsetWidth - window.innerWidth + margin + marginFromWindowSide
248
- }
249
- else if (this.position === 'bottom' && top + height + tooltipEl.offsetHeight > window.innerHeight) {
250
- const margin = parseInt(computedStyles.getPropertyValue('margin-top'))
251
- coords.top -= top + height + tooltipEl.offsetHeight - window.innerHeight + margin + marginFromWindowSide
252
- }
253
- // --------------------------------------------------
254
-
255
- // 2. Update left & top if there is a custom transition.
256
- // Tooltip position relies on transform translate, the custom animation may override the transform
257
- // property so do without it and subtract half width or height manually.
258
- if (this.transition) {
259
- // If tooltip is on top or bottom.
260
- if (['top', 'bottom'].includes(this.position)) coords.left -= tooltipEl.offsetWidth / 2
261
- // If tooltip is on left or right.
262
- if (['left', 'right'].includes(this.position)) coords.top -= tooltipEl.offsetHeight / 2
263
-
264
- if (this.position === 'left') coords.left -= tooltipEl.offsetWidth
265
- if (this.position === 'top') coords.top -= tooltipEl.offsetHeight
266
- }
267
-
268
- // 3. Hide the tooltip again so the transition happens correctly.
269
- tooltipEl.style.visibility = null
270
- tooltipEl.style.display = 'none'
271
-
272
- return coords
207
+ if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
208
+ if (!this.noPosition) window.addEventListener('resize', this.onResize)
273
209
  },
274
210
 
275
- insertTooltip () {
276
- const wrapper = this.$el
277
- this.tooltipEl = this.$refs.tooltip.$el || this.$refs.tooltip
278
-
279
- // Unwrap the activator element.
280
- wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
281
-
282
- // Move the tooltip elsewhere in the DOM.
283
- // wrapper.parentNode.insertBefore(this.$refs.tooltip, wrapper)
284
- this.detachToTarget.appendChild(this.$refs.tooltip)
285
- },
211
+ /**
212
+ * Closes the tooltip. Can happen on:
213
+ * - click of activator
214
+ * - hover outside if showOnHover
215
+ * - click inside tooltip if hideOnTooltipClick.
216
+ * / ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
217
+ *
218
+ * @param {Boolean} force when showOnHover is set to true, hovering tooltip should keep it open.
219
+ * But if hideOnTooltipClick is also set to true, this should force close
220
+ * even while hovering the tooltip.
221
+ */
222
+ async close (force = false) {
223
+ // Might be already closed.
224
+ // E.g. showOnHover & hideOnTooltipClick: on click, force hide then mouseleave is also firing.
225
+ if (!this.detachableVisible) return
226
+
227
+ if (this.showOnHover && !force) {
228
+ await new Promise(resolve => setTimeout(resolve, 10))
229
+ if (this.showOnHover && this.hoveringActivator) return
230
+ }
286
231
 
287
- removeTooltip () {
288
- if (this.tooltipEl && this.tooltipEl.parentNode) this.tooltipEl.remove()
232
+ this.$emit('update:modelValue', (this.detachableVisible = false))
233
+ this.$emit('input', false)
234
+ this.$emit('close')
235
+ // Remove the mousedown listener if the tooltip got closed without a mousedown outside of the tooltip.
236
+ document.removeEventListener('mousedown', this.onOutsideMousedown)
237
+ window.removeEventListener('resize', this.onResize)
289
238
  }
290
-
291
239
  },
292
240
 
293
241
  mounted () {
294
- this.activatorEl = this.$el.firstElementChild
295
- if (this.detachTo) this.insertTooltip()
242
+ const wrapper = this.$el
243
+ this.activatorEl = wrapper.firstElementChild
244
+
245
+ // Unwrap the activator element.
246
+ wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
296
247
 
297
248
  if (this.value) this.toggle({ type: 'click', target: this.activatorEl })
298
249
  },
299
250
 
300
251
  beforeDestroy () {
301
- this.removeTooltip()
252
+ this.removeFromDOM()
302
253
 
303
254
  if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
304
255
  },
305
256
 
306
257
  watch: {
307
258
  value (bool) {
308
- if (bool !== this.showTooltip) this.toggle({ type: 'click', target: this.activatorEl })
259
+ if (bool !== this.detachableVisible) this.toggle({ type: 'click', target: this.activatorEl })
309
260
  },
310
261
  detachTo () {
311
- this.removeTooltip()
312
- this.insertTooltip()
262
+ this.removeFromDOM()
263
+ this.insertInDOM()
264
+ },
265
+ appendTo () {
266
+ this.removeFromDOM()
267
+ this.insertInDOM()
313
268
  }
314
269
  }
315
270
  }
316
271
  </script>
317
272
 
318
273
  <style lang="scss">
319
- .w-tooltip-wrap {
320
- display: none;
321
-
322
- &--attached {display: inline-block;position: relative;}
323
- }
274
+ .w-tooltip-wrap {display: none;}
324
275
 
325
276
  .w-tooltip {
326
277
  // Fix Safari where `width: max-content` does not take padding and border into consideration.
@@ -348,22 +299,10 @@ export default {
348
299
  &--shadow {box-shadow: $box-shadow;}
349
300
  &--no-border {border: none;}
350
301
 
351
- &--top {
352
- transform: translate(-50%, -100%);
353
- margin-top: -3 * $base-increment;
354
- }
355
- &--bottom {
356
- transform: translateX(-50%);
357
- margin-top: 3 * $base-increment;
358
- }
359
- &--left {
360
- transform: translate(-100%, -50%);
361
- margin-left: -3 * $base-increment;
362
- }
363
- &--right {
364
- transform: translateY(-50%);
365
- margin-left: 3 * $base-increment;
366
- }
302
+ &--top {margin-top: -3 * $base-increment;}
303
+ &--bottom {margin-top: 3 * $base-increment;}
304
+ &--left {margin-left: -3 * $base-increment;}
305
+ &--right {margin-left: 3 * $base-increment;}
367
306
 
368
307
  &--custom-transition {transform: none;}
369
308
 
@@ -0,0 +1,189 @@
1
+ /**
2
+ * A detachable element is an element that can be appended to another DOM node
3
+ * (but keeping data-driven Vue DOM refreshes).
4
+ * This mixin is used by w-tooltip & w-menu.
5
+ */
6
+
7
+ import { consoleWarn } from '../utils/console'
8
+
9
+ export default {
10
+ computed: {
11
+ // DOM element to attach tooltip/menu to.
12
+ // ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
13
+ appendToTarget () {
14
+ const defaultTarget = '.w-app'
15
+
16
+ // Convert deprecated prop to renamed one.
17
+ if (this.detachTo && !this.appendTo) {
18
+ consoleWarn(`The ${this.$options.name} prop \`detach-to\` is deprecated. You can replace it with \`append-to\`.`, this)
19
+ }
20
+
21
+ let target = this.appendTo || this.detachTo || defaultTarget
22
+ if (target === true) target = defaultTarget
23
+ else if (this.appendTo === 'activator') target = this.$el.previousElementSibling
24
+ else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
25
+ else if (typeof target === 'object' && !target.nodeType) {
26
+ target = defaultTarget
27
+ consoleWarn(`Invalid node provided in ${this.$options.name} \`append-to\`. Falling back to .w-app.`, this)
28
+ }
29
+ if (typeof target === 'string') target = document.querySelector(target)
30
+
31
+ if (!target) {
32
+ consoleWarn(`Unable to locate ${this.appendTo ? `target ${this.appendTo}` : defaultTarget}`, this)
33
+ target = document.querySelector(defaultTarget)
34
+ }
35
+
36
+ return target
37
+ },
38
+
39
+ // DOM element that will receive the tooltip/menu.
40
+ // ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
41
+ detachableParentEl () {
42
+ return this.appendToTarget
43
+ }
44
+ },
45
+
46
+ methods: {
47
+ // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
48
+ getActivatorCoordinates (e) {
49
+ // Get the activator coordinates relative to window.
50
+ const { top, left, width, height } = (e ? e.target : this.activatorEl).getBoundingClientRect()
51
+ let coords = { top, left, width, height }
52
+
53
+ // If absolute position, adjust top & left.
54
+ if (!this.fixed) {
55
+ const { top: targetTop, left: targetLeft } = this.detachableParentEl.getBoundingClientRect()
56
+ const computedStyles = window.getComputedStyle(this.detachableParentEl, null)
57
+ coords = {
58
+ ...coords,
59
+ top: top - targetTop + this.detachableParentEl.scrollTop - parseInt(computedStyles.getPropertyValue('border-top-width')),
60
+ left: left - targetLeft + this.detachableParentEl.scrollLeft - parseInt(computedStyles.getPropertyValue('border-left-width'))
61
+ }
62
+ }
63
+
64
+ return coords
65
+ },
66
+
67
+ // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
68
+ computeDetachableCoords (e) {
69
+ // Get the activator coordinates.
70
+ let { top, left, width, height } = this.getActivatorCoordinates(e)
71
+
72
+ // 1. First display the menu but hide it (So we can get its dimension).
73
+ // --------------------------------------------------
74
+ this.detachableEl.style.visibility = 'hidden'
75
+ this.detachableEl.style.display = 'flex'
76
+ const computedStyles = window.getComputedStyle(this.detachableEl, null)
77
+
78
+ // 2. Position the menu top, left, right, bottom and apply chosen alignment.
79
+ // --------------------------------------------------
80
+ // Subtract half or full activator width or height and menu width or height according to the
81
+ // menu alignment.
82
+ // Note: the menu position relies on transform translate, the custom animation may override the
83
+ // css transform property so do without it i.e. no translateX(-50%), and recalculate top & left
84
+ // manually.
85
+ switch (this.position) {
86
+ case 'top': {
87
+ top -= this.detachableEl.offsetHeight
88
+ if (this.alignRight) {
89
+ // left: 100% of activator.
90
+ left += width - this.detachableEl.offsetWidth +
91
+ parseInt(computedStyles.getPropertyValue('border-right-width'))
92
+ }
93
+ else if (!this.alignLeft) left += (width - this.detachableEl.offsetWidth) / 2 // left: 50% of activator - half menu width.
94
+ break
95
+ }
96
+ case 'bottom': {
97
+ top += height
98
+ if (this.alignRight) {
99
+ // left: 100% of activator.
100
+ left += width - this.detachableEl.offsetWidth +
101
+ parseInt(computedStyles.getPropertyValue('border-right-width'))
102
+ }
103
+ else if (!this.alignLeft) left += (width - this.detachableEl.offsetWidth) / 2 // left: 50% of activator - half menu width.
104
+ break
105
+ }
106
+ case 'left': {
107
+ left -= this.detachableEl.offsetWidth
108
+ if (this.alignBottom) top += height - this.detachableEl.offsetHeight
109
+ else if (!this.alignTop) top += (height - this.detachableEl.offsetHeight) / 2 // top: 50% of activator - half menu height.
110
+ break
111
+ }
112
+ case 'right': {
113
+ left += width
114
+ if (this.alignBottom) {
115
+ top += height - this.detachableEl.offsetHeight +
116
+ parseInt(computedStyles.getPropertyValue('margin-top'))
117
+ }
118
+ else if (!this.alignTop) {
119
+ top += (height - this.detachableEl.offsetHeight) / 2 + // top: 50% of activator - half menu height.
120
+ parseInt(computedStyles.getPropertyValue('margin-top'))
121
+ }
122
+ break
123
+ }
124
+ }
125
+
126
+ // 3. Keep fully in viewport.
127
+ // @todo: do this.
128
+ // --------------------------------------------------
129
+ // if (this.position === 'top' && ((top - this.detachableEl.offsetHeight) < 0)) {
130
+ // const margin = - parseInt(computedStyles.getPropertyValue('margin-top'))
131
+ // top -= top - this.detachableEl.offsetHeight - margin - marginFromWindowSide
132
+ // }
133
+ // else if (this.position === 'left' && left - this.detachableEl.offsetWidth < 0) {
134
+ // const margin = - parseInt(computedStyles.getPropertyValue('margin-left'))
135
+ // left -= left - this.detachableEl.offsetWidth - margin - marginFromWindowSide
136
+ // }
137
+ // else if (this.position === 'right' && left + width + this.detachableEl.offsetWidth > window.innerWidth) {
138
+ // const margin = parseInt(computedStyles.getPropertyValue('margin-left'))
139
+ // left -= left + width + this.detachableEl.offsetWidth - window.innerWidth + margin + marginFromWindowSide
140
+ // }
141
+ // else if (this.position === 'bottom' && top + height + this.detachableEl.offsetHeight > window.innerHeight) {
142
+ // const margin = parseInt(computedStyles.getPropertyValue('margin-top'))
143
+ // top -= top + height + this.detachableEl.offsetHeight - window.innerHeight + margin + marginFromWindowSide
144
+ // }
145
+
146
+ // 4. Hide the menu again so the transition happens correctly.
147
+ // --------------------------------------------------
148
+ this.detachableEl.style.visibility = null
149
+
150
+ // The menu coordinates are also recalculated while resizing window with open menu: keep the menu visible.
151
+ if (!this.detachableVisible) this.detachableEl.style.display = 'none'
152
+
153
+ this.detachableCoords = { top, left }
154
+ },
155
+
156
+ onResize () {
157
+ if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
158
+ this.computeDetachableCoords()
159
+ },
160
+
161
+ // ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
162
+ onOutsideMousedown (e) {
163
+ if (!this.detachableEl.contains(e.target) && !this.activatorEl.contains(e.target)) {
164
+ this.$emit('update:modelValue', (this.detachableVisible = false))
165
+ this.$emit('input', false)
166
+ this.$emit('close')
167
+ document.removeEventListener('mousedown', this.onOutsideMousedown)
168
+ window.removeEventListener('resize', this.onResize)
169
+ }
170
+ },
171
+
172
+ insertInDOM () {
173
+ return new Promise(resolve => {
174
+ this.$nextTick(() => {
175
+ this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
176
+
177
+ // Move the tooltip/menu elsewhere in the DOM.
178
+ // wrapper.parentNode.insertBefore(this.detachableEl, wrapper)
179
+ this.appendToTarget.appendChild(this.detachableEl)
180
+ resolve()
181
+ })
182
+ })
183
+ },
184
+
185
+ removeFromDOM () {
186
+ if (this.detachableEl && this.detachableEl.parentNode) this.detachableEl.remove()
187
+ }
188
+ }
189
+ }
@@ -54,10 +54,24 @@
54
54
  margin-right: 0;
55
55
  }
56
56
 
57
- &#{$selector}--align-top:before {transform: none;top: (2 * $base-increment) - 1px;}
58
- &#{$selector}--align-bottom:before {transform: none;top: auto;bottom: (2 * $base-increment) - 1px;}
59
- &#{$selector}--align-left:before {transform: none;left: (2 * $base-increment) - 1px;}
60
- &#{$selector}--align-right:before {transform: none;left: auto;right: (2 * $base-increment) - 1px;}
57
+ &#{$selector}--align-top:before {
58
+ transform: none;
59
+ top: (2 * $base-increment) - 1px;
60
+ }
61
+ &#{$selector}--align-bottom:before {
62
+ transform: none;
63
+ top: auto;
64
+ bottom: (2 * $base-increment) - 1px;
65
+ }
66
+ &#{$selector}--align-left:before {
67
+ transform: none;
68
+ left: (2 * $base-increment) - 1px;
69
+ }
70
+ &#{$selector}--align-right:before {
71
+ transform: none;
72
+ left: auto;
73
+ right: (2 * $base-increment) - 1px;
74
+ }
61
75
  }
62
76
 
63
77
  // The colored triangle on top of `:before`.
@@ -11,7 +11,7 @@ $css-scope: '.w-app' !default; // Allows control on CSS rules priority.
11
11
  // True by default. False allows you to use an external CSS library (like Tailwind).
12
12
  $use-layout-classes: true !default;
13
13
 
14
- $base-font-size: 14px !default;
14
+ $base-font-size: 14px !default; // Must be a px unit.
15
15
  $base-increment: round(divide($base-font-size, 4)) !default;
16
16
  $layout-padding: $base-increment * 4 !default; // Applied on the .content-wrap tag.
17
17
  $border-radius: 3px !default;