wave-ui 1.45.15 → 1.49.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 +415 -382
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +4 -2
- package/src/wave-ui/components/w-alert.vue +0 -1
- package/src/wave-ui/components/w-confirm.vue +35 -10
- package/src/wave-ui/components/w-input.vue +117 -34
- package/src/wave-ui/components/w-menu.vue +69 -275
- package/src/wave-ui/components/w-notification-manager.vue +9 -2
- package/src/wave-ui/components/w-select.vue +1 -1
- package/src/wave-ui/components/w-tag.vue +17 -6
- package/src/wave-ui/components/w-tooltip.vue +106 -220
- package/src/wave-ui/mixins/detachable.js +321 -0
- package/src/wave-ui/scss/_mixins.scss +18 -4
- package/src/wave-ui/scss/_variables.scss +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-tooltip-wrap
|
|
3
|
-
slot(name="activator" :on="
|
|
2
|
+
.w-tooltip-wrap
|
|
3
|
+
slot(name="activator" :on="activatorEventHandlers")
|
|
4
4
|
transition(:name="transitionName" appear)
|
|
5
|
-
.w-tooltip(
|
|
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
|
|
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: {},
|
|
@@ -35,33 +36,37 @@ export default {
|
|
|
35
36
|
round: { type: Boolean },
|
|
36
37
|
transition: { type: String },
|
|
37
38
|
tooltipClass: { type: [String, Object, Array] },
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
top
|
|
42
|
-
|
|
43
|
-
left: { type: Boolean },
|
|
44
|
-
right: { type: Boolean },
|
|
45
|
-
zIndex: { type: [Number, String, Boolean] }
|
|
39
|
+
persistent: { type: Boolean },
|
|
40
|
+
delay: { type: Number }
|
|
41
|
+
// Other props in the detachable mixin:
|
|
42
|
+
// detachTo, appendTo, fixed, top, bottom, left, right, alignTop, alignBottom, alignLeft,
|
|
43
|
+
// alignRight, noPosition, zIndex, activator.
|
|
46
44
|
},
|
|
47
45
|
|
|
48
46
|
emits: ['input', 'update:modelValue', 'open', 'close'],
|
|
49
47
|
|
|
50
48
|
data: () => ({
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
coordinates
|
|
49
|
+
detachableVisible: false,
|
|
50
|
+
hoveringActivator: false,
|
|
51
|
+
// The tooltip computed top & left coordinates.
|
|
52
|
+
detachableCoords: {
|
|
54
53
|
top: 0,
|
|
55
|
-
left: 0
|
|
56
|
-
width: 0,
|
|
57
|
-
height: 0
|
|
54
|
+
left: 0
|
|
58
55
|
},
|
|
59
|
-
|
|
60
|
-
tooltipEl: null,
|
|
56
|
+
detachableEl: null,
|
|
61
57
|
timeoutId: null
|
|
62
58
|
}),
|
|
63
59
|
|
|
64
60
|
computed: {
|
|
61
|
+
/**
|
|
62
|
+
* Other computed in the detachable mixin:
|
|
63
|
+
* - `appendToTarget`
|
|
64
|
+
* - `detachableParentEl`
|
|
65
|
+
* - `activatorEl`
|
|
66
|
+
* - `position`
|
|
67
|
+
* - `alignment`
|
|
68
|
+
**/
|
|
69
|
+
|
|
65
70
|
tooltipClasses () {
|
|
66
71
|
return objectifyClasses(this.tooltipClass)
|
|
67
72
|
},
|
|
@@ -71,85 +76,17 @@ export default {
|
|
|
71
76
|
return this.transition || `w-tooltip-slide-fade-${direction}`
|
|
72
77
|
},
|
|
73
78
|
|
|
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
|
-
position () {
|
|
103
|
-
return (
|
|
104
|
-
(this.top && 'top') ||
|
|
105
|
-
(this.bottom && 'bottom') ||
|
|
106
|
-
(this.left && 'left') ||
|
|
107
|
-
(this.right && 'right') ||
|
|
108
|
-
'bottom'
|
|
109
|
-
)
|
|
110
|
-
},
|
|
111
|
-
|
|
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
|
|
140
|
-
},
|
|
141
|
-
|
|
142
79
|
classes () {
|
|
143
80
|
return {
|
|
144
81
|
[this.color]: this.color,
|
|
145
82
|
[`${this.bgColor}--bg`]: this.bgColor,
|
|
146
83
|
...this.tooltipClasses,
|
|
147
|
-
[`w-tooltip--${this.position}`]:
|
|
84
|
+
[`w-tooltip--${this.position}`]: !this.noPosition,
|
|
85
|
+
[`w-tooltip--align-${this.alignment}`]: !this.noPosition && this.alignment,
|
|
148
86
|
'w-tooltip--tile': this.tile,
|
|
149
87
|
'w-tooltip--round': this.round,
|
|
150
88
|
'w-tooltip--shadow': this.shadow,
|
|
151
89
|
'w-tooltip--fixed': this.fixed,
|
|
152
|
-
'w-tooltip--active': this.showTooltip,
|
|
153
90
|
'w-tooltip--no-border': this.noBorder || this.bgColor,
|
|
154
91
|
'w-tooltip--custom-transition': this.transition
|
|
155
92
|
}
|
|
@@ -159,21 +96,27 @@ export default {
|
|
|
159
96
|
styles () {
|
|
160
97
|
return {
|
|
161
98
|
zIndex: this.zIndex || this.zIndex === 0 || null,
|
|
162
|
-
top: (this.
|
|
163
|
-
left: (this.
|
|
99
|
+
top: (this.detachableCoords.top && `${~~this.detachableCoords.top}px`) || null,
|
|
100
|
+
left: (this.detachableCoords.left && `${~~this.detachableCoords.left}px`) || null,
|
|
164
101
|
'--w-tooltip-bg-color': this.$waveui.colors[this.bgColor || 'white']
|
|
165
102
|
}
|
|
166
103
|
},
|
|
167
104
|
|
|
168
|
-
|
|
105
|
+
activatorEventHandlers () {
|
|
169
106
|
let handlers = {}
|
|
170
107
|
if (this.showOnClick) handlers = { click: this.toggle }
|
|
171
108
|
else {
|
|
172
109
|
handlers = {
|
|
173
110
|
focus: this.toggle,
|
|
174
111
|
blur: this.toggle,
|
|
175
|
-
mouseenter:
|
|
176
|
-
|
|
112
|
+
mouseenter: e => {
|
|
113
|
+
this.hoveringActivator = true
|
|
114
|
+
this.open(e)
|
|
115
|
+
},
|
|
116
|
+
mouseleave: e => {
|
|
117
|
+
this.hoveringActivator = false
|
|
118
|
+
this.close()
|
|
119
|
+
}
|
|
177
120
|
}
|
|
178
121
|
|
|
179
122
|
// Check the window exists: SSR-proof.
|
|
@@ -184,9 +127,19 @@ export default {
|
|
|
184
127
|
},
|
|
185
128
|
|
|
186
129
|
methods: {
|
|
130
|
+
/**
|
|
131
|
+
* Other methods in the `detachable` mixin:
|
|
132
|
+
* - `getActivatorCoordinates`
|
|
133
|
+
* - `computeDetachableCoords`
|
|
134
|
+
* - `onResize`
|
|
135
|
+
* - `onOutsideMousedown`
|
|
136
|
+
* - `insertInDOM`
|
|
137
|
+
* - `removeFromDOM`
|
|
138
|
+
**/
|
|
139
|
+
|
|
187
140
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
188
141
|
toggle (e) {
|
|
189
|
-
let shouldShowTooltip = this.
|
|
142
|
+
let shouldShowTooltip = this.detachableVisible
|
|
190
143
|
if (typeof window !== 'undefined' && 'ontouchstart' in window) {
|
|
191
144
|
if (e.type === 'click') shouldShowTooltip = !shouldShowTooltip
|
|
192
145
|
}
|
|
@@ -195,132 +148,77 @@ export default {
|
|
|
195
148
|
else if (['mouseleave', 'blur'].includes(e.type) && !this.showOnClick) shouldShowTooltip = false
|
|
196
149
|
|
|
197
150
|
this.timeoutId = clearTimeout(this.timeoutId)
|
|
198
|
-
if (shouldShowTooltip)
|
|
199
|
-
|
|
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')
|
|
215
|
-
}
|
|
151
|
+
if (shouldShowTooltip) this.open(e)
|
|
152
|
+
else this.close()
|
|
216
153
|
},
|
|
217
154
|
|
|
218
155
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
156
|
+
async open (e) {
|
|
157
|
+
// A tiny delay may help positioning the detachable correctly in case of multiple activators
|
|
158
|
+
// with different menu contents.
|
|
159
|
+
if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
|
|
222
160
|
|
|
223
|
-
|
|
224
|
-
const { top: targetTop, left: targetLeft } = this.tooltipParentEl.getBoundingClientRect()
|
|
225
|
-
coords = { ...coords, top: top - targetTop, left: left - targetLeft }
|
|
226
|
-
}
|
|
161
|
+
this.detachableVisible = true
|
|
227
162
|
|
|
228
|
-
|
|
163
|
+
// If the activator is external, there might be multiple,
|
|
164
|
+
// so on open, the activator will be set to the event target.
|
|
165
|
+
if (this.activator) this.activatorEl = e.target
|
|
229
166
|
|
|
230
|
-
|
|
231
|
-
tooltipEl.style.visibility = 'hidden'
|
|
232
|
-
tooltipEl.style.display = 'table'
|
|
233
|
-
const computedStyles = window.getComputedStyle(tooltipEl, null)
|
|
167
|
+
await this.insertInDOM()
|
|
234
168
|
|
|
235
|
-
|
|
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
|
|
273
|
-
},
|
|
169
|
+
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
274
170
|
|
|
275
|
-
|
|
276
|
-
const wrapper = this.$el
|
|
277
|
-
this.tooltipEl = this.$refs.tooltip.$el || this.$refs.tooltip
|
|
171
|
+
if (!this.noPosition) this.computeDetachableCoords(e)
|
|
278
172
|
|
|
279
|
-
//
|
|
280
|
-
|
|
173
|
+
// In `getActivatorCoordinates` accessing the tooltip computed styles takes a few ms (less than 10ms),
|
|
174
|
+
// if we don't postpone the Tooltip apparition it will start transition from a visible tooltip and
|
|
175
|
+
// thus will not transition.
|
|
176
|
+
this.timeoutId = setTimeout(() => {
|
|
177
|
+
this.$emit('update:modelValue', true)
|
|
178
|
+
this.$emit('input', true)
|
|
179
|
+
this.$emit('open')
|
|
180
|
+
}, 0)
|
|
281
181
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
this.detachToTarget.appendChild(this.$refs.tooltip)
|
|
182
|
+
if (!this.persistent) document.addEventListener('mousedown', this.onOutsideMousedown)
|
|
183
|
+
if (!this.noPosition) window.addEventListener('resize', this.onResize)
|
|
285
184
|
},
|
|
286
185
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Closes the tooltip. Can happen on:
|
|
188
|
+
* - click of activator
|
|
189
|
+
* - hover outside if showOnHover
|
|
190
|
+
* - click inside tooltip if hideOnTooltipClick.
|
|
191
|
+
* / ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
192
|
+
*
|
|
193
|
+
* @param {Boolean} force when showOnHover is set to true, hovering tooltip should keep it open.
|
|
194
|
+
* But if hideOnTooltipClick is also set to true, this should force close
|
|
195
|
+
* even while hovering the tooltip.
|
|
196
|
+
*/
|
|
197
|
+
async close (force = false) {
|
|
198
|
+
// Might be already closed.
|
|
199
|
+
// E.g. showOnHover & hideOnTooltipClick: on click, force hide then mouseleave is also firing.
|
|
200
|
+
if (!this.detachableVisible) return
|
|
201
|
+
|
|
202
|
+
if (this.showOnHover && !force) {
|
|
203
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
204
|
+
if (this.showOnHover && this.hoveringActivator) return
|
|
205
|
+
}
|
|
305
206
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
this.
|
|
312
|
-
this.insertTooltip()
|
|
207
|
+
this.$emit('update:modelValue', (this.detachableVisible = false))
|
|
208
|
+
this.$emit('input', false)
|
|
209
|
+
this.$emit('close')
|
|
210
|
+
// Remove the mousedown listener if the tooltip got closed without a mousedown outside of the tooltip.
|
|
211
|
+
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
212
|
+
window.removeEventListener('resize', this.onResize)
|
|
313
213
|
}
|
|
314
214
|
}
|
|
215
|
+
|
|
216
|
+
// watch, mounted & beforeDestroy hooks are set in the detachable.js mixin.
|
|
315
217
|
}
|
|
316
218
|
</script>
|
|
317
219
|
|
|
318
220
|
<style lang="scss">
|
|
319
|
-
.w-tooltip-wrap {
|
|
320
|
-
display: none;
|
|
321
|
-
|
|
322
|
-
&--attached {display: inline-block;position: relative;}
|
|
323
|
-
}
|
|
221
|
+
.w-tooltip-wrap {display: none;}
|
|
324
222
|
|
|
325
223
|
.w-tooltip {
|
|
326
224
|
// Fix Safari where `width: max-content` does not take padding and border into consideration.
|
|
@@ -348,22 +246,10 @@ export default {
|
|
|
348
246
|
&--shadow {box-shadow: $box-shadow;}
|
|
349
247
|
&--no-border {border: none;}
|
|
350
248
|
|
|
351
|
-
&--top {
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
}
|
|
249
|
+
&--top {margin-top: -3 * $base-increment;}
|
|
250
|
+
&--bottom {margin-top: 3 * $base-increment;}
|
|
251
|
+
&--left {margin-left: -3 * $base-increment;}
|
|
252
|
+
&--right {margin-left: 3 * $base-increment;}
|
|
367
253
|
|
|
368
254
|
&--custom-transition {transform: none;}
|
|
369
255
|
|