wave-ui 4.0.0 → 4.0.2
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/.nojekyll +0 -0
- package/dist/types/types/components/WAccordion.d.ts +6 -6
- package/dist/types/types/components/WMenu.d.ts +2 -1
- package/dist/types/types/components/WTabs.d.ts +6 -5
- package/dist/types/types/components/WTooltip.d.ts +2 -1
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/mixins/detachable.d.ts +7 -0
- package/dist/types/types/mixins/detachable.js +2 -0
- package/dist/wave-ui.cjs.js +3 -3
- package/dist/wave-ui.esm.js +59 -25
- package/dist/wave-ui.umd.js +3 -3
- package/package.json +1 -1
- package/src/wave-ui/components/w-accordion/index.vue +15 -2
- package/src/wave-ui/components/w-menu.vue +1 -1
- package/src/wave-ui/components/w-tabs/index.vue +61 -18
- package/src/wave-ui/components/w-tooltip.vue +1 -1
- package/src/wave-ui/mixins/detachable.js +29 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "A UI framework for Vue.js 3 (and 2) with only the bright side. :sunny:",
|
|
5
5
|
"author": "Antoni Andre <antoniandre.web@gmail.com>",
|
|
6
6
|
"homepage": "https://antoniandre.github.io/wave-ui",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
</template>
|
|
46
46
|
|
|
47
47
|
<script>
|
|
48
|
-
import { computed } from 'vue'
|
|
48
|
+
import { computed, Fragment } from 'vue'
|
|
49
49
|
import { objectifyClasses } from '../../utils/index'
|
|
50
50
|
import { consoleError } from '../../utils/console'
|
|
51
51
|
import RippleMixin from '../../mixins/ripple'
|
|
@@ -119,7 +119,8 @@ export default {
|
|
|
119
119
|
|
|
120
120
|
// Detect if the accordion items are directly provided through slot using WAccordionItem.
|
|
121
121
|
accordionItemsProvided () {
|
|
122
|
-
|
|
122
|
+
const slot = this.$slots.default?.() || []
|
|
123
|
+
return this.hasAccordionItemVNodes(slot)
|
|
123
124
|
},
|
|
124
125
|
|
|
125
126
|
accordionClasses () {
|
|
@@ -151,6 +152,18 @@ export default {
|
|
|
151
152
|
},
|
|
152
153
|
|
|
153
154
|
methods: {
|
|
155
|
+
hasAccordionItemVNodes (nodes = []) {
|
|
156
|
+
return nodes.some((node) => {
|
|
157
|
+
if (!node) return false
|
|
158
|
+
if (node.type === WAccordionItem) return true
|
|
159
|
+
if (node.type?.name === 'w-accordion-item') return true
|
|
160
|
+
if (node.type === Fragment && Array.isArray(node.children)) {
|
|
161
|
+
return this.hasAccordionItemVNodes(node.children)
|
|
162
|
+
}
|
|
163
|
+
return false
|
|
164
|
+
})
|
|
165
|
+
},
|
|
166
|
+
|
|
154
167
|
getAccordionItem (cuid) {
|
|
155
168
|
return this.accordionItemsById[cuid]
|
|
156
169
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
slot(name="activator")
|
|
3
3
|
slot(v-if="!$slots.activator")
|
|
4
|
-
teleport(:to="teleportTarget" :disabled="!teleportTarget")
|
|
4
|
+
teleport(v-if="detachableDomReady" :to="teleportTarget" :disabled="!teleportTarget")
|
|
5
5
|
transition(:name="transitionName" appear @after-leave="onAfterLeave")
|
|
6
6
|
.w-menu(
|
|
7
7
|
v-if="custom && detachableVisible"
|
|
@@ -183,6 +183,38 @@ export default {
|
|
|
183
183
|
},
|
|
184
184
|
|
|
185
185
|
methods: {
|
|
186
|
+
resolveTabUid (value) {
|
|
187
|
+
if (!this.tabs.length) return null
|
|
188
|
+
if (value === undefined || value === null || value === '') return this.tabs[0]._uid
|
|
189
|
+
|
|
190
|
+
if (typeof value === 'string') {
|
|
191
|
+
if (this.tabsByUid[value]?._uid) return value
|
|
192
|
+
const parsed = Number.parseInt(value, 10)
|
|
193
|
+
if (!Number.isNaN(parsed) && `${parsed}` === value.trim()) return this.tabs[parsed]?._uid || null
|
|
194
|
+
return null
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (typeof value === 'number' && value >= 0) return this.tabs[value]?._uid || null
|
|
198
|
+
return null
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
syncActiveTabFromModelValue (value = this.modelValue) {
|
|
202
|
+
const uid = this.resolveTabUid(value) || this.tabs[0]?._uid || null
|
|
203
|
+
const tab = uid ? this.tabsByUid[uid] : null
|
|
204
|
+
this.activeTabUid = tab?._uid || null
|
|
205
|
+
this.activeTabIndex = tab?._index || 0
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
shouldEmitUidModelValue () {
|
|
209
|
+
if (typeof this.modelValue !== 'string') return false
|
|
210
|
+
return !/^\d+$/.test(this.modelValue.trim())
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
getModelValueForTab (tab) {
|
|
214
|
+
if (this.shouldEmitUidModelValue()) return tab[this.itemIdKey] ?? tab._uid
|
|
215
|
+
return tab._index
|
|
216
|
+
},
|
|
217
|
+
|
|
186
218
|
// Adding a tab in the list.
|
|
187
219
|
addTab (item) {
|
|
188
220
|
// If there is no unique ID provided, inject one in each tab.
|
|
@@ -200,6 +232,7 @@ export default {
|
|
|
200
232
|
refreshTabs () {
|
|
201
233
|
let items = this.items
|
|
202
234
|
if (typeof items === 'number') items = Array(items).fill().map((_, i) => this.tabs[i] || {})
|
|
235
|
+
else items = items || []
|
|
203
236
|
|
|
204
237
|
this.tabs = items.map((item, _index) => {
|
|
205
238
|
// If there is no unique ID provided, inject one in each tab.
|
|
@@ -258,19 +291,23 @@ export default {
|
|
|
258
291
|
openTab (uid) {
|
|
259
292
|
this.prevTabIndex = this.activeTabIndex // To resolve the transition direction.
|
|
260
293
|
const tab = this.tabsByUid[uid]
|
|
294
|
+
if (!tab) return
|
|
261
295
|
this.activeTabIndex = tab._index
|
|
262
296
|
this.activeTabUid = tab._uid
|
|
263
|
-
this
|
|
264
|
-
this.$emit('
|
|
297
|
+
const modelValue = this.getModelValueForTab(tab)
|
|
298
|
+
this.$emit('update:modelValue', modelValue)
|
|
299
|
+
this.$emit('input', modelValue)
|
|
265
300
|
|
|
266
301
|
if (!this.noSlider) this.$nextTick(this.updateSlider)
|
|
267
302
|
},
|
|
268
303
|
|
|
269
304
|
// Updates the slider position.
|
|
270
305
|
updateSlider (domLookup = true) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
this.activeTabEl =
|
|
306
|
+
const ref = this.$refs['tabs-bar']
|
|
307
|
+
if (domLookup || !this.activeTabEl) {
|
|
308
|
+
this.activeTabEl =
|
|
309
|
+
ref?.querySelector('.w-tabs__bar-item--active')
|
|
310
|
+
|| ref?.querySelector(`.w-tabs__bar-item:nth-child(${this.activeTabIndex + 1})`)
|
|
274
311
|
}
|
|
275
312
|
|
|
276
313
|
if (!this.fillBar && this.activeTabEl) {
|
|
@@ -281,6 +318,10 @@ export default {
|
|
|
281
318
|
this.slider.left = `${left - parentLeft - parseInt(borderLeftWidth) + tabsBar.scrollLeft}px`
|
|
282
319
|
this.slider.width = `${width}px`
|
|
283
320
|
}
|
|
321
|
+
else if (!this.fillBar && domLookup && this.tabs.length) {
|
|
322
|
+
// Hydration/layout timing can briefly hide active title lookup; retry once on next tick.
|
|
323
|
+
this.$nextTick(() => this.updateSlider(false))
|
|
324
|
+
}
|
|
284
325
|
else {
|
|
285
326
|
this.slider.left = `${this.activeTab._index * 100 / this.tabs.length}%`
|
|
286
327
|
this.slider.width = `${100 / this.tabs.length}%`
|
|
@@ -288,17 +329,17 @@ export default {
|
|
|
288
329
|
},
|
|
289
330
|
|
|
290
331
|
updateActiveTab (index) {
|
|
291
|
-
|
|
292
|
-
|
|
332
|
+
const uid = this.resolveTabUid(index)
|
|
333
|
+
const tab = uid ? this.tabsByUid[uid] : null
|
|
293
334
|
|
|
294
335
|
// Only open the tab if it is found.
|
|
295
|
-
if (
|
|
296
|
-
this.openTab(
|
|
336
|
+
if (tab?._uid) {
|
|
337
|
+
this.openTab(tab._uid)
|
|
297
338
|
|
|
298
339
|
// Scroll the new active tab item title into view if needed.
|
|
299
340
|
this.$nextTick(() => {
|
|
300
341
|
const ref = this.$refs['tabs-bar']
|
|
301
|
-
this.activeTabEl = ref?.querySelector(`.w-tabs__bar-item:nth-child(${
|
|
342
|
+
this.activeTabEl = ref?.querySelector(`.w-tabs__bar-item:nth-child(${tab._index + 1})`)
|
|
302
343
|
if (this.activeTabEl) {
|
|
303
344
|
this.activeTabEl.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })
|
|
304
345
|
}
|
|
@@ -312,13 +353,14 @@ export default {
|
|
|
312
353
|
}
|
|
313
354
|
},
|
|
314
355
|
|
|
315
|
-
|
|
356
|
+
created () {
|
|
316
357
|
this.tabs = [] // Reset for hot-reloading.
|
|
317
|
-
const items = typeof this.items === 'number' ? Array(this.items).fill().map(Object) : this.items
|
|
358
|
+
const items = typeof this.items === 'number' ? Array(this.items).fill().map(Object) : this.items || []
|
|
318
359
|
items.forEach(this.addTab)
|
|
360
|
+
this.syncActiveTabFromModelValue(this.modelValue)
|
|
361
|
+
},
|
|
319
362
|
|
|
320
|
-
|
|
321
|
-
|
|
363
|
+
beforeMount () {
|
|
322
364
|
this.$nextTick(() => {
|
|
323
365
|
this.updateSlider()
|
|
324
366
|
// Disable the slider transition while loading.
|
|
@@ -333,14 +375,15 @@ export default {
|
|
|
333
375
|
},
|
|
334
376
|
|
|
335
377
|
watch: {
|
|
336
|
-
modelValue (
|
|
337
|
-
|
|
378
|
+
modelValue (value) {
|
|
379
|
+
const uid = this.resolveTabUid(value)
|
|
380
|
+
if (uid && uid !== this.activeTabUid) this.updateActiveTab(value)
|
|
338
381
|
},
|
|
339
382
|
items: {
|
|
340
383
|
handler () {
|
|
341
384
|
this.refreshTabs()
|
|
342
|
-
|
|
343
|
-
if (this.tabs.length) this.reopenTheActiveTab()
|
|
385
|
+
this.syncActiveTabFromModelValue(this.modelValue)
|
|
386
|
+
if (!this.activeTabUid && this.tabs.length) this.reopenTheActiveTab()
|
|
344
387
|
|
|
345
388
|
if (!this.noSlider) this.$nextTick(this.updateSlider)
|
|
346
389
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
slot(name="activator")
|
|
3
3
|
slot(v-if="!$slots.activator")
|
|
4
|
-
teleport(:to="teleportTarget" :disabled="!teleportTarget")
|
|
4
|
+
teleport(v-if="detachableDomReady" :to="teleportTarget" :disabled="!teleportTarget")
|
|
5
5
|
transition(:name="transitionName" appear @after-leave="onAfterLeave")
|
|
6
6
|
.w-tooltip(
|
|
7
7
|
v-if="detachableVisible"
|
|
@@ -44,6 +44,11 @@ export default {
|
|
|
44
44
|
},
|
|
45
45
|
|
|
46
46
|
data: () => ({
|
|
47
|
+
/**
|
|
48
|
+
* When false (SSR + first hydrated paint), the Teleport subtree is not rendered so HTML matches.
|
|
49
|
+
* Set true in mounted(), then the menu/tooltip portal attaches — avoids Nuxt/Vue hydration mismatches.
|
|
50
|
+
*/
|
|
51
|
+
detachableDomReady: false,
|
|
47
52
|
// The event listeners handlers have to be removed the exact same way they have been attached.
|
|
48
53
|
// Since the handler functions have variables that change after hot-reload, keep them exactly
|
|
49
54
|
// as is in an array so we can delete them on destroy.
|
|
@@ -464,29 +469,32 @@ export default {
|
|
|
464
469
|
},
|
|
465
470
|
|
|
466
471
|
mounted () {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
this.
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
//
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
472
|
+
this.detachableDomReady = true
|
|
473
|
+
this.$nextTick(() => {
|
|
474
|
+
if (this.activator) {
|
|
475
|
+
// External activator: attach via document-level delegation.
|
|
476
|
+
this.bindActivatorEvents()
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
// Slot-based activator: auto-attach DOM listeners to the slot's root element on next tick
|
|
480
|
+
// so the slot content is guaranteed to be in the DOM.
|
|
481
|
+
this.$nextTick(() => {
|
|
482
|
+
// Re-check activator prop (might have resolved from a Vue ref after the tick).
|
|
483
|
+
if (this.activator) this.bindActivatorEvents()
|
|
484
|
+
else this._attachActivatorListeners()
|
|
485
|
+
|
|
486
|
+
if (this.modelValue && !this.disable) this.open({ target: this.activatorEl })
|
|
487
|
+
})
|
|
488
|
+
}
|
|
482
489
|
|
|
483
|
-
|
|
484
|
-
|
|
490
|
+
// Unwrap the overlay if any.
|
|
491
|
+
if (this.overlay) this.overlayEl = this.$refs.overlay?.$el
|
|
485
492
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
493
|
+
if (this.modelValue && this.activator && !this.disable) {
|
|
494
|
+
this.toggle({ type: this.shouldShowOnClick ? 'click' : 'mouseenter', target: this.activatorEl })
|
|
495
|
+
}
|
|
496
|
+
else if (this.modelValue && !this.disable) this.open({ target: this.activatorEl })
|
|
497
|
+
})
|
|
490
498
|
},
|
|
491
499
|
|
|
492
500
|
unmounted () {
|