wave-ui 1.65.1 → 1.66.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 +109 -59
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +1 -1
- package/src/wave-ui/components/w-notification-manager.vue +2 -17
- package/src/wave-ui/components/w-tabs/index.vue +149 -64
- package/src/wave-ui/components/w-tabs/tab-content.vue +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.66.0",
|
|
4
4
|
"description": "An emerging UI framework for Vue.js (2 & 3) with only the bright side. :sunny:",
|
|
5
5
|
"author": "Antoni Andre <antoniandre.web@gmail.com>",
|
|
6
6
|
"homepage": "https://antoniandre.github.io/wave-ui",
|
|
@@ -10,27 +10,21 @@ transition-group(
|
|
|
10
10
|
v-if="notif._value"
|
|
11
11
|
:key="notif._uid"
|
|
12
12
|
v-model="notif._value"
|
|
13
|
-
@close="
|
|
13
|
+
@close="$waveui._notificationManager.dismiss(notif._uid)"
|
|
14
14
|
v-bind="notifProps(notif)")
|
|
15
15
|
div(v-html="notif.message")
|
|
16
16
|
</template>
|
|
17
17
|
|
|
18
18
|
<script>
|
|
19
|
-
import NotificationManager from '../utils/notification-manager'
|
|
20
|
-
|
|
21
19
|
export default {
|
|
22
20
|
name: 'w-notification-manager',
|
|
23
21
|
|
|
24
|
-
data: () => ({
|
|
25
|
-
notifManager: null
|
|
26
|
-
}),
|
|
27
|
-
|
|
28
22
|
computed: {
|
|
29
23
|
conf () {
|
|
30
24
|
return this.$waveui.config.notificationManager
|
|
31
25
|
},
|
|
32
26
|
notifications () {
|
|
33
|
-
return this.
|
|
27
|
+
return this.$waveui._notificationManager.notifications || []
|
|
34
28
|
},
|
|
35
29
|
// Possible transitions: slide-fade-down, slide-fade-left, slide-fade-right,
|
|
36
30
|
// slide-left, slide-right, bounce, twist, fade, scale, scale-fade.
|
|
@@ -46,15 +40,6 @@ export default {
|
|
|
46
40
|
const { _value, _uid, message, timeout, ...props } = notif
|
|
47
41
|
return props
|
|
48
42
|
}
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
created () {
|
|
52
|
-
this.notifManager = new NotificationManager()
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
beforeUnmount () {
|
|
56
|
-
this.notifManager.notifications = []
|
|
57
|
-
delete this.notifManager
|
|
58
43
|
}
|
|
59
44
|
}
|
|
60
45
|
</script>
|
|
@@ -2,56 +2,79 @@
|
|
|
2
2
|
.w-tabs(:class="tabsClasses")
|
|
3
3
|
.w-tabs__bar(ref="tabs-bar" :class="tabsBarClasses")
|
|
4
4
|
.w-tabs__bar-item(
|
|
5
|
-
v-for="(
|
|
5
|
+
v-for="(tab, i) in tabs"
|
|
6
6
|
:key="i"
|
|
7
|
-
:class="barItemClasses(
|
|
8
|
-
@click="!
|
|
9
|
-
@focus="$emit('focus', getOriginalItem(
|
|
10
|
-
:tabindex="!
|
|
11
|
-
@keypress.enter="!
|
|
12
|
-
:aria-selected="
|
|
7
|
+
:class="barItemClasses(tab)"
|
|
8
|
+
@click="!tab._disabled && tab._uid !== activeTabUid && openTab(tab._uid)"
|
|
9
|
+
@focus="$emit('focus', getOriginalItem(tab))"
|
|
10
|
+
:tabindex="!tab._disabled && 0"
|
|
11
|
+
@keypress.enter="!tab._disabled && openTab(tab._uid)"
|
|
12
|
+
:aria-selected="tab._uid === activeTabUid ? 'true' : 'false'"
|
|
13
13
|
role="tab")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
slot(
|
|
15
|
+
v-if="$scopedSlots[`item-title.${tab.id || i + 1}`]"
|
|
16
|
+
:name="`item-title.${tab.id || i + 1}`"
|
|
17
|
+
:item="getOriginalItem(tab)"
|
|
18
|
+
:index="i + 1"
|
|
19
|
+
:active="tab._uid === activeTabUid")
|
|
20
|
+
slot(
|
|
21
|
+
v-else
|
|
22
|
+
name="item-title"
|
|
23
|
+
:item="getOriginalItem(tab)"
|
|
24
|
+
:index="i + 1"
|
|
25
|
+
:active="tab._uid === activeTabUid")
|
|
26
|
+
div(v-html="tab[itemTitleKey]")
|
|
27
27
|
.w-tabs__bar-extra(v-if="$scopedSlots['tabs-bar-extra']")
|
|
28
28
|
slot(name="tabs-bar-extra")
|
|
29
29
|
.w-tabs__slider(v-if="!noSlider && !card" :class="sliderColor" :style="sliderStyles")
|
|
30
30
|
|
|
31
|
-
.w-tabs__content-wrap(v-if="
|
|
32
|
-
transition(
|
|
33
|
-
|
|
31
|
+
.w-tabs__content-wrap(v-if="tabs.length")
|
|
32
|
+
transition-group(v-if="keepInDom" :name="transitionName")
|
|
33
|
+
tab-content(
|
|
34
|
+
v-for="(tab, i) in tabs"
|
|
35
|
+
:key="tab._uid"
|
|
36
|
+
:item="tab"
|
|
37
|
+
v-show="tab._uid === activeTab._uid"
|
|
38
|
+
:class="contentClass")
|
|
39
|
+
slot(
|
|
40
|
+
v-if="$scopedSlots[`item-content.${tab._index + 1}`]"
|
|
41
|
+
:name="`item-content.${tab._index + 1}`"
|
|
42
|
+
:item="getOriginalItem(tab)"
|
|
43
|
+
:index="tab._index + 1"
|
|
44
|
+
:active="tab._index === activeTab._index")
|
|
45
|
+
slot(
|
|
46
|
+
v-else
|
|
47
|
+
name="item-content"
|
|
48
|
+
:item="getOriginalItem(tab)"
|
|
49
|
+
:index="tab._index + 1"
|
|
50
|
+
:active="tab._index === activeTab._index")
|
|
51
|
+
div(v-if="tab[itemContentKey]" v-html="tab[itemContentKey]")
|
|
52
|
+
transition(v-else :name="transitionName" :mode="transitionMode")
|
|
53
|
+
keep-alive(:exclude="keepAlive ? '' : 'tab-content'")
|
|
34
54
|
//- Keep-alive only works with components, not with DOM nodes.
|
|
35
|
-
tab-content(:
|
|
36
|
-
|
|
37
|
-
v-if="
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
tab-content(:key="activeTabUid" :item="activeTab" :class="contentClass")
|
|
56
|
+
template(#default="{ item }")
|
|
57
|
+
template(v-if="item")
|
|
58
|
+
slot(
|
|
59
|
+
v-if="$scopedSlots[`item-content.${item._index + 1}`]"
|
|
60
|
+
:name="`item-content.${item._index + 1}`"
|
|
61
|
+
:item="getOriginalItem(item)"
|
|
62
|
+
:index="item._index + 1"
|
|
63
|
+
:active="item._uid === activeTabUid")
|
|
64
|
+
slot(
|
|
65
|
+
v-else
|
|
66
|
+
name="item-content"
|
|
67
|
+
:item="getOriginalItem(item)"
|
|
68
|
+
:index="item._index + 1"
|
|
69
|
+
:active="item._uid === activeTabUid")
|
|
70
|
+
div(v-if="item[itemContentKey]" v-html="item[itemContentKey]")
|
|
49
71
|
</template>
|
|
50
72
|
|
|
51
73
|
<script>
|
|
52
|
-
import Vue from 'vue'
|
|
53
74
|
import TabContent from './tab-content.vue'
|
|
54
75
|
|
|
76
|
+
let uid = 0
|
|
77
|
+
|
|
55
78
|
export default {
|
|
56
79
|
name: 'w-tabs',
|
|
57
80
|
|
|
@@ -60,6 +83,7 @@ export default {
|
|
|
60
83
|
color: { type: String },
|
|
61
84
|
bgColor: { type: String },
|
|
62
85
|
items: { type: [Array, Number] },
|
|
86
|
+
itemIdKey: { type: String, default: 'id' },
|
|
63
87
|
itemTitleKey: { type: String, default: 'title' },
|
|
64
88
|
itemContentKey: { type: String, default: 'content' },
|
|
65
89
|
titleClass: { type: String },
|
|
@@ -72,7 +96,9 @@ export default {
|
|
|
72
96
|
fillBar: { type: Boolean },
|
|
73
97
|
center: { type: Boolean },
|
|
74
98
|
right: { type: Boolean },
|
|
75
|
-
card: { type: Boolean }
|
|
99
|
+
card: { type: Boolean },
|
|
100
|
+
keepAlive: { type: Boolean, default: true },
|
|
101
|
+
keepInDom: { type: Boolean, default: false }
|
|
76
102
|
},
|
|
77
103
|
|
|
78
104
|
components: { TabContent },
|
|
@@ -80,7 +106,9 @@ export default {
|
|
|
80
106
|
emits: ['input', 'update:modelValue', 'focus'],
|
|
81
107
|
|
|
82
108
|
data: () => ({
|
|
109
|
+
tabs: [],
|
|
83
110
|
activeTabEl: null,
|
|
111
|
+
activeTabUid: null,
|
|
84
112
|
activeTabIndex: 0,
|
|
85
113
|
prevTabIndex: -1, // To detect transition direction.
|
|
86
114
|
slider: {
|
|
@@ -104,19 +132,13 @@ export default {
|
|
|
104
132
|
return this.activeTab._index < this.prevTabIndex ? 'right' : 'left'
|
|
105
133
|
},
|
|
106
134
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
// eslint-disable-next-line new-cap
|
|
111
|
-
return items.map((item, _index) => new Vue.observable({
|
|
112
|
-
...item,
|
|
113
|
-
_index,
|
|
114
|
-
_disabled: !!item.disabled
|
|
115
|
-
}))
|
|
135
|
+
activeTab () {
|
|
136
|
+
return this.tabsByUid[this.activeTabUid] || this.tabs[0] || {}
|
|
116
137
|
},
|
|
117
138
|
|
|
118
|
-
|
|
119
|
-
|
|
139
|
+
// An object indexing the tabs by their uid.
|
|
140
|
+
tabsByUid () {
|
|
141
|
+
return this.tabs.reduce((obj, tab) => ((obj[tab._uid] = tab) && obj), {})
|
|
120
142
|
},
|
|
121
143
|
|
|
122
144
|
tabsClasses () {
|
|
@@ -145,6 +167,54 @@ export default {
|
|
|
145
167
|
},
|
|
146
168
|
|
|
147
169
|
methods: {
|
|
170
|
+
// Adding a tab in the list.
|
|
171
|
+
addTab (item) {
|
|
172
|
+
// If there is no unique ID provided, inject one in each tab.
|
|
173
|
+
// This will cause a single other update from watching the tabs items and stop there.
|
|
174
|
+
if (!(item[this.itemIdKey] ?? item._uid ?? false)) item._uid = +`${this._uid}${++uid}`
|
|
175
|
+
|
|
176
|
+
this.tabs.push({
|
|
177
|
+
_uid: item[this.itemIdKey] ?? item._uid,
|
|
178
|
+
_index: this.tabs.length,
|
|
179
|
+
...item,
|
|
180
|
+
_disabled: !!item.disabled
|
|
181
|
+
})
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
refreshTabs () {
|
|
185
|
+
let items = this.items
|
|
186
|
+
if (typeof items === 'number') items = Array(items).fill().map((_, i) => this.tabs[i] || {})
|
|
187
|
+
|
|
188
|
+
this.tabs = items.map((item, _index) => {
|
|
189
|
+
// If there is no unique ID provided, inject one in each tab.
|
|
190
|
+
// This will cause a single other update from watching the tabs items and stop there.
|
|
191
|
+
if (!(item[this.itemIdKey] ?? item._uid ?? false)) item._uid = +`${this._uid}${++uid}`
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
...item,
|
|
195
|
+
_uid: item[this.itemIdKey] ?? item._uid,
|
|
196
|
+
_index,
|
|
197
|
+
_disabled: !!item.disabled
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
reopenTheActiveTab () {
|
|
203
|
+
// If there is only 1 tab left open it.
|
|
204
|
+
if (this.tabs.length === 1) return this.openTab(this.tabs[0]._uid)
|
|
205
|
+
|
|
206
|
+
// First try to find the same uid in remaining tabs.
|
|
207
|
+
let uid = this.tabsByUid[this.activeTabUid]?._uid
|
|
208
|
+
|
|
209
|
+
// If not found, try to open the tab with the same index.
|
|
210
|
+
if (!uid) uid = this.tabs[this.activeTabIndex]?._uid
|
|
211
|
+
|
|
212
|
+
// If not found (no tab to the right), try to open the next tab to the left.
|
|
213
|
+
if (!uid) uid = this.tabs[Math.max(this.activeTabIndex - 1, this.tabs.length - 1)]?._uid
|
|
214
|
+
|
|
215
|
+
if (uid) this.openTab(uid)
|
|
216
|
+
},
|
|
217
|
+
|
|
148
218
|
onResize () {
|
|
149
219
|
this.updateSlider(false)
|
|
150
220
|
},
|
|
@@ -161,11 +231,14 @@ export default {
|
|
|
161
231
|
}
|
|
162
232
|
},
|
|
163
233
|
|
|
164
|
-
|
|
234
|
+
// Switching tabs.
|
|
235
|
+
openTab (uid) {
|
|
165
236
|
this.prevTabIndex = this.activeTabIndex // To resolve the transition direction.
|
|
166
|
-
|
|
167
|
-
this
|
|
168
|
-
this
|
|
237
|
+
const tab = this.tabsByUid[uid]
|
|
238
|
+
this.activeTabIndex = tab._index
|
|
239
|
+
this.activeTabUid = tab._uid
|
|
240
|
+
this.$emit('update:modelValue', tab._index)
|
|
241
|
+
this.$emit('input', tab._index)
|
|
169
242
|
|
|
170
243
|
if (!this.noSlider) this.$nextTick(this.updateSlider)
|
|
171
244
|
},
|
|
@@ -173,7 +246,8 @@ export default {
|
|
|
173
246
|
// Updates the slider position.
|
|
174
247
|
updateSlider (domLookup = true) {
|
|
175
248
|
if (domLookup) {
|
|
176
|
-
|
|
249
|
+
const ref = this.$refs['tabs-bar']
|
|
250
|
+
this.activeTabEl = ref && ref.querySelector('.w-tabs__bar-item--active')
|
|
177
251
|
}
|
|
178
252
|
|
|
179
253
|
if (!this.fillBar && this.activeTabEl) {
|
|
@@ -185,15 +259,16 @@ export default {
|
|
|
185
259
|
this.slider.width = `${width}px`
|
|
186
260
|
}
|
|
187
261
|
else {
|
|
188
|
-
this.slider.left = `${this.activeTab._index * 100 / this.
|
|
189
|
-
this.slider.width = `${100 / this.
|
|
262
|
+
this.slider.left = `${this.activeTab._index * 100 / this.tabs.length}%`
|
|
263
|
+
this.slider.width = `${100 / this.tabs.length}%`
|
|
190
264
|
}
|
|
191
265
|
},
|
|
192
266
|
|
|
193
267
|
updateActiveTab (index) {
|
|
194
268
|
if (typeof index === 'string') index = ~~index
|
|
195
269
|
else if (isNaN(index) || index < 0) index = 0
|
|
196
|
-
|
|
270
|
+
|
|
271
|
+
this.openTab(this.tabs[index]._uid)
|
|
197
272
|
|
|
198
273
|
// Scroll the new active tab item title into view if needed.
|
|
199
274
|
this.$nextTick(() => {
|
|
@@ -207,11 +282,15 @@ export default {
|
|
|
207
282
|
|
|
208
283
|
// Return the original item (so there is no `_index`, etc.).
|
|
209
284
|
getOriginalItem (item) {
|
|
210
|
-
return this.items[item._index]
|
|
285
|
+
return this.items[item._index] || {}
|
|
211
286
|
}
|
|
212
287
|
},
|
|
213
288
|
|
|
214
289
|
beforeMount () {
|
|
290
|
+
this.tabs = [] // Reset for hot-reloading.
|
|
291
|
+
const items = typeof this.items === 'number' ? Array(this.items).fill().map(Object) : this.items
|
|
292
|
+
items.forEach(this.addTab)
|
|
293
|
+
|
|
215
294
|
this.updateActiveTab(this.value)
|
|
216
295
|
|
|
217
296
|
this.$nextTick(() => {
|
|
@@ -229,13 +308,17 @@ export default {
|
|
|
229
308
|
|
|
230
309
|
watch: {
|
|
231
310
|
value (index) {
|
|
232
|
-
this.updateActiveTab(index)
|
|
311
|
+
if (index !== this.activeTabIndex) this.updateActiveTab(index)
|
|
233
312
|
},
|
|
234
|
-
items
|
|
235
|
-
|
|
236
|
-
|
|
313
|
+
items: {
|
|
314
|
+
handler () {
|
|
315
|
+
this.refreshTabs()
|
|
237
316
|
|
|
238
|
-
|
|
317
|
+
if (this.tabs.length) this.reopenTheActiveTab()
|
|
318
|
+
|
|
319
|
+
if (!this.noSlider) this.$nextTick(this.updateSlider)
|
|
320
|
+
},
|
|
321
|
+
deep: true
|
|
239
322
|
},
|
|
240
323
|
fillBar () {
|
|
241
324
|
if (!this.noSlider) this.$nextTick(this.updateSlider)
|
|
@@ -374,8 +457,10 @@ export default {
|
|
|
374
457
|
.w-tabs-slide-left-leave-active,
|
|
375
458
|
.w-tabs-slide-right-leave-active {
|
|
376
459
|
position: absolute;
|
|
460
|
+
top: 0;
|
|
377
461
|
left: 0;
|
|
378
462
|
right: 0;
|
|
463
|
+
overflow: hidden;
|
|
379
464
|
}
|
|
380
465
|
|
|
381
466
|
.w-tabs-slide-left-enter-active {animation: w-tabs-slide-left-enter $transition-duration + 0.15s;}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
.w-tabs__content
|
|
3
|
-
slot
|
|
3
|
+
slot(:item="item")
|
|
4
4
|
</template>
|
|
5
5
|
|
|
6
6
|
<script>
|
|
7
7
|
// Keep-alive only works with components, not with DOM nodes.
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
name: 'tab-content', // Keep-alive include/exclude mechanism is component-name-based.
|
|
11
|
+
|
|
12
|
+
props: { item: Object }
|
|
13
|
+
}
|
|
8
14
|
</script>
|