wave-ui 2.32.3 → 2.33.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.
- package/dist/wave-ui.cjs.js +1 -1
- package/dist/wave-ui.css +1 -1
- package/dist/wave-ui.es.js +1656 -2801
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +1 -1
- package/src/wave-ui/components/w-accordion.vue +0 -8
- package/src/wave-ui/components/w-card.vue +12 -5
- package/src/wave-ui/components/w-dialog.vue +6 -5
- package/src/wave-ui/components/w-divider.vue +6 -2
- package/src/wave-ui/components/w-menu.vue +9 -33
- package/src/wave-ui/components/w-overlay.vue +27 -5
- package/src/wave-ui/components/w-select.vue +6 -3
- package/src/wave-ui/components/w-tabs/index.vue +10 -1
- package/src/wave-ui/components/w-toolbar.vue +46 -6
- package/src/wave-ui/components/w-tooltip.vue +1 -31
- package/src/wave-ui/mixins/detachable.js +79 -26
package/package.json
CHANGED
|
@@ -125,14 +125,6 @@ export default {
|
|
|
125
125
|
[item[this.itemColorKey]]: item[this.itemColorKey]
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
watch: {
|
|
131
|
-
modelValue (array) {
|
|
132
|
-
this.accordionItems.forEach((item, i) => {
|
|
133
|
-
item.expanded = (Array.isArray(array) && array[i]) || false
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
128
|
}
|
|
137
129
|
}
|
|
138
130
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-card(:class="classes"
|
|
2
|
+
.w-card(:class="classes")
|
|
3
3
|
.w-card__title(
|
|
4
4
|
v-if="$slots.title"
|
|
5
5
|
:class="{ 'w-card__title--has-toolbar': titleHasToolbar, ...titleClasses }")
|
|
@@ -71,10 +71,6 @@ export default {
|
|
|
71
71
|
'w-card--tile': this.tile,
|
|
72
72
|
'w-card--shadow': this.shadow
|
|
73
73
|
}
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
styles () {
|
|
77
|
-
return false
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
}
|
|
@@ -107,6 +103,17 @@ export default {
|
|
|
107
103
|
&__content {
|
|
108
104
|
padding: 3 * $base-increment;
|
|
109
105
|
flex-grow: 1;
|
|
106
|
+
|
|
107
|
+
// Only if there is no title bar.
|
|
108
|
+
&:first-child {
|
|
109
|
+
border-top-left-radius: inherit;
|
|
110
|
+
border-top-right-radius: inherit;
|
|
111
|
+
}
|
|
112
|
+
&:last-child {
|
|
113
|
+
// Only if there is no actions bar.
|
|
114
|
+
border-bottom-left-radius: inherit;
|
|
115
|
+
border-bottom-right-radius: inherit;
|
|
116
|
+
}
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
&__actions {
|
|
@@ -4,12 +4,13 @@ w-overlay.w-dialog(
|
|
|
4
4
|
:persistent="persistent"
|
|
5
5
|
:persistent-no-animation="persistentNoAnimation"
|
|
6
6
|
@click="onOutsideClick"
|
|
7
|
+
@closed="$emit('closed')"
|
|
7
8
|
:bg-color="overlayColor"
|
|
8
9
|
:opacity="overlayOpacity"
|
|
9
10
|
:class="classes")
|
|
10
11
|
transition(:name="transition" appear @after-leave="onClose")
|
|
11
12
|
w-card.w-dialog__content(
|
|
12
|
-
v-
|
|
13
|
+
v-show="showContent"
|
|
13
14
|
no-border
|
|
14
15
|
:color="color"
|
|
15
16
|
:bg-color="bgColor"
|
|
@@ -18,10 +19,10 @@ w-overlay.w-dialog(
|
|
|
18
19
|
:content-class="contentClass"
|
|
19
20
|
:title="title || undefined"
|
|
20
21
|
:style="contentStyles")
|
|
21
|
-
template(v-if="$slots.title"
|
|
22
|
+
template(#title v-if="$slots.title")
|
|
22
23
|
slot(name="title")
|
|
23
24
|
slot
|
|
24
|
-
template(v-if="$slots.actions"
|
|
25
|
+
template(#actions v-if="$slots.actions")
|
|
25
26
|
slot(name="actions")
|
|
26
27
|
</template>
|
|
27
28
|
|
|
@@ -47,7 +48,7 @@ export default {
|
|
|
47
48
|
overlayOpacity: { type: [Number, String, Boolean] }
|
|
48
49
|
},
|
|
49
50
|
|
|
50
|
-
emits: ['input', 'update:modelValue', 'close'],
|
|
51
|
+
emits: ['input', 'update:modelValue', 'close', 'closed'],
|
|
51
52
|
|
|
52
53
|
data () {
|
|
53
54
|
return {
|
|
@@ -87,7 +88,7 @@ export default {
|
|
|
87
88
|
this.showWrapper = false
|
|
88
89
|
this.$emit('update:modelValue', false)
|
|
89
90
|
this.$emit('input', false)
|
|
90
|
-
this.$emit('close'
|
|
91
|
+
this.$emit('close')
|
|
91
92
|
}
|
|
92
93
|
},
|
|
93
94
|
|
|
@@ -18,7 +18,7 @@ export default {
|
|
|
18
18
|
classes () {
|
|
19
19
|
return {
|
|
20
20
|
[`w-divider--has-color ${this.color}`]: this.color,
|
|
21
|
-
|
|
21
|
+
[`w-divider--${this.vertical ? 'vertical' : 'horizontal'}`]: true,
|
|
22
22
|
'w-divider--has-content': this.$slots.default
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -34,10 +34,14 @@ export default {
|
|
|
34
34
|
&--has-color {border-color: currentColor;}
|
|
35
35
|
|
|
36
36
|
&--vertical {
|
|
37
|
+
align-self: stretch; // Fill up the available height.
|
|
37
38
|
display: flex;
|
|
38
39
|
border-top-width: 0;
|
|
39
40
|
border-left-width: 1px;
|
|
40
|
-
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.w-toolbar--vertical > &--horizontal {
|
|
44
|
+
align-self: stretch; // Fill up the available width.
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
// With a slot.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
.w-menu(
|
|
6
6
|
v-if="custom && detachableVisible"
|
|
7
7
|
ref="detachable"
|
|
8
|
+
v-bind="$attrs"
|
|
8
9
|
@click="hideOnMenuClick && close(true)"
|
|
9
10
|
@mouseenter="showOnHover && (hoveringMenu = true)"
|
|
10
11
|
@mouseleave="showOnHover && ((hoveringMenu = false), close())"
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
w-card.w-menu(
|
|
15
16
|
v-else-if="detachableVisible"
|
|
16
17
|
ref="detachable"
|
|
18
|
+
v-bind="$attrs"
|
|
17
19
|
@click.native="hideOnMenuClick && close(true)"
|
|
18
20
|
@mouseenter.native="showOnHover && (hoveringMenu = true)"
|
|
19
21
|
@mouseleave.native="showOnHover && ((hoveringMenu = false), close())"
|
|
@@ -134,7 +136,10 @@ export default {
|
|
|
134
136
|
},
|
|
135
137
|
|
|
136
138
|
overlayClasses () {
|
|
137
|
-
return
|
|
139
|
+
return {
|
|
140
|
+
...objectifyClasses(this.overlayClass),
|
|
141
|
+
'w-overlay--no-pointer-event': this.showOnHover
|
|
142
|
+
}
|
|
138
143
|
},
|
|
139
144
|
|
|
140
145
|
classes () {
|
|
@@ -196,6 +201,7 @@ export default {
|
|
|
196
201
|
methods: {
|
|
197
202
|
/**
|
|
198
203
|
* Other methods in the `detachable` mixin:
|
|
204
|
+
* - `open`
|
|
199
205
|
* - `getActivatorCoordinates`
|
|
200
206
|
* - `computeDetachableCoords`
|
|
201
207
|
* - `onResize`
|
|
@@ -207,7 +213,8 @@ export default {
|
|
|
207
213
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
208
214
|
toggle (e) {
|
|
209
215
|
let shouldShowMenu = this.detachableVisible
|
|
210
|
-
if (
|
|
216
|
+
if (typeof window !== 'undefined' && 'ontouchstart' in window &&
|
|
217
|
+
this.showOnHover && e.type === 'click') {
|
|
211
218
|
shouldShowMenu = !shouldShowMenu
|
|
212
219
|
}
|
|
213
220
|
else if (e.type === 'click' && !this.showOnHover) shouldShowMenu = !shouldShowMenu
|
|
@@ -226,37 +233,6 @@ export default {
|
|
|
226
233
|
else this.close()
|
|
227
234
|
},
|
|
228
235
|
|
|
229
|
-
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
230
|
-
async open (e) {
|
|
231
|
-
// A tiny delay may help positioning the detachable correctly in case of multiple activators
|
|
232
|
-
// with different menu contents.
|
|
233
|
-
if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
|
|
234
|
-
|
|
235
|
-
this.detachableVisible = true
|
|
236
|
-
|
|
237
|
-
// If the activator is external, there might be multiple,
|
|
238
|
-
// so on open, the activator will be set to the event target.
|
|
239
|
-
if (this.activator) this.activatorEl = e.target
|
|
240
|
-
|
|
241
|
-
await this.insertInDOM()
|
|
242
|
-
|
|
243
|
-
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
244
|
-
|
|
245
|
-
if (!this.noPosition) this.computeDetachableCoords(e)
|
|
246
|
-
|
|
247
|
-
// In `getActivatorCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
|
|
248
|
-
// if we don't postpone the Menu apparition it will start transition from a visible menu and
|
|
249
|
-
// thus will not transition.
|
|
250
|
-
this.timeoutId = setTimeout(() => {
|
|
251
|
-
this.$emit('update:modelValue', true)
|
|
252
|
-
this.$emit('input', true)
|
|
253
|
-
this.$emit('open')
|
|
254
|
-
}, 0)
|
|
255
|
-
|
|
256
|
-
if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
|
|
257
|
-
if (!this.noPosition) window.addEventListener('resize', this.onResize)
|
|
258
|
-
},
|
|
259
|
-
|
|
260
236
|
/**
|
|
261
237
|
* Closes the menu. Can happen on:
|
|
262
238
|
* - click of activator
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
transition(name="fade"
|
|
2
|
+
transition(name="fade" appear @after-leave="onClosed")
|
|
3
3
|
.w-overlay(
|
|
4
|
-
v-if="
|
|
4
|
+
v-if="showOverlay"
|
|
5
|
+
v-show="modelValue"
|
|
5
6
|
:style="(modelValue && styles) || null"
|
|
6
7
|
@keydown.escape.stop="onClick"
|
|
7
8
|
@click="onClick"
|
|
@@ -24,10 +25,11 @@ export default {
|
|
|
24
25
|
persistentNoAnimation: { type: Boolean }
|
|
25
26
|
},
|
|
26
27
|
|
|
27
|
-
emits: ['input', 'update:modelValue', 'click', 'close'],
|
|
28
|
+
emits: ['input', 'update:modelValue', 'click', 'close', 'closed'],
|
|
28
29
|
|
|
29
30
|
data: () => ({
|
|
30
|
-
persistentAnimate: false
|
|
31
|
+
persistentAnimate: false,
|
|
32
|
+
showOverlay: false
|
|
31
33
|
}),
|
|
32
34
|
|
|
33
35
|
computed: {
|
|
@@ -60,10 +62,29 @@ export default {
|
|
|
60
62
|
else if (!this.persistent) {
|
|
61
63
|
this.$emit('update:modelValue', false)
|
|
62
64
|
this.$emit('input', false)
|
|
63
|
-
this.$emit('close'
|
|
65
|
+
this.$emit('close')
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
this.$emit('click', e)
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Wait until the end of the closing transition to unmount the components it contains.
|
|
72
|
+
// This way, in case of w-select in w-dialog, the w-select will only remove its activator
|
|
73
|
+
// element from the DOM once the dialog is completely closed.
|
|
74
|
+
// https://github.com/antoniandre/wave-ui/issues/82
|
|
75
|
+
onClosed () {
|
|
76
|
+
this.showOverlay = false
|
|
77
|
+
this.$emit('closed')
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
created () {
|
|
82
|
+
this.showOverlay = this.modelValue
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
watch: {
|
|
86
|
+
modelValue (bool) {
|
|
87
|
+
if (bool) this.showOverlay = true
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
}
|
|
@@ -85,6 +106,7 @@ export default {
|
|
|
85
106
|
background-color: rgba(0, 0, 0, 0.3);
|
|
86
107
|
|
|
87
108
|
&--persistent-animate {animation: 0.15s w-overlay-pop cubic-bezier(0.6, -0.28, 0.74, 0.05);}
|
|
109
|
+
&--no-pointer-event {pointer-events: none;}
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
@keyframes w-overlay-pop {
|
|
@@ -25,11 +25,12 @@ component(
|
|
|
25
25
|
align-left
|
|
26
26
|
custom
|
|
27
27
|
min-width="activator"
|
|
28
|
+
@mousedown="isFocused = true, selectingItem = true"
|
|
29
|
+
@mouseup="isFocused = true, selectingItem = false"
|
|
28
30
|
v-bind="menuProps || {}")
|
|
29
31
|
template(#activator="{ on }")
|
|
30
32
|
//- Input wrapper.
|
|
31
33
|
.w-select__selection-wrap(
|
|
32
|
-
ref="selection-wrap"
|
|
33
34
|
@click="!isDisabled && !isReadonly && (showMenu ? closeMenu : openMenu)()"
|
|
34
35
|
role="button"
|
|
35
36
|
aria-haspopup="listbox"
|
|
@@ -172,7 +173,9 @@ export default {
|
|
|
172
173
|
inputValue: [],
|
|
173
174
|
showMenu: false,
|
|
174
175
|
menuMinWidth: 0,
|
|
175
|
-
isFocused: false
|
|
176
|
+
isFocused: false,
|
|
177
|
+
selectingItem: false,
|
|
178
|
+
selectionWrapRef: undefined
|
|
176
179
|
}),
|
|
177
180
|
|
|
178
181
|
computed: {
|
|
@@ -209,7 +212,7 @@ export default {
|
|
|
209
212
|
'w-select--disabled': this.isDisabled,
|
|
210
213
|
'w-select--readonly': this.isReadonly,
|
|
211
214
|
[`w-select--${this.hasValue ? 'filled' : 'empty'}`]: true,
|
|
212
|
-
'w-select--focused': this.isFocused && !this.isReadonly,
|
|
215
|
+
'w-select--focused': (this.isFocused || this.selectingItem) && !this.isReadonly,
|
|
213
216
|
'w-select--dark': this.dark,
|
|
214
217
|
'w-select--floating-label': this.hasLabel && this.labelPosition === 'inside' && !this.staticLabel,
|
|
215
218
|
'w-select--no-padding': !this.outline && !this.bgColor && !this.shadow && !this.round,
|
|
@@ -122,6 +122,7 @@ export default {
|
|
|
122
122
|
return {
|
|
123
123
|
'w-tabs--card': this.card,
|
|
124
124
|
'w-tabs--no-slider': this.noSlider,
|
|
125
|
+
'w-tabs--pill-slider': this.pillSlider,
|
|
125
126
|
'w-tabs--fill-bar': this.fillBar,
|
|
126
127
|
'w-tabs--init': this.init
|
|
127
128
|
}
|
|
@@ -253,11 +254,11 @@ export default {
|
|
|
253
254
|
&__bar {
|
|
254
255
|
position: relative;
|
|
255
256
|
display: flex;
|
|
256
|
-
// align-items: center;
|
|
257
257
|
overflow-x: auto;
|
|
258
258
|
|
|
259
259
|
&--center {justify-content: center;}
|
|
260
260
|
&--right {justify-content: flex-end;}
|
|
261
|
+
.w-tabs--pill-slider & {padding-left: $base-increment;}
|
|
261
262
|
|
|
262
263
|
.w-tabs--card &:after {
|
|
263
264
|
content: '';
|
|
@@ -311,6 +312,7 @@ export default {
|
|
|
311
312
|
&:active:before {opacity: 0.08;}
|
|
312
313
|
&--disabled:before {display: none;}
|
|
313
314
|
}
|
|
315
|
+
&--pill-slider &__bar-item:before {display: none;}
|
|
314
316
|
|
|
315
317
|
// Bar Extra.
|
|
316
318
|
// ------------------------------------------------------
|
|
@@ -331,6 +333,13 @@ export default {
|
|
|
331
333
|
background-color: currentColor;
|
|
332
334
|
transition: $transition-duration ease-in-out;
|
|
333
335
|
}
|
|
336
|
+
&--pill-slider &__slider {
|
|
337
|
+
opacity: 0.1;
|
|
338
|
+
bottom: 15%;
|
|
339
|
+
height: 70%;
|
|
340
|
+
border-radius: 99em;
|
|
341
|
+
}
|
|
342
|
+
|
|
334
343
|
&--init &__slider {transition: none;}
|
|
335
344
|
|
|
336
345
|
// Content.
|
|
@@ -13,6 +13,10 @@ export default {
|
|
|
13
13
|
absolute: { type: Boolean },
|
|
14
14
|
fixed: { type: Boolean },
|
|
15
15
|
bottom: { type: Boolean },
|
|
16
|
+
vertical: { type: Boolean },
|
|
17
|
+
left: { type: Boolean },
|
|
18
|
+
right: { type: Boolean },
|
|
19
|
+
width: { type: [Number, String], default: null },
|
|
16
20
|
height: { type: [Number, String], default: null },
|
|
17
21
|
noBorder: { type: Boolean },
|
|
18
22
|
shadow: { type: Boolean }
|
|
@@ -21,25 +25,35 @@ export default {
|
|
|
21
25
|
emits: [],
|
|
22
26
|
|
|
23
27
|
computed: {
|
|
24
|
-
// Return the
|
|
28
|
+
// Return the height value if defined, or false otherwise.
|
|
25
29
|
toolbarHeight () {
|
|
26
30
|
const h = this.height
|
|
27
31
|
// If a number is passed without units, append `px`.
|
|
28
32
|
return h && parseInt(h) == h ? h + 'px' : h
|
|
29
33
|
},
|
|
34
|
+
// Return the width value if defined, or false otherwise.
|
|
35
|
+
toolbarWidth () {
|
|
36
|
+
const w = this.width
|
|
37
|
+
// If a number is passed without units, append `px`.
|
|
38
|
+
return w && parseInt(w) == w ? w + 'px' : w
|
|
39
|
+
},
|
|
30
40
|
classes () {
|
|
31
41
|
return {
|
|
32
42
|
[this.color]: !!this.color,
|
|
33
43
|
[`${this.bgColor}--bg`]: !!this.bgColor,
|
|
34
44
|
'w-toolbar--absolute': !!this.absolute,
|
|
35
45
|
'w-toolbar--fixed': !!this.fixed,
|
|
36
|
-
[`w-toolbar--${this.bottom ? 'bottom' : 'top'}`]:
|
|
46
|
+
[`w-toolbar--${this.bottom ? 'bottom' : 'top'}`]: !this.vertical,
|
|
47
|
+
[`w-toolbar--vertical w-toolbar--${this.right ? 'right' : 'left'}`]: this.vertical,
|
|
37
48
|
'w-toolbar--no-border': this.noBorder,
|
|
38
49
|
'w-toolbar--shadow': !!this.shadow
|
|
39
50
|
}
|
|
40
51
|
},
|
|
41
52
|
styles () {
|
|
42
|
-
return
|
|
53
|
+
return {
|
|
54
|
+
height: this.height && !this.vertical ? this.toolbarHeight : null,
|
|
55
|
+
width: this.width && this.vertical ? this.toolbarWidth : null
|
|
56
|
+
}
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
}
|
|
@@ -58,16 +72,34 @@ export default {
|
|
|
58
72
|
&--absolute, &--fixed {top: 0;left: 0;right: 0;}
|
|
59
73
|
&--absolute {position: absolute;}
|
|
60
74
|
&--fixed {position: fixed;}
|
|
75
|
+
&--absolute.w-toolbar--vertical, &--fixed.w-toolbar--vertical {top: 0;bottom: 0;}
|
|
76
|
+
&--absolute.w-toolbar--left, &--fixed.w-toolbar--left {left: 0;right: auto;}
|
|
77
|
+
&--absolute.w-toolbar--right, &--fixed.w-toolbar--right {left: auto;right: 0;}
|
|
78
|
+
|
|
79
|
+
// Horizontal.
|
|
61
80
|
&--top {border-bottom: $border;}
|
|
62
81
|
&--bottom {
|
|
63
82
|
bottom: 0;
|
|
64
83
|
top: auto;
|
|
65
84
|
border-top: $border;
|
|
66
85
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
|
|
87
|
+
// Vertical.
|
|
88
|
+
&--vertical {
|
|
89
|
+
padding: (2 * $base-increment);
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
flex-grow: 0;
|
|
92
|
+
flex-shrink: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&--left {border-right: $border;}
|
|
96
|
+
&--right {
|
|
97
|
+
right: 0;
|
|
98
|
+
left: auto;
|
|
99
|
+
border-left: $border;
|
|
70
100
|
}
|
|
101
|
+
|
|
102
|
+
&--no-border, &--shadow {border-width: 0;}
|
|
71
103
|
&--shadow {box-shadow: $box-shadow;}
|
|
72
104
|
|
|
73
105
|
.w-app > & {z-index: 200;}
|
|
@@ -81,5 +113,13 @@ export default {
|
|
|
81
113
|
border-bottom-left-radius: inherit;
|
|
82
114
|
border-bottom-right-radius: inherit;
|
|
83
115
|
}
|
|
116
|
+
.w-card__content &--left {
|
|
117
|
+
border-top-left-radius: inherit;
|
|
118
|
+
border-bottom-left-radius: inherit;
|
|
119
|
+
}
|
|
120
|
+
.w-card__content &--right {
|
|
121
|
+
border-top-right-radius: inherit;
|
|
122
|
+
border-bottom-right-radius: inherit;
|
|
123
|
+
}
|
|
84
124
|
}
|
|
85
125
|
</style>
|
|
@@ -134,6 +134,7 @@ export default {
|
|
|
134
134
|
methods: {
|
|
135
135
|
/**
|
|
136
136
|
* Other methods in the `detachable` mixin:
|
|
137
|
+
* - `open`
|
|
137
138
|
* - `getActivatorCoordinates`
|
|
138
139
|
* - `computeDetachableCoords`
|
|
139
140
|
* - `onResize`
|
|
@@ -157,37 +158,6 @@ export default {
|
|
|
157
158
|
else this.close()
|
|
158
159
|
},
|
|
159
160
|
|
|
160
|
-
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
161
|
-
async open (e) {
|
|
162
|
-
// A tiny delay may help positioning the detachable correctly in case of multiple activators
|
|
163
|
-
// with different menu contents.
|
|
164
|
-
if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
|
|
165
|
-
|
|
166
|
-
this.detachableVisible = true
|
|
167
|
-
|
|
168
|
-
// If the activator is external, there might be multiple,
|
|
169
|
-
// so on open, the activator will be set to the event target.
|
|
170
|
-
if (this.activator) this.activatorEl = e.target
|
|
171
|
-
|
|
172
|
-
await this.insertInDOM()
|
|
173
|
-
|
|
174
|
-
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
175
|
-
|
|
176
|
-
if (!this.noPosition) this.computeDetachableCoords(e)
|
|
177
|
-
|
|
178
|
-
// In `getActivatorCoordinates` accessing the tooltip computed styles takes a few ms (less than 10ms),
|
|
179
|
-
// if we don't postpone the Tooltip apparition it will start transition from a visible tooltip and
|
|
180
|
-
// thus will not transition.
|
|
181
|
-
this.timeoutId = setTimeout(() => {
|
|
182
|
-
this.$emit('update:modelValue', true)
|
|
183
|
-
this.$emit('input', true)
|
|
184
|
-
this.$emit('open')
|
|
185
|
-
}, 0)
|
|
186
|
-
|
|
187
|
-
if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
|
|
188
|
-
if (!this.noPosition) window.addEventListener('resize', this.onResize)
|
|
189
|
-
},
|
|
190
|
-
|
|
191
161
|
/**
|
|
192
162
|
* Closes the tooltip. Can happen on:
|
|
193
163
|
* - click of activator
|
|
@@ -22,7 +22,8 @@ export default {
|
|
|
22
22
|
alignRight: { type: Boolean },
|
|
23
23
|
noPosition: { type: Boolean },
|
|
24
24
|
zIndex: { type: [Number, String, Boolean] },
|
|
25
|
-
|
|
25
|
+
// Optionally designate an external activator.
|
|
26
|
+
activator: { type: [String, Object, HTMLElement] } // The activator can be a DOM string selector, a ref or a DOM node.
|
|
26
27
|
},
|
|
27
28
|
|
|
28
29
|
data: () => ({
|
|
@@ -31,7 +32,7 @@ export default {
|
|
|
31
32
|
// as is in an array so we can delete them on destroy.
|
|
32
33
|
// This only applies to the activatorEventHandlers, the other events listeners can be removed
|
|
33
34
|
// normally.
|
|
34
|
-
|
|
35
|
+
docEventListenersHandlers: []
|
|
35
36
|
}),
|
|
36
37
|
|
|
37
38
|
computed: {
|
|
@@ -70,17 +71,22 @@ export default {
|
|
|
70
71
|
},
|
|
71
72
|
|
|
72
73
|
hasSeparateActivator () {
|
|
73
|
-
|
|
74
|
+
if (this.$slots.activator) return false
|
|
75
|
+
const activatorIsString = typeof this.activator === 'string'
|
|
76
|
+
const activatorIsDomEl = (this.activator?.$el || this.activator) instanceof HTMLElement
|
|
77
|
+
return activatorIsString || activatorIsDomEl
|
|
74
78
|
},
|
|
75
79
|
|
|
76
80
|
activatorEl: {
|
|
77
81
|
get () {
|
|
78
|
-
if (this.hasSeparateActivator)
|
|
82
|
+
if (this.hasSeparateActivator) {
|
|
83
|
+
const activator = this.activator?.$el || this.activator
|
|
84
|
+
if (activator instanceof HTMLElement) return activator
|
|
85
|
+
return document.querySelector(this.activator)
|
|
86
|
+
}
|
|
79
87
|
return this.$el.firstElementChild
|
|
80
88
|
},
|
|
81
|
-
set () {
|
|
82
|
-
|
|
83
|
-
}
|
|
89
|
+
set () {}
|
|
84
90
|
},
|
|
85
91
|
|
|
86
92
|
position () {
|
|
@@ -106,9 +112,40 @@ export default {
|
|
|
106
112
|
|
|
107
113
|
methods: {
|
|
108
114
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
109
|
-
|
|
115
|
+
async open (e) {
|
|
116
|
+
// A tiny delay may help positioning the detachable correctly in case of multiple activators
|
|
117
|
+
// with different menu contents.
|
|
118
|
+
if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
|
|
119
|
+
|
|
120
|
+
this.detachableVisible = true
|
|
121
|
+
|
|
122
|
+
// If the activator is external, there might be multiple,
|
|
123
|
+
// so on open, the activator will be set to the event target.
|
|
124
|
+
if (this.activator) this.activatorEl = e.target
|
|
125
|
+
|
|
126
|
+
await this.insertInDOM()
|
|
127
|
+
|
|
128
|
+
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
129
|
+
|
|
130
|
+
if (!this.noPosition) this.computeDetachableCoords()
|
|
131
|
+
|
|
132
|
+
// In `getActivatorCoordinates` accessing the menu computed styles takes a few ms (less than 10ms),
|
|
133
|
+
// if we don't postpone the Menu apparition it will start transition from a visible menu and
|
|
134
|
+
// thus will not transition.
|
|
135
|
+
this.timeoutId = setTimeout(() => {
|
|
136
|
+
this.$emit('update:modelValue', true)
|
|
137
|
+
this.$emit('input', true)
|
|
138
|
+
this.$emit('open')
|
|
139
|
+
}, 0)
|
|
140
|
+
|
|
141
|
+
if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
|
|
142
|
+
if (!this.noPosition) window.addEventListener('resize', this.onResize)
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
146
|
+
getActivatorCoordinates () {
|
|
110
147
|
// Get the activator coordinates relative to window.
|
|
111
|
-
const { top, left, width, height } = (
|
|
148
|
+
const { top, left, width, height } = (this.activatorEl).getBoundingClientRect()
|
|
112
149
|
let coords = { top, left, width, height }
|
|
113
150
|
|
|
114
151
|
// If absolute position, adjust top & left.
|
|
@@ -126,9 +163,9 @@ export default {
|
|
|
126
163
|
},
|
|
127
164
|
|
|
128
165
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
129
|
-
computeDetachableCoords (
|
|
166
|
+
computeDetachableCoords () {
|
|
130
167
|
// Get the activator coordinates.
|
|
131
|
-
let { top, left, width, height } = this.getActivatorCoordinates(
|
|
168
|
+
let { top, left, width, height } = this.getActivatorCoordinates()
|
|
132
169
|
|
|
133
170
|
// 1. First display the menu but hide it (So we can get its dimension).
|
|
134
171
|
// --------------------------------------------------
|
|
@@ -251,31 +288,47 @@ export default {
|
|
|
251
288
|
this.detachableEl.remove()
|
|
252
289
|
this.detachableEl = null
|
|
253
290
|
}
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
mounted () {
|
|
258
|
-
const wrapper = this.$el
|
|
259
|
-
|
|
260
|
-
// Unwrap the activator element if the activator is in the activator slot.
|
|
261
|
-
if (this.$slots.activator) wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
|
|
291
|
+
},
|
|
262
292
|
|
|
263
293
|
// If the activator is external, add event listeners to the document and check the target is
|
|
264
294
|
// the activator when toggling.
|
|
265
295
|
// This way, the activator can be a future DOM element, that is not yet in the DOM.
|
|
266
|
-
|
|
296
|
+
bindActivatorEvents () {
|
|
297
|
+
const activatorIsString = typeof this.activator === 'string'
|
|
298
|
+
|
|
267
299
|
Object.entries(this.activatorEventHandlers).forEach(([eventName, handler]) => {
|
|
268
300
|
// Convert mouseenter to mouseover & mouseleave to mouseout because we are attaching
|
|
269
|
-
// event to the document, so it can accept future nodes.
|
|
301
|
+
// event to the document, so it can accept future DOM nodes.
|
|
270
302
|
eventName = eventName.replace('mouseenter', 'mouseover').replace('mouseleave', 'mouseout')
|
|
271
303
|
const handlerWrap = e => {
|
|
272
|
-
|
|
304
|
+
// The activator can be a DOM string selector a ref or a DOM node.
|
|
305
|
+
if (activatorIsString && e.target?.matches && e.target.matches(this.activator)) handler(e)
|
|
306
|
+
else if (e.target === this.activatorEl || this.activatorEl.contains(e.target)) handler(e)
|
|
273
307
|
}
|
|
274
308
|
document.addEventListener(eventName, handlerWrap)
|
|
275
309
|
// The event listeners handlers have to be removed the exact same way they have been attached.
|
|
276
310
|
// Since the handler functions have variables that change after hot-reload, keep them exactly
|
|
277
311
|
// as is in an array so we can delete them on destroy.
|
|
278
|
-
this.
|
|
312
|
+
this.docEventListenersHandlers.push({ eventName, handler: handlerWrap })
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
mounted () {
|
|
318
|
+
const wrapper = this.$el
|
|
319
|
+
|
|
320
|
+
// Unwrap the activator element if the activator is in the activator slot.
|
|
321
|
+
if (this.$slots.activator) wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
|
|
322
|
+
|
|
323
|
+
// If the activator is external.
|
|
324
|
+
else if (this.activator) this.bindActivatorEvents()
|
|
325
|
+
|
|
326
|
+
// If the activator seems to be undefined, it is probably a DOM node or Vue ref,
|
|
327
|
+
// so check it on nextTick.
|
|
328
|
+
else {
|
|
329
|
+
this.$nextTick(() => {
|
|
330
|
+
this.activator && this.bindActivatorEvents()
|
|
331
|
+
if (this.modelValue) this.toggle({ type: 'click', target: this.activatorEl })
|
|
279
332
|
})
|
|
280
333
|
}
|
|
281
334
|
|
|
@@ -285,7 +338,7 @@ export default {
|
|
|
285
338
|
wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
|
|
286
339
|
}
|
|
287
340
|
|
|
288
|
-
if (this.modelValue) this.
|
|
341
|
+
if (this.modelValue && this.activator) this.toggle({ type: 'click', target: this.activatorEl })
|
|
289
342
|
},
|
|
290
343
|
|
|
291
344
|
beforeUnmount () {
|
|
@@ -295,8 +348,8 @@ export default {
|
|
|
295
348
|
|
|
296
349
|
// Remove the event listeners the exact same way they have been defined.
|
|
297
350
|
// Fixes issues on hot-reloading.
|
|
298
|
-
if (this.
|
|
299
|
-
this.
|
|
351
|
+
if (this.docEventListenersHandlers.length) {
|
|
352
|
+
this.docEventListenersHandlers.forEach(({ eventName, handler }) => {
|
|
300
353
|
document.removeEventListener(eventName, handler)
|
|
301
354
|
})
|
|
302
355
|
}
|