wave-ui 3.1.3 → 3.3.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 +804 -738
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +1 -1
- package/src/wave-ui/components/w-button/button.vue +14 -10
- package/src/wave-ui/components/w-select.vue +1 -1
- package/src/wave-ui/components/w-table.vue +163 -65
- package/src/wave-ui/components/w-tree.vue +75 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.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",
|
|
@@ -165,7 +165,7 @@ $spinner-size: 40;
|
|
|
165
165
|
z-index: 1;
|
|
166
166
|
// Background-color must not transition to not affect the hover & focus states
|
|
167
167
|
// in :before & :after.
|
|
168
|
-
transition: $transition-duration, background-color 0s, padding 0s;
|
|
168
|
+
transition: all $transition-duration, background-color 0s, padding 0s;
|
|
169
169
|
-webkit-tap-highlight-color: transparent;
|
|
170
170
|
|
|
171
171
|
@include themeable;
|
|
@@ -253,10 +253,10 @@ $spinner-size: 40;
|
|
|
253
253
|
// Button states.
|
|
254
254
|
// ------------------------------------------------------
|
|
255
255
|
// Hover & focus states - inside button.
|
|
256
|
-
&:hover:before, &:focus:before {opacity: 0.
|
|
257
|
-
&--dark:hover:before, &--dark:focus:before {opacity: 0.
|
|
258
|
-
&--outline:hover:before, &--outline:focus:before,
|
|
259
|
-
&--text:hover:before, &--text:focus:before {opacity: 0.12;}
|
|
256
|
+
&:hover:before, &:focus-visible:before {opacity: 0.2;}
|
|
257
|
+
&--dark:hover:before, &--dark:focus-visible:before {opacity: 0.4;}
|
|
258
|
+
&--outline:hover:before, &--outline:focus-visible:before,
|
|
259
|
+
&--text:hover:before, &--text:focus-visible:before {opacity: 0.12;}
|
|
260
260
|
|
|
261
261
|
// Focus state - outside button.
|
|
262
262
|
&:after {
|
|
@@ -273,22 +273,25 @@ $spinner-size: 40;
|
|
|
273
273
|
transition: opacity 0.2s cubic-bezier(0.45, 0.05, 0.55, 0.95), transform 0.25s ease-in;
|
|
274
274
|
transform: scale(0.85, 0.7);
|
|
275
275
|
}
|
|
276
|
-
&:focus:after {
|
|
276
|
+
&:focus-visible:after {
|
|
277
277
|
opacity: 0.4;
|
|
278
278
|
transform: scale(1);
|
|
279
279
|
transition: opacity 0.2s cubic-bezier(0.45, 0.05, 0.55, 0.95), transform 0.25s ease-out;
|
|
280
280
|
}
|
|
281
|
-
&--dark:focus:after {opacity: 0.2;}
|
|
281
|
+
&--dark:focus-visible:after {opacity: 0.2;}
|
|
282
282
|
|
|
283
283
|
// Active state.
|
|
284
|
-
&:active
|
|
285
|
-
|
|
284
|
+
&:active {transform: scale(1.02);}
|
|
285
|
+
&:active:before {opacity: 0.3;}
|
|
286
|
+
&--dark:active:before, &.primary--bg:active:before {opacity: 0.35;}
|
|
286
287
|
|
|
287
288
|
// Disable visual feedback on loading and disabled buttons.
|
|
288
289
|
&--loading:hover:before,
|
|
289
|
-
&--loading:focus:before,
|
|
290
|
+
&--loading:focus-visible:before,
|
|
290
291
|
&--loading:active:before,
|
|
291
292
|
&[disabled]:before {opacity: 0;}
|
|
293
|
+
&--loading:active,
|
|
294
|
+
&[disabled] {transform: none;}
|
|
292
295
|
// ------------------------------------------------------
|
|
293
296
|
|
|
294
297
|
// Disable events binding on nested content.
|
|
@@ -304,6 +307,7 @@ $spinner-size: 40;
|
|
|
304
307
|
align-items: center;
|
|
305
308
|
justify-content: center;
|
|
306
309
|
background: inherit;
|
|
310
|
+
border-radius: inherit;
|
|
307
311
|
|
|
308
312
|
svg {height: 75%;}
|
|
309
313
|
circle {
|
|
@@ -190,7 +190,7 @@ export default {
|
|
|
190
190
|
},
|
|
191
191
|
selectionString () {
|
|
192
192
|
return this.inputValue && this.inputValue.map(
|
|
193
|
-
item => item[this.itemValueKey] !== undefined ? item[this.itemLabelKey] : (item[this.itemLabelKey]
|
|
193
|
+
item => item[this.itemValueKey] !== undefined ? item[this.itemLabelKey] : (item[this.itemLabelKey] ?? item)
|
|
194
194
|
).join(', ')
|
|
195
195
|
},
|
|
196
196
|
classes () {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
col.w-table__col(
|
|
10
10
|
v-for="(header, i) in headers"
|
|
11
11
|
:key="i"
|
|
12
|
-
:width="header.width || null"
|
|
12
|
+
:width="header.width || null"
|
|
13
|
+
:class="colClasses[i]")
|
|
13
14
|
|
|
14
15
|
//- Table header.
|
|
15
16
|
thead(v-if="!noHeaders")
|
|
@@ -135,42 +136,41 @@
|
|
|
135
136
|
slot(name="footer")
|
|
136
137
|
tr.w-table__row.w-table__pagination-wrap(v-if="pagination && paginationConfig")
|
|
137
138
|
td.w-table__cell(:colspan="headers.length")
|
|
138
|
-
.w-table__pagination
|
|
139
|
-
| {{ paginationConfig }}
|
|
139
|
+
.w-table__pagination.w-pagination
|
|
140
140
|
slot(
|
|
141
141
|
name="pagination"
|
|
142
|
-
:range="`${paginationConfig.start}-${paginationConfig.end}
|
|
142
|
+
:range="`${paginationConfig.start}-${paginationConfig.end}`"
|
|
143
143
|
:total="paginationConfig.total")
|
|
144
|
-
w-select.
|
|
144
|
+
w-select.w-pagination__items-per-page(
|
|
145
145
|
v-if="paginationConfig.itemsPerPageOptions"
|
|
146
146
|
v-model="paginationConfig.itemsPerPage"
|
|
147
|
-
@input="updatePaginationConfig"
|
|
147
|
+
@input="updatePaginationConfig({ itemsPerPage: paginationConfig.itemsPerPage })"
|
|
148
148
|
:items="paginationConfig.itemsPerPageOptions"
|
|
149
149
|
label-position="left"
|
|
150
150
|
label="Items per page"
|
|
151
151
|
label-color="inherit")
|
|
152
|
-
.
|
|
153
|
-
w-button.
|
|
152
|
+
.pages-wrap
|
|
153
|
+
w-button.w-pagination__arrow.w-pagination__arrow--prev(
|
|
154
154
|
@click="goToPage('-1')"
|
|
155
155
|
:disabled="paginationConfig.page <= 1"
|
|
156
156
|
icon="wi-chevron-left"
|
|
157
157
|
text
|
|
158
158
|
lg)
|
|
159
|
-
w-button.
|
|
159
|
+
w-button.w-pagination__page(
|
|
160
160
|
v-for="i in paginationConfig.pagesCount"
|
|
161
161
|
:key="i"
|
|
162
|
-
@click="goToPage(i)"
|
|
162
|
+
@click="i !== paginationConfig.page && goToPage(i)"
|
|
163
|
+
:class="{ 'w-pagination__page--active': i === paginationConfig.page }"
|
|
163
164
|
round
|
|
164
|
-
text
|
|
165
165
|
lg) {{ i }}
|
|
166
|
-
w-button.
|
|
166
|
+
w-button.w-pagination__arrow.w-pagination__arrow--next(
|
|
167
167
|
@click="goToPage('+1')"
|
|
168
168
|
:disabled="paginationConfig.page >= paginationConfig.pagesCount"
|
|
169
169
|
icon="wi-chevron-right"
|
|
170
170
|
text
|
|
171
171
|
lg)
|
|
172
|
-
span.
|
|
173
|
-
{{ paginationConfig.start }}-{{ paginationConfig.end }} of {{ paginationConfig.total }}
|
|
172
|
+
span.w-pagination__results.
|
|
173
|
+
{{ paginationConfig.start }}-{{ paginationConfig.end || paginationConfig.total }} of {{ paginationConfig.total }}
|
|
174
174
|
</template>
|
|
175
175
|
|
|
176
176
|
<script>
|
|
@@ -195,6 +195,9 @@ export default {
|
|
|
195
195
|
loading: { type: [Boolean, String] }, // Bool or 'header' to only display the bar in the header.
|
|
196
196
|
// Allow single sort: `+id`, or multiple in an array like: ['+id', '-firstName'].
|
|
197
197
|
sort: { type: [String, Array] },
|
|
198
|
+
sortFunction: { type: Function },
|
|
199
|
+
filter: { type: Function },
|
|
200
|
+
fetch: { type: Function },
|
|
198
201
|
|
|
199
202
|
expandableRows: {
|
|
200
203
|
validator: value => {
|
|
@@ -229,11 +232,16 @@ export default {
|
|
|
229
232
|
// Useful to select or expand a row, and even after a filter, the same row will stay selected or expanded.
|
|
230
233
|
uidKey: { type: String, default: 'id' },
|
|
231
234
|
|
|
232
|
-
filter: { type: Function },
|
|
233
|
-
sortFunction: { type: Function },
|
|
234
235
|
mobileBreakpoint: { type: Number, default: 0 },
|
|
235
236
|
resizableColumns: { type: Boolean },
|
|
236
237
|
|
|
238
|
+
// An object containing:
|
|
239
|
+
// - itemsPerPage
|
|
240
|
+
// - itemsPerPageOptions
|
|
241
|
+
// - start
|
|
242
|
+
// - end
|
|
243
|
+
// - page
|
|
244
|
+
// - total
|
|
237
245
|
pagination: {
|
|
238
246
|
type: [Boolean, Object, String],
|
|
239
247
|
validator: object => {
|
|
@@ -283,7 +291,7 @@ export default {
|
|
|
283
291
|
computed: {
|
|
284
292
|
tableItems () {
|
|
285
293
|
return this.items.map((item, i) => {
|
|
286
|
-
item._uid = item[this.uidKey]
|
|
294
|
+
item._uid = item[this.uidKey] ?? i
|
|
287
295
|
return item
|
|
288
296
|
})
|
|
289
297
|
},
|
|
@@ -293,7 +301,7 @@ export default {
|
|
|
293
301
|
},
|
|
294
302
|
|
|
295
303
|
sortedItems () {
|
|
296
|
-
if (!this.activeSorting.length || this.sortFunction) return this.filteredItems
|
|
304
|
+
if (!this.activeSorting.length || this.sortFunction || this.fetch) return this.filteredItems
|
|
297
305
|
|
|
298
306
|
// Only sort with 1 key for now, may handle more later.
|
|
299
307
|
const sortKey1 = this.activeSorting[0].replace(/^[+-]/, '')
|
|
@@ -310,6 +318,10 @@ export default {
|
|
|
310
318
|
})
|
|
311
319
|
},
|
|
312
320
|
|
|
321
|
+
paginatedItems () {
|
|
322
|
+
return typeof this.fetch === 'function' ? this.sortedItems : this.sortedItems.slice(this.paginationConfig.start - 1, this.paginationConfig.end)
|
|
323
|
+
},
|
|
324
|
+
|
|
313
325
|
// Returns an object containing { key1: '+', key2: '-' }. With + or - for ASC/DESC.
|
|
314
326
|
activeSortingKeys () {
|
|
315
327
|
return this.activeSorting.reduce((obj, item) => {
|
|
@@ -324,8 +336,16 @@ export default {
|
|
|
324
336
|
}
|
|
325
337
|
},
|
|
326
338
|
|
|
339
|
+
colClasses () {
|
|
340
|
+
return this.headers.map(header => {
|
|
341
|
+
return { 'w-table__col--highlighted': this.activeSortingKeys[header.key] }
|
|
342
|
+
}) || []
|
|
343
|
+
},
|
|
344
|
+
|
|
327
345
|
classes () {
|
|
328
346
|
return {
|
|
347
|
+
'w-table--loading': this.loading,
|
|
348
|
+
'w-table--loading-in-header': this.loading === 'header',
|
|
329
349
|
'w-table--fixed-layout': this.fixedLayout || this.resizableColumns || this.hasStickyColumn,
|
|
330
350
|
'w-table--mobile': this.isMobile || null,
|
|
331
351
|
'w-table--resizable-cols': this.resizableColumns || null,
|
|
@@ -354,10 +374,6 @@ export default {
|
|
|
354
374
|
// Faster lookup than array.includes(uid) and also cached.
|
|
355
375
|
expandedRowsByUid () {
|
|
356
376
|
return this.expandedRowsInternal.reduce((obj, uid) => (obj[uid] = true) && obj, {})
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
paginatedItems () {
|
|
360
|
-
return this.sortedItems.slice(this.paginationConfig.start, this.paginationConfig.end)
|
|
361
377
|
}
|
|
362
378
|
},
|
|
363
379
|
|
|
@@ -389,9 +405,8 @@ export default {
|
|
|
389
405
|
|
|
390
406
|
this.$emit('update:sort', this.activeSorting)
|
|
391
407
|
|
|
392
|
-
if (typeof this.sortFunction === 'function')
|
|
393
|
-
|
|
394
|
-
}
|
|
408
|
+
if (typeof this.sortFunction === 'function') await this.sortFunction(this.activeSorting)
|
|
409
|
+
else if (typeof this.fetch === 'function') await this.callApiFetch()
|
|
395
410
|
},
|
|
396
411
|
|
|
397
412
|
doSelectRow (item, index) {
|
|
@@ -553,33 +568,75 @@ export default {
|
|
|
553
568
|
}, 0)
|
|
554
569
|
},
|
|
555
570
|
|
|
556
|
-
|
|
557
|
-
const itemsPerPage = this.pagination?.itemsPerPage
|
|
571
|
+
initPagination () {
|
|
572
|
+
const itemsPerPage = this.pagination?.itemsPerPage ?? 20 // Can also be `0` for all.
|
|
573
|
+
|
|
558
574
|
const itemsPerPageOptions = this.pagination?.itemsPerPageOptions || [20, 100, { label: 'All', value: 0 }]
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
575
|
+
// If the given itemsPerPage is not in the itemsPerPageOptions, add it.
|
|
576
|
+
if (!itemsPerPageOptions.find(item => (item?.value ?? item) === +itemsPerPage)) {
|
|
577
|
+
itemsPerPageOptions.push(itemsPerPage)
|
|
578
|
+
}
|
|
579
|
+
this.paginationConfig.itemsPerPageOptions = itemsPerPageOptions.map(item => ({
|
|
580
|
+
label: ['string', 'number'].includes(typeof item) ? item.toString() : (item.label || item.value),
|
|
581
|
+
value: ['string', 'number'].includes(typeof item) ? ~~item : (item.value ?? item.label)
|
|
582
|
+
}))
|
|
583
|
+
// Sort the options in an ascending order.
|
|
584
|
+
this.paginationConfig.itemsPerPageOptions.sort((a, b) => a.value < b.value ? -1 : 1)
|
|
585
|
+
const optionAll = this.paginationConfig.itemsPerPageOptions.shift()
|
|
586
|
+
this.paginationConfig.itemsPerPageOptions.push(optionAll)
|
|
587
|
+
|
|
588
|
+
this.updatePaginationConfig({
|
|
563
589
|
itemsPerPage,
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
590
|
+
page: this.pagination.page || 1,
|
|
591
|
+
total: this.pagination.total || this.items.length
|
|
592
|
+
})
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
updatePaginationConfig ({ itemsPerPage, page, total }) {
|
|
596
|
+
if (total) this.paginationConfig.total = total
|
|
597
|
+
if (itemsPerPage !== undefined) {
|
|
598
|
+
this.paginationConfig.itemsPerPage = itemsPerPage
|
|
599
|
+
itemsPerPage = itemsPerPage || this.paginationConfig.total // If `0`, take all the results.
|
|
600
|
+
this.paginationConfig.page = 1;
|
|
601
|
+
({ page } = this.paginationConfig) // Shorthand var for next lines.
|
|
602
|
+
total = this.paginationConfig.total // Shorthand var for next lines.
|
|
603
|
+
this.paginationConfig.start = 1
|
|
604
|
+
this.paginationConfig.end = total >= (itemsPerPage * page) ? (itemsPerPage * page) : (total % (itemsPerPage * page))
|
|
605
|
+
this.paginationConfig.pagesCount = Math.ceil(total / itemsPerPage)
|
|
573
606
|
}
|
|
607
|
+
if (page) this.goToPage(page)
|
|
574
608
|
},
|
|
575
609
|
|
|
576
|
-
|
|
610
|
+
/**
|
|
611
|
+
* Goes to a given page or to the next or previous page.
|
|
612
|
+
*
|
|
613
|
+
* @param {Number|String} page a number to go to a specific page or `-1`, `+1` for prev & next page.
|
|
614
|
+
*/
|
|
615
|
+
async goToPage (page) {
|
|
577
616
|
if (['-1', '+1'].includes(page)) this.paginationConfig.page += +page
|
|
578
617
|
else this.paginationConfig.page = page
|
|
579
|
-
const { itemsPerPage } = this.paginationConfig
|
|
618
|
+
const { itemsPerPage, total } = this.paginationConfig
|
|
580
619
|
this.paginationConfig.page = Math.max(1, this.paginationConfig.page)
|
|
581
620
|
this.paginationConfig.start = (itemsPerPage * (this.paginationConfig.page - 1)) + 1
|
|
582
|
-
this.paginationConfig.end = (this.paginationConfig.start - 1) + itemsPerPage
|
|
621
|
+
this.paginationConfig.end = (this.paginationConfig.start - 1) + (itemsPerPage || total)
|
|
622
|
+
|
|
623
|
+
if (typeof this.fetch === 'function') await this.callApiFetch()
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Call a user provided fetch function in order to fetch table items from an API.
|
|
628
|
+
* While waiting for the call to resolve, nothing in the table will change.
|
|
629
|
+
*/
|
|
630
|
+
async callApiFetch () {
|
|
631
|
+
const { page, start, end, total, itemsPerPage } = this.paginationConfig
|
|
632
|
+
return await this.fetch({
|
|
633
|
+
page,
|
|
634
|
+
start,
|
|
635
|
+
end: end || total,
|
|
636
|
+
total,
|
|
637
|
+
itemsPerPage: itemsPerPage || total,
|
|
638
|
+
sorting: this.activeSorting
|
|
639
|
+
})
|
|
583
640
|
}
|
|
584
641
|
},
|
|
585
642
|
|
|
@@ -590,7 +647,7 @@ export default {
|
|
|
590
647
|
if ((this.expandedRows || []).length) this.expandedRowsInternal = this.expandedRows
|
|
591
648
|
if ((this.selectedRows || []).length) this.selectedRowsInternal = this.selectedRows
|
|
592
649
|
|
|
593
|
-
if (this.pagination) this.
|
|
650
|
+
if (this.pagination) this.initPagination()
|
|
594
651
|
},
|
|
595
652
|
|
|
596
653
|
watch: {
|
|
@@ -617,9 +674,14 @@ export default {
|
|
|
617
674
|
this.selectedRowsInternal = Array.isArray(array) && array.length ? this.selectedRows : []
|
|
618
675
|
},
|
|
619
676
|
|
|
620
|
-
pagination
|
|
621
|
-
|
|
622
|
-
|
|
677
|
+
'pagination.page' (page) {
|
|
678
|
+
this.updatePaginationConfig({ page })
|
|
679
|
+
},
|
|
680
|
+
'pagination.itemsPerPage' (itemsPerPage) {
|
|
681
|
+
this.updatePaginationConfig({ itemsPerPage })
|
|
682
|
+
},
|
|
683
|
+
'pagination.total' (total) {
|
|
684
|
+
this.updatePaginationConfig({ total })
|
|
623
685
|
}
|
|
624
686
|
}
|
|
625
687
|
}
|
|
@@ -655,6 +717,12 @@ $tr-border-top: 1px;
|
|
|
655
717
|
user-select: none;
|
|
656
718
|
}
|
|
657
719
|
|
|
720
|
+
// Table columns.
|
|
721
|
+
// ------------------------------------------------------
|
|
722
|
+
&__col--highlighted {
|
|
723
|
+
background-color: rgba(var(--w-contrast-bg-color-rgb), 0.04);
|
|
724
|
+
}
|
|
725
|
+
|
|
658
726
|
// Table headers.
|
|
659
727
|
// ------------------------------------------------------
|
|
660
728
|
thead {position: relative;}
|
|
@@ -744,7 +812,7 @@ $tr-border-top: 1px;
|
|
|
744
812
|
left: 0;
|
|
745
813
|
right: 0;
|
|
746
814
|
}
|
|
747
|
-
&__progress-bar td {padding: 0;height:
|
|
815
|
+
&__progress-bar td {padding: 0;height: 0;}
|
|
748
816
|
@-moz-document url-prefix() {
|
|
749
817
|
&__progress-bar td {height: 100%;}
|
|
750
818
|
}
|
|
@@ -753,7 +821,7 @@ $tr-border-top: 1px;
|
|
|
753
821
|
display: flex;
|
|
754
822
|
align-items: center;
|
|
755
823
|
justify-content: center;
|
|
756
|
-
height:100%;
|
|
824
|
+
height: 100%;
|
|
757
825
|
width: 100%;
|
|
758
826
|
padding-top: 2 * $base-increment;
|
|
759
827
|
padding-bottom: 2 * $base-increment;
|
|
@@ -761,6 +829,9 @@ $tr-border-top: 1px;
|
|
|
761
829
|
|
|
762
830
|
// Table body.
|
|
763
831
|
// ------------------------------------------------------
|
|
832
|
+
tbody {transition: opacity $transition-duration;}
|
|
833
|
+
&--loading-in-header tbody {opacity: 0.6;}
|
|
834
|
+
|
|
764
835
|
tbody tr {border-top: $tr-border-top solid rgba(var(--w-base-color-rgb), 0.06);}
|
|
765
836
|
// Don't apply built-in bg color if a bg color is already found on a tr.
|
|
766
837
|
tbody tr:nth-child(odd):not(.no-data):not([class*="--bg"]) {background-color: $table-tr-odd-color;}
|
|
@@ -818,52 +889,79 @@ $tr-border-top: 1px;
|
|
|
818
889
|
|
|
819
890
|
// Table footer.
|
|
820
891
|
// ------------------------------------------------------
|
|
821
|
-
&__footer &__cell {
|
|
822
|
-
padding-top: $base-increment;
|
|
823
|
-
padding-bottom: $base-increment;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
892
|
&--fixed-footer tfoot {
|
|
827
893
|
position: sticky;
|
|
828
|
-
bottom:
|
|
894
|
+
bottom: -1px;
|
|
829
895
|
background-color: $base-bg-color;
|
|
830
896
|
z-index: 1; // For sticky columns to go under.
|
|
831
897
|
|
|
832
898
|
&:after {
|
|
833
899
|
content: '';
|
|
834
900
|
position: absolute;
|
|
835
|
-
|
|
901
|
+
top: 0;
|
|
836
902
|
left: 0;
|
|
837
903
|
right: 0;
|
|
838
|
-
border-
|
|
904
|
+
border-top: $border;
|
|
839
905
|
}
|
|
840
906
|
}
|
|
841
907
|
|
|
908
|
+
&__footer &__cell {
|
|
909
|
+
padding-top: $base-increment;
|
|
910
|
+
padding-bottom: $base-increment;
|
|
911
|
+
}
|
|
912
|
+
|
|
842
913
|
// Pagination.
|
|
843
914
|
// ------------------------------------------------------
|
|
844
|
-
&__pagination-wrap {
|
|
845
|
-
border-top: $border;
|
|
846
|
-
}
|
|
847
915
|
&__pagination {
|
|
848
916
|
display: flex;
|
|
849
917
|
align-items: center;
|
|
850
918
|
justify-content: flex-end;
|
|
851
919
|
|
|
852
|
-
.
|
|
853
|
-
flex
|
|
920
|
+
.w-pagination__items-per-page {
|
|
921
|
+
flex: 0 0 auto;
|
|
854
922
|
text-align: right;
|
|
855
923
|
}
|
|
856
924
|
|
|
857
|
-
.
|
|
925
|
+
.pages-wrap {
|
|
858
926
|
margin-left: 3 * $base-increment;
|
|
859
927
|
margin-right: 3 * $base-increment;
|
|
928
|
+
overflow: auto;
|
|
929
|
+
max-height: 4.5em;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.w-pagination__page {
|
|
933
|
+
margin: 0.5 * $base-increment;
|
|
934
|
+
font-size: 0.9em;
|
|
935
|
+
aspect-ratio: 1;
|
|
936
|
+
overflow: hidden;
|
|
937
|
+
color: rgba(var(--w-base-color-rgb), 0.65);
|
|
938
|
+
background-color: rgba(var(--w-base-bg-color-rgb), 0.4);
|
|
939
|
+
|
|
940
|
+
&:hover:before {
|
|
941
|
+
background-color: $primary;
|
|
942
|
+
opacity: 0.1;
|
|
943
|
+
}
|
|
944
|
+
&:active:before {
|
|
945
|
+
background-color: $primary;
|
|
946
|
+
opacity: 0.2;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
&--active {
|
|
950
|
+
font-weight: bold;
|
|
951
|
+
color: $primary;
|
|
952
|
+
|
|
953
|
+
&:before {
|
|
954
|
+
background-color: $primary;
|
|
955
|
+
opacity: 0.1;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
860
958
|
}
|
|
861
959
|
|
|
862
|
-
.
|
|
960
|
+
.w-pagination__results {
|
|
863
961
|
margin-left: $base-increment;
|
|
864
962
|
margin-right: $base-increment;
|
|
963
|
+
white-space: nowrap;
|
|
865
964
|
}
|
|
866
|
-
.w-select__selection {max-width: 60px;}
|
|
867
965
|
}
|
|
868
966
|
}
|
|
869
967
|
|
|
@@ -6,11 +6,11 @@ ul.w-tree(:class="classes")
|
|
|
6
6
|
:class="itemClasses(item)")
|
|
7
7
|
//- The keys `route` & `disabled` are always present in any currentDepthItems.
|
|
8
8
|
component.w-tree__item-label(
|
|
9
|
-
:is="
|
|
9
|
+
:is="getTreeItemComponent(item)"
|
|
10
10
|
v-bind="item.route && { [!$router || hasExternalLink(item) ? 'href' : 'to']: item.route }"
|
|
11
11
|
@click="!disabled && !item.disabled && onLabelClick(item, $event)"
|
|
12
12
|
@keydown="!disabled && !item.disabled && onLabelKeydown(item, $event)"
|
|
13
|
-
:tabindex="
|
|
13
|
+
:tabindex="getTreeItemTabindex(item)")
|
|
14
14
|
//- @click.stop to not follow link if item is a link.
|
|
15
15
|
w-button.w-tree__item-expand(
|
|
16
16
|
v-if="(item.children || item.branch) && ((expandOpenIcon && item.open) || expandIcon) && !(unexpandableEmpty && !item.children)"
|
|
@@ -22,7 +22,12 @@ ul.w-tree(:class="classes")
|
|
|
22
22
|
:disabled="disabled || item.disabled"
|
|
23
23
|
text
|
|
24
24
|
sm)
|
|
25
|
-
slot(
|
|
25
|
+
slot(
|
|
26
|
+
name="item"
|
|
27
|
+
:item="item.originalItem"
|
|
28
|
+
:depth="depth"
|
|
29
|
+
:path="item.path"
|
|
30
|
+
:open="item.open")
|
|
26
31
|
w-icon(v-if="itemIcon(item)" class="w-tree__item-icon" :color="item.originalItem[itemIconColorKey] || iconColor") {{ itemIcon(item) }}
|
|
27
32
|
span(v-html="item.label")
|
|
28
33
|
span.ml1(v-if="counts && (item.children || item.branch)").
|
|
@@ -30,13 +35,14 @@ ul.w-tree(:class="classes")
|
|
|
30
35
|
component(
|
|
31
36
|
:is="noTransition ? 'div' : 'w-transition-expand'"
|
|
32
37
|
:y="!noTransition || null"
|
|
33
|
-
@after-enter="$emit('open', { item: item.originalItem, open: item.open, depth })"
|
|
34
|
-
@after-leave="$emit('close', { item: item.originalItem, open: item.open, depth })")
|
|
38
|
+
@after-enter="$emit('open', { item: item.originalItem, open: item.open, depth, path: getTreeItemPathForOutput(item) })"
|
|
39
|
+
@after-leave="$emit('close', { item: item.originalItem, open: item.open, depth, path: getTreeItemPathForOutput(item) })")
|
|
35
40
|
w-tree(
|
|
36
41
|
v-if="item.children && item.open"
|
|
37
42
|
v-bind="$props"
|
|
38
43
|
:depth="depth + 1"
|
|
39
44
|
:data="item.originalItem.children"
|
|
45
|
+
:parent="item"
|
|
40
46
|
@before-open="$emit('before-open', $event)"
|
|
41
47
|
@open="$emit('open', $event)"
|
|
42
48
|
@before-close="$emit('before-close', $event)"
|
|
@@ -44,8 +50,8 @@ ul.w-tree(:class="classes")
|
|
|
44
50
|
@click="$emit('click', $event)"
|
|
45
51
|
@select="$emit('select', $event)"
|
|
46
52
|
@update:model-value="$emit('update:model-value', $event)")
|
|
47
|
-
template(#item="{ item, depth, open }")
|
|
48
|
-
slot(name="item" :item="item" :depth="depth" :open="open")
|
|
53
|
+
template(#item="{ item, depth, path, open }")
|
|
54
|
+
slot(name="item" :item="item" :depth="depth" :path="path" :open="open")
|
|
49
55
|
</template>
|
|
50
56
|
|
|
51
57
|
<script>
|
|
@@ -60,7 +66,8 @@ export default {
|
|
|
60
66
|
props: {
|
|
61
67
|
modelValue: { type: [Object, Array] },
|
|
62
68
|
data: { type: [Object, Array], required: true },
|
|
63
|
-
depth: { type: Number, default: 0 },
|
|
69
|
+
depth: { type: Number, default: 0 }, // To get the context from nested items.
|
|
70
|
+
parent: { type: Object, default: null }, // To get the context from nested items.
|
|
64
71
|
branchClass: { type: String },
|
|
65
72
|
leafClass: { type: String },
|
|
66
73
|
branchIcon: { type: String },
|
|
@@ -115,7 +122,7 @@ export default {
|
|
|
115
122
|
if (!Array.isArray(items)) items = [items]
|
|
116
123
|
|
|
117
124
|
items.forEach((item, i) => {
|
|
118
|
-
|
|
125
|
+
const itemWrapper = {
|
|
119
126
|
originalItem: item, // Store the original item to return it on event emits.
|
|
120
127
|
_uid: this.depth.toString() + (i + 1),
|
|
121
128
|
label: item[this.itemLabelKey],
|
|
@@ -124,11 +131,48 @@ export default {
|
|
|
124
131
|
route: item[this.itemRouteKey],
|
|
125
132
|
disabled: item[this.itemDisabledKey],
|
|
126
133
|
depth: this.depth,
|
|
127
|
-
open: !!(oldItems[i]?.open || this.expandAll || item[this.itemOpenKey])
|
|
128
|
-
|
|
134
|
+
open: !!(oldItems[i]?.open || this.expandAll || item[this.itemOpenKey]),
|
|
135
|
+
parent: this.parent || null,
|
|
136
|
+
path: [] // Ancestors path from root to leaf including self.
|
|
137
|
+
}
|
|
138
|
+
itemWrapper.path = this.getTreeItemPath(itemWrapper)
|
|
139
|
+
this.currentDepthItems.push(itemWrapper)
|
|
129
140
|
})
|
|
130
141
|
},
|
|
131
142
|
|
|
143
|
+
getTreeItemComponent (item) {
|
|
144
|
+
return !this.disabled && !item.disabled && item.route ? (!this.$router || this.hasExternalLink(item) ? 'a' : 'router-link') : 'div'
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
getTreeItemTabindex (item) {
|
|
148
|
+
return !this.disabled && !item.disabled && (item.children || item.branch || this.selectable) && !(this.unexpandableEmpty && !item.children) ? 0 : null
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the tree path of the given item.
|
|
153
|
+
* The full ancestors items are stored in the array and not only their `originalItem`s in case
|
|
154
|
+
* it is mutated before we return it to the user through slots and emitted events.
|
|
155
|
+
* Before it is returned to the user, this array is mapped to only give the `originalItem`s.
|
|
156
|
+
*
|
|
157
|
+
* @param {Object} item the tree item to get the ancestors path for.
|
|
158
|
+
* @return an array of item objects from the root to the leaf (including the item itself).
|
|
159
|
+
*/
|
|
160
|
+
getTreeItemPath (item) {
|
|
161
|
+
const ancestorsPath = [item]
|
|
162
|
+
|
|
163
|
+
let ancestor = item.parent
|
|
164
|
+
while (ancestor) {
|
|
165
|
+
ancestorsPath.push(ancestor)
|
|
166
|
+
ancestor = ancestor.parent
|
|
167
|
+
}
|
|
168
|
+
ancestorsPath.reverse()
|
|
169
|
+
return ancestorsPath
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
getTreeItemPathForOutput (item) {
|
|
173
|
+
return item.path.map(item => item.originalItem)
|
|
174
|
+
},
|
|
175
|
+
|
|
132
176
|
/**
|
|
133
177
|
* Expand/collapse the given tree item when possible (not disabled, has children).
|
|
134
178
|
*
|
|
@@ -139,7 +183,12 @@ export default {
|
|
|
139
183
|
if (typeof open === 'boolean') item.open = open
|
|
140
184
|
else item.open = !item.open
|
|
141
185
|
|
|
142
|
-
const emitParams = {
|
|
186
|
+
const emitParams = {
|
|
187
|
+
item: item.originalItem,
|
|
188
|
+
open: item.open,
|
|
189
|
+
depth: this.depth,
|
|
190
|
+
path: this.getTreeItemPathForOutput(item)
|
|
191
|
+
}
|
|
143
192
|
|
|
144
193
|
this.$emit(item.open ? 'before-open' : 'before-close', emitParams)
|
|
145
194
|
|
|
@@ -154,14 +203,24 @@ export default {
|
|
|
154
203
|
const route = item[this.itemRouteKey]
|
|
155
204
|
if (route && this.$router && !this.hasExternalLink(item)) e.preventDefault()
|
|
156
205
|
|
|
157
|
-
this.$emit('click', {
|
|
206
|
+
this.$emit('click', {
|
|
207
|
+
item: item.originalItem,
|
|
208
|
+
depth: this.depth,
|
|
209
|
+
path: this.getTreeItemPathForOutput(item),
|
|
210
|
+
e
|
|
211
|
+
})
|
|
158
212
|
if (item.children || (item.branch && !this.unexpandableEmpty)) this.expandDepth(item)
|
|
159
213
|
|
|
160
214
|
if (this.selectable) this.emitItemSelection(item, e)
|
|
161
215
|
},
|
|
162
216
|
|
|
163
217
|
emitItemSelection (item, e) {
|
|
164
|
-
const emitParams = {
|
|
218
|
+
const emitParams = {
|
|
219
|
+
item: item.originalItem,
|
|
220
|
+
depth: this.depth,
|
|
221
|
+
path: this.getTreeItemPathForOutput(item),
|
|
222
|
+
e
|
|
223
|
+
}
|
|
165
224
|
if (item.children || (item.branch && !this.unexpandableEmpty)) {
|
|
166
225
|
emitParams.open = item.open
|
|
167
226
|
}
|
|
@@ -299,8 +358,8 @@ $expand-icon-size: 20px;
|
|
|
299
358
|
right: - $base-increment - 2px;
|
|
300
359
|
border-radius: $border-radius;
|
|
301
360
|
}
|
|
302
|
-
&:hover:before {background-color:
|
|
303
|
-
&:focus:before {background-color:
|
|
361
|
+
&:hover:before {background-color: $primary;opacity: 0.1;}
|
|
362
|
+
&:focus:before {background-color: $primary;opacity: 0.2;}
|
|
304
363
|
}
|
|
305
364
|
&__item--leaf &__item-label:before {
|
|
306
365
|
left: - $base-increment;
|