vueless 0.0.496 → 0.0.498
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/package.json +1 -1
- package/types.ts +2 -0
- package/ui.data-table/UTable.vue +415 -458
- package/ui.data-table/UTableRow.vue +234 -253
- package/ui.data-table/{config.js → config.ts} +3 -10
- package/ui.data-table/storybook/Docs.mdx +2 -2
- package/ui.data-table/storybook/{stories.js → stories.ts} +65 -50
- package/ui.data-table/types.ts +142 -0
- package/ui.data-table/{useAttrs.js → useAttrs.ts} +32 -14
- package/ui.data-table/{utilTable.js → utilTable.ts} +24 -18
- package/ui.form-calendar/UCalendar.vue +1 -1
- package/ui.form-date-picker-range/UDatePickerRange.vue +1 -1
- package/web-types.json +36 -21
- /package/ui.data-table/{constants.js → constants.ts} +0 -0
package/ui.data-table/UTable.vue
CHANGED
|
@@ -1,269 +1,4 @@
|
|
|
1
|
-
<
|
|
2
|
-
<div :data-test="dataTest" v-bind="wrapperAttrs">
|
|
3
|
-
<div
|
|
4
|
-
v-show="isHeaderSticky || isShownActionsHeader"
|
|
5
|
-
ref="sticky-header-row"
|
|
6
|
-
:style="tableRowWidthStyle"
|
|
7
|
-
v-bind="stickyHeaderAttrs"
|
|
8
|
-
>
|
|
9
|
-
<template v-if="isShownActionsHeader">
|
|
10
|
-
<div v-bind="stickyHeaderCellAttrs">
|
|
11
|
-
<UCheckbox
|
|
12
|
-
v-if="selectable"
|
|
13
|
-
v-model="selectAll"
|
|
14
|
-
:partial="!isSelectedAllRows"
|
|
15
|
-
:data-test="`${dataTest}-select-all`"
|
|
16
|
-
v-bind="stickyHeaderActionsCheckboxAttrs"
|
|
17
|
-
/>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<div
|
|
21
|
-
v-if="selectedRows.length"
|
|
22
|
-
v-bind="stickyHeaderActionsCounterAttrs"
|
|
23
|
-
v-text="selectedRows.length"
|
|
24
|
-
/>
|
|
25
|
-
|
|
26
|
-
<!--
|
|
27
|
-
@slot Use it to add action buttons within the actions header, which appear when rows are selected.
|
|
28
|
-
@binding {array} selected-rows
|
|
29
|
-
-->
|
|
30
|
-
<slot name="header-actions" :selected-rows="selectedRows" />
|
|
31
|
-
</template>
|
|
32
|
-
|
|
33
|
-
<template v-else>
|
|
34
|
-
<div v-bind="stickyHeaderCellAttrs">
|
|
35
|
-
<UCheckbox
|
|
36
|
-
v-if="selectable"
|
|
37
|
-
v-model="selectAll"
|
|
38
|
-
:partial="!isSelectedAllRows"
|
|
39
|
-
:data-test="`${dataTest}-select-all`"
|
|
40
|
-
v-bind="stickyHeaderCheckboxAttrs"
|
|
41
|
-
/>
|
|
42
|
-
|
|
43
|
-
<div
|
|
44
|
-
v-if="selectedRows.length"
|
|
45
|
-
v-bind="stickyHeaderCounterAttrs"
|
|
46
|
-
v-text="selectedRows.length"
|
|
47
|
-
/>
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<div
|
|
51
|
-
v-for="(column, index) in columns"
|
|
52
|
-
:key="index"
|
|
53
|
-
v-bind="stickyHeaderCellAttrs"
|
|
54
|
-
:class="cx([stickyHeaderCellAttrs.class, column.thClass])"
|
|
55
|
-
>
|
|
56
|
-
<template v-if="hasSlotContent($slots[`header-${column.key}`])">
|
|
57
|
-
<!--
|
|
58
|
-
@slot Use it to customise needed header cell.
|
|
59
|
-
@binding {object} column
|
|
60
|
-
@binding {number} index
|
|
61
|
-
-->
|
|
62
|
-
<slot :name="`header-${column.key}`" :column="column" :index="index" />
|
|
63
|
-
</template>
|
|
64
|
-
|
|
65
|
-
<template v-else>
|
|
66
|
-
{{ column.label }}
|
|
67
|
-
</template>
|
|
68
|
-
|
|
69
|
-
<!--
|
|
70
|
-
@slot Use it to add something after the needed header cell.
|
|
71
|
-
@binding {object} column
|
|
72
|
-
@binding {number} index
|
|
73
|
-
-->
|
|
74
|
-
<slot :name="`header-${column.key}-after`" :column="column" :index="index" />
|
|
75
|
-
</div>
|
|
76
|
-
</template>
|
|
77
|
-
|
|
78
|
-
<ULoaderProgress v-if="isHeaderSticky" :loading="loading" v-bind="stickyHeaderLoaderAttrs" />
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<div ref="table-wrapper" v-bind="tableWrapperAttrs">
|
|
82
|
-
<table v-bind="tableAttrs">
|
|
83
|
-
<thead v-bind="headerAttrs" :style="tableRowWidthStyle">
|
|
84
|
-
<tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs">
|
|
85
|
-
<td
|
|
86
|
-
v-if="hasSlotContent($slots['before-header'])"
|
|
87
|
-
:colspan="colsCount"
|
|
88
|
-
v-bind="headerCellBaseAttrs"
|
|
89
|
-
>
|
|
90
|
-
<!--
|
|
91
|
-
@slot Use it to add something before header row.
|
|
92
|
-
@binding {number} cols-count
|
|
93
|
-
-->
|
|
94
|
-
<slot name="before-header" :cols-count="colsCount" />
|
|
95
|
-
</td>
|
|
96
|
-
</tr>
|
|
97
|
-
|
|
98
|
-
<tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs"></tr>
|
|
99
|
-
|
|
100
|
-
<tr ref="header-row" v-bind="headerRowAttrs">
|
|
101
|
-
<th v-if="selectable" v-bind="headerCellCheckboxAttrs">
|
|
102
|
-
<UCheckbox
|
|
103
|
-
v-model="selectAll"
|
|
104
|
-
:partial="!isSelectedAllRows"
|
|
105
|
-
:data-test="`${dataTest}-select-all`"
|
|
106
|
-
v-bind="headerCheckboxAttrs"
|
|
107
|
-
/>
|
|
108
|
-
|
|
109
|
-
<div
|
|
110
|
-
v-if="selectedRows.length"
|
|
111
|
-
v-bind="headerCounterAttrs"
|
|
112
|
-
v-text="selectedRows.length"
|
|
113
|
-
/>
|
|
114
|
-
</th>
|
|
115
|
-
|
|
116
|
-
<th
|
|
117
|
-
v-for="(column, index) in visibleColumns"
|
|
118
|
-
:key="index"
|
|
119
|
-
v-bind="headerCellBaseAttrs"
|
|
120
|
-
:class="cx([headerCellBaseAttrs.class, column.thClass])"
|
|
121
|
-
>
|
|
122
|
-
<!--
|
|
123
|
-
@slot Use it to customise needed header cell.
|
|
124
|
-
@binding {object} column
|
|
125
|
-
@binding {number} index
|
|
126
|
-
-->
|
|
127
|
-
<slot
|
|
128
|
-
v-if="hasSlotContent($slots[`header-${column.key}`])"
|
|
129
|
-
:name="`header-${column.key}`"
|
|
130
|
-
:column="column"
|
|
131
|
-
:index="index"
|
|
132
|
-
/>
|
|
133
|
-
|
|
134
|
-
<template v-else>
|
|
135
|
-
{{ column.label }}
|
|
136
|
-
</template>
|
|
137
|
-
|
|
138
|
-
<!--
|
|
139
|
-
@slot Use it to add something after the needed header cell.
|
|
140
|
-
@binding {object} column
|
|
141
|
-
@binding {number} index
|
|
142
|
-
-->
|
|
143
|
-
<slot :name="`header-${column.key}-after`" :column="column" :index="index" />
|
|
144
|
-
</th>
|
|
145
|
-
</tr>
|
|
146
|
-
|
|
147
|
-
<ULoaderProgress :loading="loading" v-bind="headerLoaderAttrs" />
|
|
148
|
-
</thead>
|
|
149
|
-
|
|
150
|
-
<tbody v-if="tableRows.length" v-bind="bodyAttrs">
|
|
151
|
-
<template v-for="(row, rowIndex) in sortedRows" :key="row.id">
|
|
152
|
-
<tr
|
|
153
|
-
v-if="rowIndex === firstRow && hasSlotContent($slots['before-first-row'])"
|
|
154
|
-
v-bind="bodyRowBeforeAttrs"
|
|
155
|
-
>
|
|
156
|
-
<td :colspan="colsCount" v-bind="bodyRowBeforeCellAttrs">
|
|
157
|
-
<!-- @slot Use it to add something before first row. -->
|
|
158
|
-
<slot name="before-first-row" />
|
|
159
|
-
</td>
|
|
160
|
-
</tr>
|
|
161
|
-
|
|
162
|
-
<tr v-if="isShownDateDivider(rowIndex) && row.rowDate" v-bind="bodyRowDateDividerAttrs">
|
|
163
|
-
<td v-bind="bodyCellDateDividerAttrs" :colspan="colsCount">
|
|
164
|
-
<UDivider
|
|
165
|
-
size="xs"
|
|
166
|
-
:label="getDateDividerLabel(row.rowDate)"
|
|
167
|
-
v-bind="bodyDateDividerAttrs"
|
|
168
|
-
/>
|
|
169
|
-
</td>
|
|
170
|
-
</tr>
|
|
171
|
-
|
|
172
|
-
<UTableRow
|
|
173
|
-
v-model:selected-rows="selectedRows"
|
|
174
|
-
:selectable="selectable"
|
|
175
|
-
:data-test="`${dataTest}-row`"
|
|
176
|
-
:row="row"
|
|
177
|
-
:columns="columns"
|
|
178
|
-
:config="config"
|
|
179
|
-
:attrs="keysAttrs"
|
|
180
|
-
:empty-cell-label="emptyCellLabel"
|
|
181
|
-
@click="onClickRow"
|
|
182
|
-
@click-cell="onClickCell"
|
|
183
|
-
@toggle-row-visibility="onToggleRowVisibility"
|
|
184
|
-
>
|
|
185
|
-
<template
|
|
186
|
-
v-for="(value, key, index) in getFilteredRow(row, columns)"
|
|
187
|
-
:key="index"
|
|
188
|
-
#[`cell-${key}`]="slotValues"
|
|
189
|
-
>
|
|
190
|
-
<!--
|
|
191
|
-
@slot Use it to customise needed table cell.
|
|
192
|
-
@binding {string} value
|
|
193
|
-
@binding {object} row
|
|
194
|
-
@binding {number} index
|
|
195
|
-
-->
|
|
196
|
-
<slot
|
|
197
|
-
:name="`cell-${key}`"
|
|
198
|
-
:value="slotValues.value"
|
|
199
|
-
:row="slotValues.row"
|
|
200
|
-
:index="index"
|
|
201
|
-
/>
|
|
202
|
-
</template>
|
|
203
|
-
<template #nested-content>
|
|
204
|
-
<!--
|
|
205
|
-
@slot Use it to add nested content inside a row.
|
|
206
|
-
@binding {object} row
|
|
207
|
-
-->
|
|
208
|
-
<slot v-if="row" name="nested-content" :row="row" />
|
|
209
|
-
</template>
|
|
210
|
-
</UTableRow>
|
|
211
|
-
|
|
212
|
-
<tr
|
|
213
|
-
v-if="rowIndex === lastRow && hasSlotContent($slots['after-last-row'])"
|
|
214
|
-
v-bind="bodyRowAfterAttrs"
|
|
215
|
-
>
|
|
216
|
-
<td :colspan="colsCount" v-bind="bodyRowAfterCellAttrs">
|
|
217
|
-
<!-- @slot Use it to add something after last row. -->
|
|
218
|
-
<slot name="after-last-row" />
|
|
219
|
-
</td>
|
|
220
|
-
</tr>
|
|
221
|
-
</template>
|
|
222
|
-
</tbody>
|
|
223
|
-
|
|
224
|
-
<tbody v-else>
|
|
225
|
-
<tr>
|
|
226
|
-
<td :colspan="colsCount">
|
|
227
|
-
<!-- @slot Use it to add custom empty state. -->
|
|
228
|
-
<slot name="empty-state">
|
|
229
|
-
<UEmpty
|
|
230
|
-
size="md"
|
|
231
|
-
:description="currentLocale.noData"
|
|
232
|
-
:data-test="`${dataTest}-empty`"
|
|
233
|
-
v-bind="bodyEmptyStateAttrs"
|
|
234
|
-
/>
|
|
235
|
-
</slot>
|
|
236
|
-
</td>
|
|
237
|
-
</tr>
|
|
238
|
-
</tbody>
|
|
239
|
-
|
|
240
|
-
<tfoot v-if="hasSlotContent($slots['footer'])" v-bind="footerAttrs">
|
|
241
|
-
<tr ref="footer-row" v-bind="footerRowAttrs">
|
|
242
|
-
<td v-if="selectable" />
|
|
243
|
-
|
|
244
|
-
<!--
|
|
245
|
-
@slot Use it to add something into the table footer.
|
|
246
|
-
@binding {number} cols-count
|
|
247
|
-
-->
|
|
248
|
-
<slot name="footer" :cols-count="colsCount" />
|
|
249
|
-
</tr>
|
|
250
|
-
|
|
251
|
-
<tr ref="sticky-footer-row" :style="tableRowWidthStyle" v-bind="stickyFooterRowAttrs">
|
|
252
|
-
<td v-if="selectable" />
|
|
253
|
-
|
|
254
|
-
<!--
|
|
255
|
-
@slot Use it to add something into the table footer.
|
|
256
|
-
@binding {number} cols-count
|
|
257
|
-
-->
|
|
258
|
-
<slot name="footer" :cols-count="colsCount" />
|
|
259
|
-
</tr>
|
|
260
|
-
</tfoot>
|
|
261
|
-
</table>
|
|
262
|
-
</div>
|
|
263
|
-
</div>
|
|
264
|
-
</template>
|
|
265
|
-
|
|
266
|
-
<script setup>
|
|
1
|
+
<script setup lang="ts">
|
|
267
2
|
import {
|
|
268
3
|
ref,
|
|
269
4
|
computed,
|
|
@@ -286,112 +21,37 @@ import UTableRow from "./UTableRow.vue";
|
|
|
286
21
|
|
|
287
22
|
import { getDefault, cx } from "../utils/ui.ts";
|
|
288
23
|
|
|
289
|
-
import defaultConfig from "./config.
|
|
24
|
+
import defaultConfig from "./config.ts";
|
|
290
25
|
import {
|
|
291
26
|
normalizeColumns,
|
|
292
|
-
|
|
27
|
+
mapRowColumns,
|
|
293
28
|
syncRowCheck,
|
|
294
29
|
toggleRowVisibility,
|
|
295
30
|
switchRowCheck,
|
|
296
31
|
getFlatRows,
|
|
297
32
|
addRowId,
|
|
298
|
-
} from "./utilTable.
|
|
33
|
+
} from "./utilTable.ts";
|
|
299
34
|
|
|
300
35
|
import { PX_IN_REM } from "../constants.js";
|
|
301
|
-
import { UTable } from "./constants.
|
|
302
|
-
import useAttrs from "./useAttrs.
|
|
36
|
+
import { UTable } from "./constants.ts";
|
|
37
|
+
import useAttrs from "./useAttrs.ts";
|
|
303
38
|
import { useLocale } from "../composables/useLocale.ts";
|
|
304
39
|
|
|
305
|
-
|
|
40
|
+
import type { Cell, Row, RowId, UTableProps, UTableRowAttrs } from "./types.ts";
|
|
41
|
+
import type { Ref, RendererElement, ComputedRef } from "vue";
|
|
306
42
|
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Table columns (headers).
|
|
310
|
-
*/
|
|
311
|
-
columns: {
|
|
312
|
-
type: Array,
|
|
313
|
-
required: true,
|
|
314
|
-
},
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Table rows data.
|
|
318
|
-
*/
|
|
319
|
-
rows: {
|
|
320
|
-
type: Array,
|
|
321
|
-
required: true,
|
|
322
|
-
},
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Label to display for empty cell values.
|
|
326
|
-
*/
|
|
327
|
-
emptyCellLabel: {
|
|
328
|
-
type: String,
|
|
329
|
-
default: getDefault(defaultConfig, UTable).emptyCellLabel,
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Show date divider line between dates.
|
|
334
|
-
*/
|
|
335
|
-
dateDivider: {
|
|
336
|
-
type: [Boolean, Array],
|
|
337
|
-
default: getDefault(defaultConfig, UTable).dateDivider,
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Allow rows selecting.
|
|
342
|
-
*/
|
|
343
|
-
selectable: {
|
|
344
|
-
type: Boolean,
|
|
345
|
-
default: getDefault(defaultConfig, UTable).selectable,
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Makes the table compact (fewer spacings).
|
|
350
|
-
*/
|
|
351
|
-
compact: {
|
|
352
|
-
type: Boolean,
|
|
353
|
-
default: getDefault(defaultConfig, UTable).compact,
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Set header sticky.
|
|
358
|
-
*/
|
|
359
|
-
stickyHeader: {
|
|
360
|
-
type: Boolean,
|
|
361
|
-
default: getDefault(defaultConfig, UTable).stickyHeader,
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Set footer sticky.
|
|
366
|
-
*/
|
|
367
|
-
stickyFooter: {
|
|
368
|
-
type: Boolean,
|
|
369
|
-
default: getDefault(defaultConfig, UTable).stickyFooter,
|
|
370
|
-
},
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Set table loader state.
|
|
374
|
-
*/
|
|
375
|
-
loading: {
|
|
376
|
-
type: Boolean,
|
|
377
|
-
default: getDefault(defaultConfig, UTable).loading,
|
|
378
|
-
},
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Component config object.
|
|
382
|
-
*/
|
|
383
|
-
config: {
|
|
384
|
-
type: Object,
|
|
385
|
-
default: () => ({}),
|
|
386
|
-
},
|
|
43
|
+
defineOptions({ inheritAttrs: false });
|
|
387
44
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
45
|
+
const props = withDefaults(defineProps<UTableProps>(), {
|
|
46
|
+
emptyCellLabel: getDefault<UTableProps>(defaultConfig, UTable).emptyCellLabel,
|
|
47
|
+
dateDivider: () => getDefault<UTableProps>(defaultConfig, UTable).dateDivider || false,
|
|
48
|
+
selectable: getDefault<UTableProps>(defaultConfig, UTable).selectable,
|
|
49
|
+
compact: getDefault<UTableProps>(defaultConfig, UTable).compact,
|
|
50
|
+
stickyHeader: getDefault<UTableProps>(defaultConfig, UTable).stickyHeader,
|
|
51
|
+
stickyFooter: getDefault<UTableProps>(defaultConfig, UTable).stickyFooter,
|
|
52
|
+
loading: getDefault<UTableProps>(defaultConfig, UTable).loading,
|
|
53
|
+
dataTest: "",
|
|
54
|
+
config: () => ({}),
|
|
395
55
|
});
|
|
396
56
|
|
|
397
57
|
const emit = defineEmits([
|
|
@@ -419,29 +79,31 @@ const { tm } = useLocale();
|
|
|
419
79
|
|
|
420
80
|
const selectAll = ref(false);
|
|
421
81
|
const canSelectAll = ref(true);
|
|
422
|
-
const selectedRows = ref([]);
|
|
423
|
-
const tableRows = ref([]);
|
|
82
|
+
const selectedRows: Ref<RowId[]> = ref([]);
|
|
83
|
+
const tableRows: Ref<Row[]> = ref([]);
|
|
424
84
|
const firstRow = ref(0);
|
|
425
85
|
const tableWidth = ref(0);
|
|
426
86
|
const tableHeight = ref(0);
|
|
427
87
|
const pagePositionY = ref(0);
|
|
428
88
|
|
|
429
|
-
const headerRowRef = useTemplateRef("header-row");
|
|
430
|
-
const footerRowRef = useTemplateRef("footer-row");
|
|
431
|
-
const tableWrapperRef = useTemplateRef("table-wrapper");
|
|
432
|
-
const stickyFooterRowRef = useTemplateRef("sticky-footer-row");
|
|
433
|
-
const stickyHeaderRowRef = useTemplateRef("sticky-header-row");
|
|
89
|
+
const headerRowRef = useTemplateRef<HTMLTableRowElement>("header-row");
|
|
90
|
+
const footerRowRef = useTemplateRef<HTMLTableRowElement>("footer-row");
|
|
91
|
+
const tableWrapperRef = useTemplateRef<HTMLDivElement>("table-wrapper");
|
|
92
|
+
const stickyFooterRowRef = useTemplateRef<HTMLTableRowElement>("sticky-footer-row");
|
|
93
|
+
const stickyHeaderRowRef = useTemplateRef<HTMLDivElement>("sticky-header-row");
|
|
434
94
|
|
|
435
95
|
const i18nGlobal = tm(UTable);
|
|
436
96
|
const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
|
|
437
97
|
|
|
438
|
-
const sortedRows = computed(() => {
|
|
439
|
-
const headerKeys = props.columns.map((column) =>
|
|
98
|
+
const sortedRows: ComputedRef<Row[]> = computed(() => {
|
|
99
|
+
const headerKeys = props.columns.map((column) =>
|
|
100
|
+
typeof column === "object" ? column.key : column,
|
|
101
|
+
);
|
|
440
102
|
|
|
441
103
|
return tableRows.value.map((row) => {
|
|
442
104
|
const rowEntries = Object.entries(row);
|
|
443
105
|
|
|
444
|
-
const sortedEntries = new Array(rowEntries.length);
|
|
106
|
+
const sortedEntries: typeof rowEntries = new Array(rowEntries.length);
|
|
445
107
|
|
|
446
108
|
rowEntries.forEach((entry) => {
|
|
447
109
|
const [key] = entry;
|
|
@@ -458,7 +120,7 @@ const sortedRows = computed(() => {
|
|
|
458
120
|
|
|
459
121
|
const sortedRow = Object.fromEntries(sortedEntries.filter((value) => value));
|
|
460
122
|
|
|
461
|
-
return sortedRow;
|
|
123
|
+
return sortedRow as Row;
|
|
462
124
|
});
|
|
463
125
|
});
|
|
464
126
|
|
|
@@ -477,7 +139,7 @@ const visibleColumns = computed(() => {
|
|
|
477
139
|
});
|
|
478
140
|
|
|
479
141
|
const colsCount = computed(() => {
|
|
480
|
-
return
|
|
142
|
+
return normalizedColumns.value.length + 1;
|
|
481
143
|
});
|
|
482
144
|
|
|
483
145
|
const lastRow = computed(() => {
|
|
@@ -490,14 +152,15 @@ const isShownActionsHeader = computed(
|
|
|
490
152
|
|
|
491
153
|
const isHeaderSticky = computed(() => {
|
|
492
154
|
const positionForFixHeader =
|
|
493
|
-
headerRowRef.value?.getBoundingClientRect()
|
|
155
|
+
Number(headerRowRef.value?.getBoundingClientRect()?.top) + window.scrollY || 0;
|
|
494
156
|
|
|
495
157
|
return positionForFixHeader <= pagePositionY.value && props.stickyHeader;
|
|
496
158
|
});
|
|
497
159
|
|
|
498
160
|
const isShownFooterPosition = computed(() => {
|
|
499
161
|
const pageBottom = pagePositionY.value + window.innerHeight;
|
|
500
|
-
const positionForFixFooter =
|
|
162
|
+
const positionForFixFooter =
|
|
163
|
+
Number(footerRowRef.value?.getBoundingClientRect()?.bottom) + window.scrollY;
|
|
501
164
|
|
|
502
165
|
return pageBottom >= positionForFixFooter;
|
|
503
166
|
});
|
|
@@ -509,9 +172,16 @@ const isCheckedMoreOneTableItems = computed(() => {
|
|
|
509
172
|
const tableRowWidthStyle = computed(() => ({ width: `${tableWidth.value / PX_IN_REM}rem` }));
|
|
510
173
|
|
|
511
174
|
const hasSlotContentBeforeFirstRow = computed(() => {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
175
|
+
if (
|
|
176
|
+
hasSlotContent(slots["before-first-row"]) &&
|
|
177
|
+
typeof slots["before-first-row"] === "function"
|
|
178
|
+
) {
|
|
179
|
+
return slots["before-first-row"]()?.some((item) =>
|
|
180
|
+
Boolean((item.type as RendererElement)?.render),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return false;
|
|
515
185
|
});
|
|
516
186
|
|
|
517
187
|
const isSelectedAllRows = computed(() => {
|
|
@@ -522,7 +192,6 @@ const isSelectedAllRows = computed(() => {
|
|
|
522
192
|
|
|
523
193
|
const {
|
|
524
194
|
config,
|
|
525
|
-
keysAttrs,
|
|
526
195
|
wrapperAttrs,
|
|
527
196
|
stickyHeaderCellAttrs,
|
|
528
197
|
stickyHeaderAttrs,
|
|
@@ -553,6 +222,16 @@ const {
|
|
|
553
222
|
stickyFooterRowAttrs,
|
|
554
223
|
hasSlotContent,
|
|
555
224
|
headerAttrs,
|
|
225
|
+
bodyCellContentAttrs,
|
|
226
|
+
bodyCellCheckboxAttrs,
|
|
227
|
+
bodyCheckboxAttrs,
|
|
228
|
+
bodyCellNestedAttrs,
|
|
229
|
+
bodyCellNestedExpandIconAttrs,
|
|
230
|
+
bodyCellNestedCollapseIconAttrs,
|
|
231
|
+
bodyCellBaseAttrs,
|
|
232
|
+
bodyCellNestedExpandIconWrapperAttrs,
|
|
233
|
+
bodyRowCheckedAttrs,
|
|
234
|
+
bodyRowAttrs,
|
|
556
235
|
} = useAttrs(props, {
|
|
557
236
|
tableRows,
|
|
558
237
|
isShownActionsHeader,
|
|
@@ -560,6 +239,19 @@ const {
|
|
|
560
239
|
isFooterSticky,
|
|
561
240
|
});
|
|
562
241
|
|
|
242
|
+
const tableRowAttrs = computed(() => ({
|
|
243
|
+
bodyCellContentAttrs,
|
|
244
|
+
bodyCellCheckboxAttrs,
|
|
245
|
+
bodyCheckboxAttrs,
|
|
246
|
+
bodyCellNestedAttrs,
|
|
247
|
+
bodyCellNestedExpandIconAttrs,
|
|
248
|
+
bodyCellNestedCollapseIconAttrs,
|
|
249
|
+
bodyCellBaseAttrs,
|
|
250
|
+
bodyCellNestedExpandIconWrapperAttrs,
|
|
251
|
+
bodyRowCheckedAttrs,
|
|
252
|
+
bodyRowAttrs,
|
|
253
|
+
}));
|
|
254
|
+
|
|
563
255
|
watch(selectAll, onChangeSelectAll, { deep: true });
|
|
564
256
|
watch(selectedRows, onChangeSelectedRows, { deep: true });
|
|
565
257
|
watch(tableRows, () => emit("update:rows", toValue(tableRows)), { deep: true });
|
|
@@ -588,8 +280,8 @@ onMounted(() => {
|
|
|
588
280
|
});
|
|
589
281
|
|
|
590
282
|
onUpdated(() => {
|
|
591
|
-
tableHeight.value = tableWrapperRef.value?.offsetHeight;
|
|
592
|
-
tableWidth.value = tableWrapperRef.value?.offsetWidth;
|
|
283
|
+
tableHeight.value = Number(tableWrapperRef.value?.offsetHeight);
|
|
284
|
+
tableWidth.value = Number(tableWrapperRef.value?.offsetWidth);
|
|
593
285
|
});
|
|
594
286
|
|
|
595
287
|
onBeforeUnmount(() => {
|
|
@@ -605,31 +297,31 @@ function onWindowResize() {
|
|
|
605
297
|
setFooterCellWidth();
|
|
606
298
|
}
|
|
607
299
|
|
|
608
|
-
function getDateDividerLabel(rowDate) {
|
|
300
|
+
function getDateDividerLabel(rowDate: string | Date) {
|
|
609
301
|
return Array.isArray(props.dateDivider)
|
|
610
|
-
? props.dateDivider.find((dateItem) => dateItem.date === rowDate)?.label || rowDate
|
|
611
|
-
: rowDate;
|
|
302
|
+
? props.dateDivider.find((dateItem) => dateItem.date === rowDate)?.label || String(rowDate)
|
|
303
|
+
: String(rowDate);
|
|
612
304
|
}
|
|
613
305
|
|
|
614
|
-
function setFooterCellWidth(
|
|
306
|
+
function setFooterCellWidth(zero?: null) {
|
|
615
307
|
const ZERO_WIDTH = 0;
|
|
616
308
|
|
|
617
|
-
if (!props.stickyFooter) return;
|
|
309
|
+
if (!props.stickyFooter || !footerRowRef.value || !stickyFooterRowRef.value) return;
|
|
618
310
|
|
|
619
|
-
const mainFooterItems = [...footerRowRef.value.children];
|
|
620
|
-
const stickyFooterItems = [...stickyFooterRowRef.value.children];
|
|
311
|
+
const mainFooterItems = [...footerRowRef.value.children] as HTMLElement[];
|
|
312
|
+
const stickyFooterItems = [...stickyFooterRowRef.value.children] as HTMLElement[];
|
|
621
313
|
|
|
622
314
|
stickyFooterItems.forEach((item, index) => {
|
|
623
315
|
item.style.width =
|
|
624
|
-
|
|
316
|
+
zero === null ? `${ZERO_WIDTH}rem` : `${mainFooterItems[index].offsetWidth / PX_IN_REM}rem`;
|
|
625
317
|
});
|
|
626
318
|
}
|
|
627
319
|
|
|
628
320
|
function setHeaderCellWidth() {
|
|
629
|
-
if (selectedRows.value.length) return;
|
|
321
|
+
if (selectedRows.value.length || !footerRowRef.value || !stickyFooterRowRef.value) return;
|
|
630
322
|
|
|
631
|
-
const mainHeaderItems = [...(headerRowRef.value?.children || [])];
|
|
632
|
-
const stickyHeaderItems = [...(stickyHeaderRowRef.value?.children || [])];
|
|
323
|
+
const mainHeaderItems = [...(headerRowRef.value?.children || [])] as HTMLElement[];
|
|
324
|
+
const stickyHeaderItems = [...(stickyHeaderRowRef.value?.children || [])] as HTMLDivElement[];
|
|
633
325
|
|
|
634
326
|
stickyHeaderItems.forEach((item, index) => {
|
|
635
327
|
item.style.width = `${mainHeaderItems[index]?.offsetWidth / PX_IN_REM}rem`;
|
|
@@ -640,91 +332,356 @@ function onScroll() {
|
|
|
640
332
|
pagePositionY.value = window.scrollY;
|
|
641
333
|
}
|
|
642
334
|
|
|
643
|
-
function synchronizeTableItemsWithProps() {
|
|
644
|
-
if (!props.rows.length || props.rows.length !== tableRows.value.length) {
|
|
645
|
-
selectedRows.value = [];
|
|
646
|
-
}
|
|
335
|
+
function synchronizeTableItemsWithProps() {
|
|
336
|
+
if (!props.rows.length || props.rows.length !== tableRows.value.length) {
|
|
337
|
+
selectedRows.value = [];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
tableRows.value = props.rows;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function updateSelectedRows() {
|
|
344
|
+
selectedRows.value = tableRows.value.filter((row) => row.isChecked).map((row) => row.id);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function onKeyupEsc(event: KeyboardEvent) {
|
|
348
|
+
if (event.code === "Escape" && props.selectable) {
|
|
349
|
+
selectedRows.value = [];
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function isShownDateDivider(rowIndex: number) {
|
|
354
|
+
const prevIndex = rowIndex ? rowIndex - 1 : rowIndex;
|
|
355
|
+
const nextIndex = rowIndex ? rowIndex + 1 : rowIndex;
|
|
356
|
+
const prevItem = tableRows.value[prevIndex];
|
|
357
|
+
const nextItem = tableRows.value[nextIndex];
|
|
358
|
+
const currentItem = tableRows.value[rowIndex];
|
|
359
|
+
|
|
360
|
+
if (rowIndex === 0) {
|
|
361
|
+
return hasSlotContentBeforeFirstRow.value;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const isPrevSameDate = prevItem?.rowDate === currentItem?.rowDate;
|
|
365
|
+
const isNextSameDate = nextItem?.rowDate === currentItem?.rowDate;
|
|
366
|
+
|
|
367
|
+
return isPrevSameDate && !isNextSameDate && props.dateDivider;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function onClickRow(row: Row) {
|
|
371
|
+
emit("clickRow", row);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function onClickCell(cell: Cell, row: Row) {
|
|
375
|
+
emit("clickCell", cell, row);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function onChangeSelectAll(selectAll: boolean) {
|
|
379
|
+
if (selectAll && canSelectAll.value) {
|
|
380
|
+
selectedRows.value = getFlatRows(tableRows.value).map((row) => row.id);
|
|
381
|
+
|
|
382
|
+
tableRows.value.forEach((row) => switchRowCheck(row, true));
|
|
383
|
+
} else if (!selectAll) {
|
|
384
|
+
selectedRows.value = [];
|
|
385
|
+
|
|
386
|
+
tableRows.value.forEach((row) => switchRowCheck(row, false));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
canSelectAll.value = true;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function onChangeSelectedRows(selectedRows: RowId[]) {
|
|
393
|
+
if (selectedRows.length) {
|
|
394
|
+
canSelectAll.value = false;
|
|
395
|
+
|
|
396
|
+
isCheckedMoreOneTableItems.value && setFooterCellWidth();
|
|
397
|
+
} else {
|
|
398
|
+
nextTick(setHeaderCellWidth);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
selectAll.value = !!selectedRows.length;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function clearSelectedItems() {
|
|
405
|
+
selectedRows.value = [];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function onToggleRowVisibility(rowId: string | number) {
|
|
409
|
+
// TODO: Use map instead of forEach to get rid of implicit array mutation.
|
|
410
|
+
tableRows.value.forEach((row) => toggleRowVisibility(row, rowId));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
defineExpose({
|
|
414
|
+
/**
|
|
415
|
+
* Allows to clear selected rows.
|
|
416
|
+
* @property {Function}
|
|
417
|
+
*/
|
|
418
|
+
clearSelectedItems,
|
|
419
|
+
});
|
|
420
|
+
</script>
|
|
421
|
+
|
|
422
|
+
<template>
|
|
423
|
+
<div :data-test="dataTest" v-bind="wrapperAttrs">
|
|
424
|
+
<div
|
|
425
|
+
v-show="isHeaderSticky || isShownActionsHeader"
|
|
426
|
+
ref="sticky-header-row"
|
|
427
|
+
:style="tableRowWidthStyle"
|
|
428
|
+
v-bind="stickyHeaderAttrs"
|
|
429
|
+
>
|
|
430
|
+
<template v-if="isShownActionsHeader">
|
|
431
|
+
<div v-bind="stickyHeaderCellAttrs">
|
|
432
|
+
<UCheckbox
|
|
433
|
+
v-if="selectable"
|
|
434
|
+
v-model="selectAll"
|
|
435
|
+
:partial="!isSelectedAllRows"
|
|
436
|
+
:data-test="`${dataTest}-select-all`"
|
|
437
|
+
v-bind="stickyHeaderActionsCheckboxAttrs"
|
|
438
|
+
/>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
<div
|
|
442
|
+
v-if="selectedRows.length"
|
|
443
|
+
v-bind="stickyHeaderActionsCounterAttrs"
|
|
444
|
+
v-text="selectedRows.length"
|
|
445
|
+
/>
|
|
446
|
+
|
|
447
|
+
<!--
|
|
448
|
+
@slot Use it to add action buttons within the actions header, which appear when rows are selected.
|
|
449
|
+
@binding {array} selected-rows
|
|
450
|
+
-->
|
|
451
|
+
<slot name="header-actions" :selected-rows="selectedRows" />
|
|
452
|
+
</template>
|
|
453
|
+
|
|
454
|
+
<template v-else>
|
|
455
|
+
<div v-bind="stickyHeaderCellAttrs">
|
|
456
|
+
<UCheckbox
|
|
457
|
+
v-if="selectable"
|
|
458
|
+
v-model="selectAll"
|
|
459
|
+
:partial="!isSelectedAllRows"
|
|
460
|
+
:data-test="`${dataTest}-select-all`"
|
|
461
|
+
v-bind="stickyHeaderCheckboxAttrs"
|
|
462
|
+
/>
|
|
463
|
+
|
|
464
|
+
<div
|
|
465
|
+
v-if="selectedRows.length"
|
|
466
|
+
v-bind="stickyHeaderCounterAttrs"
|
|
467
|
+
v-text="selectedRows.length"
|
|
468
|
+
/>
|
|
469
|
+
</div>
|
|
647
470
|
|
|
648
|
-
|
|
649
|
-
|
|
471
|
+
<!-- TODO: Remove any when key attrs are typed-->
|
|
472
|
+
<div
|
|
473
|
+
v-for="(column, index) in normalizedColumns"
|
|
474
|
+
:key="index"
|
|
475
|
+
v-bind="stickyHeaderCellAttrs"
|
|
476
|
+
:class="cx([(stickyHeaderCellAttrs as any).class, column.thClass])"
|
|
477
|
+
>
|
|
478
|
+
<template v-if="hasSlotContent($slots[`header-${column.key}`])">
|
|
479
|
+
<!--
|
|
480
|
+
@slot Use it to customise needed header cell.
|
|
481
|
+
@binding {object} column
|
|
482
|
+
@binding {number} index
|
|
483
|
+
-->
|
|
484
|
+
<slot :name="`header-${column.key}`" :column="column" :index="index" />
|
|
485
|
+
</template>
|
|
650
486
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
487
|
+
<template v-else>
|
|
488
|
+
{{ column.label }}
|
|
489
|
+
</template>
|
|
654
490
|
|
|
655
|
-
|
|
656
|
-
|
|
491
|
+
<!--
|
|
492
|
+
@slot Use it to add something after the needed header cell.
|
|
493
|
+
@binding {object} column
|
|
494
|
+
@binding {number} index
|
|
495
|
+
-->
|
|
496
|
+
<slot :name="`header-${column.key}-after`" :column="column" :index="index" />
|
|
497
|
+
</div>
|
|
498
|
+
</template>
|
|
657
499
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
}
|
|
500
|
+
<ULoaderProgress v-if="isHeaderSticky" :loading="loading" v-bind="stickyHeaderLoaderAttrs" />
|
|
501
|
+
</div>
|
|
662
502
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
503
|
+
<div ref="table-wrapper" v-bind="tableWrapperAttrs">
|
|
504
|
+
<table v-bind="tableAttrs">
|
|
505
|
+
<thead v-bind="headerAttrs" :style="tableRowWidthStyle">
|
|
506
|
+
<tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs">
|
|
507
|
+
<td
|
|
508
|
+
v-if="hasSlotContent($slots['before-header'])"
|
|
509
|
+
:colspan="colsCount"
|
|
510
|
+
v-bind="headerCellBaseAttrs"
|
|
511
|
+
>
|
|
512
|
+
<!--
|
|
513
|
+
@slot Use it to add something before header row.
|
|
514
|
+
@binding {number} cols-count
|
|
515
|
+
-->
|
|
516
|
+
<slot name="before-header" :cols-count="colsCount" />
|
|
517
|
+
</td>
|
|
518
|
+
</tr>
|
|
669
519
|
|
|
670
|
-
|
|
671
|
-
return hasSlotContentBeforeFirstRow.value;
|
|
672
|
-
}
|
|
520
|
+
<tr v-if="hasSlotContent($slots['before-header'])" v-bind="headerRowAttrs"></tr>
|
|
673
521
|
|
|
674
|
-
|
|
675
|
-
|
|
522
|
+
<tr ref="header-row" v-bind="headerRowAttrs">
|
|
523
|
+
<th v-if="selectable" v-bind="headerCellCheckboxAttrs">
|
|
524
|
+
<UCheckbox
|
|
525
|
+
v-model="selectAll"
|
|
526
|
+
:partial="!isSelectedAllRows"
|
|
527
|
+
:data-test="`${dataTest}-select-all`"
|
|
528
|
+
v-bind="headerCheckboxAttrs"
|
|
529
|
+
/>
|
|
676
530
|
|
|
677
|
-
|
|
678
|
-
|
|
531
|
+
<div
|
|
532
|
+
v-if="selectedRows.length"
|
|
533
|
+
v-bind="headerCounterAttrs"
|
|
534
|
+
v-text="selectedRows.length"
|
|
535
|
+
/>
|
|
536
|
+
</th>
|
|
679
537
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
538
|
+
<th
|
|
539
|
+
v-for="(column, index) in visibleColumns"
|
|
540
|
+
:key="index"
|
|
541
|
+
v-bind="headerCellBaseAttrs"
|
|
542
|
+
:class="cx([(headerCellBaseAttrs as any).class, column.thClass])"
|
|
543
|
+
>
|
|
544
|
+
<!--
|
|
545
|
+
@slot Use it to customise needed header cell.
|
|
546
|
+
@binding {object} column
|
|
547
|
+
@binding {number} index
|
|
548
|
+
-->
|
|
549
|
+
<slot
|
|
550
|
+
v-if="hasSlotContent($slots[`header-${column.key}`])"
|
|
551
|
+
:name="`header-${column.key}`"
|
|
552
|
+
:column="column"
|
|
553
|
+
:index="index"
|
|
554
|
+
/>
|
|
683
555
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
556
|
+
<template v-else>
|
|
557
|
+
{{ column.label }}
|
|
558
|
+
</template>
|
|
687
559
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
560
|
+
<!--
|
|
561
|
+
@slot Use it to add something after the needed header cell.
|
|
562
|
+
@binding {object} column
|
|
563
|
+
@binding {number} index
|
|
564
|
+
-->
|
|
565
|
+
<slot :name="`header-${column.key}-after`" :column="column" :index="index" />
|
|
566
|
+
</th>
|
|
567
|
+
</tr>
|
|
691
568
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
selectedRows.value = [];
|
|
569
|
+
<ULoaderProgress :loading="loading" v-bind="headerLoaderAttrs" />
|
|
570
|
+
</thead>
|
|
695
571
|
|
|
696
|
-
|
|
697
|
-
|
|
572
|
+
<tbody v-if="tableRows.length" v-bind="bodyAttrs">
|
|
573
|
+
<template v-for="(row, rowIndex) in sortedRows" :key="row.id">
|
|
574
|
+
<tr
|
|
575
|
+
v-if="rowIndex === firstRow && hasSlotContent($slots['before-first-row'])"
|
|
576
|
+
v-bind="bodyRowBeforeAttrs"
|
|
577
|
+
>
|
|
578
|
+
<td :colspan="colsCount" v-bind="bodyRowBeforeCellAttrs">
|
|
579
|
+
<!-- @slot Use it to add something before first row. -->
|
|
580
|
+
<slot name="before-first-row" />
|
|
581
|
+
</td>
|
|
582
|
+
</tr>
|
|
698
583
|
|
|
699
|
-
|
|
700
|
-
|
|
584
|
+
<tr v-if="isShownDateDivider(rowIndex) && row.rowDate" v-bind="bodyRowDateDividerAttrs">
|
|
585
|
+
<td v-bind="bodyCellDateDividerAttrs" :colspan="colsCount">
|
|
586
|
+
<UDivider
|
|
587
|
+
size="xs"
|
|
588
|
+
:label="getDateDividerLabel(row.rowDate)"
|
|
589
|
+
v-bind="bodyDateDividerAttrs"
|
|
590
|
+
/>
|
|
591
|
+
</td>
|
|
592
|
+
</tr>
|
|
701
593
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
594
|
+
<UTableRow
|
|
595
|
+
v-model:selected-rows="selectedRows"
|
|
596
|
+
:selectable="selectable"
|
|
597
|
+
:data-test="`${dataTest}-row`"
|
|
598
|
+
:row="row"
|
|
599
|
+
:columns="normalizedColumns"
|
|
600
|
+
:config="config"
|
|
601
|
+
:attrs="tableRowAttrs as unknown as UTableRowAttrs"
|
|
602
|
+
:nested-level="0"
|
|
603
|
+
:empty-cell-label="emptyCellLabel"
|
|
604
|
+
@click="onClickRow"
|
|
605
|
+
@click-cell="onClickCell"
|
|
606
|
+
@toggle-row-visibility="onToggleRowVisibility"
|
|
607
|
+
>
|
|
608
|
+
<template
|
|
609
|
+
v-for="(value, key, index) in mapRowColumns(row, normalizedColumns)"
|
|
610
|
+
:key="index"
|
|
611
|
+
#[`cell-${key}`]="slotValues"
|
|
612
|
+
>
|
|
613
|
+
<!--
|
|
614
|
+
@slot Use it to customise needed table cell.
|
|
615
|
+
@binding {string} value
|
|
616
|
+
@binding {object} row
|
|
617
|
+
@binding {number} index
|
|
618
|
+
-->
|
|
619
|
+
<slot
|
|
620
|
+
:name="`cell-${key}`"
|
|
621
|
+
:value="slotValues.value"
|
|
622
|
+
:row="slotValues.row"
|
|
623
|
+
:index="index"
|
|
624
|
+
/>
|
|
625
|
+
</template>
|
|
626
|
+
<template #nested-content>
|
|
627
|
+
<!--
|
|
628
|
+
@slot Use it to add nested content inside a row.
|
|
629
|
+
@binding {object} row
|
|
630
|
+
-->
|
|
631
|
+
<slot v-if="row" name="nested-content" :row="row" />
|
|
632
|
+
</template>
|
|
633
|
+
</UTableRow>
|
|
705
634
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
635
|
+
<tr
|
|
636
|
+
v-if="rowIndex === lastRow && hasSlotContent($slots['after-last-row'])"
|
|
637
|
+
v-bind="bodyRowAfterAttrs"
|
|
638
|
+
>
|
|
639
|
+
<td :colspan="colsCount" v-bind="bodyRowAfterCellAttrs">
|
|
640
|
+
<!-- @slot Use it to add something after last row. -->
|
|
641
|
+
<slot name="after-last-row" />
|
|
642
|
+
</td>
|
|
643
|
+
</tr>
|
|
644
|
+
</template>
|
|
645
|
+
</tbody>
|
|
710
646
|
|
|
711
|
-
|
|
712
|
-
|
|
647
|
+
<tbody v-else>
|
|
648
|
+
<tr>
|
|
649
|
+
<td :colspan="colsCount">
|
|
650
|
+
<!-- @slot Use it to add custom empty state. -->
|
|
651
|
+
<slot name="empty-state">
|
|
652
|
+
<UEmpty
|
|
653
|
+
size="md"
|
|
654
|
+
:description="currentLocale.noData"
|
|
655
|
+
:data-test="`${dataTest}-empty`"
|
|
656
|
+
v-bind="bodyEmptyStateAttrs"
|
|
657
|
+
/>
|
|
658
|
+
</slot>
|
|
659
|
+
</td>
|
|
660
|
+
</tr>
|
|
661
|
+
</tbody>
|
|
713
662
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
663
|
+
<tfoot v-if="hasSlotContent($slots['footer'])" v-bind="footerAttrs">
|
|
664
|
+
<tr ref="footer-row" v-bind="footerRowAttrs">
|
|
665
|
+
<td v-if="selectable" />
|
|
717
666
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
667
|
+
<!--
|
|
668
|
+
@slot Use it to add something into the table footer.
|
|
669
|
+
@binding {number} cols-count
|
|
670
|
+
-->
|
|
671
|
+
<slot name="footer" :cols-count="colsCount" />
|
|
672
|
+
</tr>
|
|
722
673
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
674
|
+
<tr ref="sticky-footer-row" :style="tableRowWidthStyle" v-bind="stickyFooterRowAttrs">
|
|
675
|
+
<td v-if="selectable" />
|
|
676
|
+
|
|
677
|
+
<!--
|
|
678
|
+
@slot Use it to add something into the table footer.
|
|
679
|
+
@binding {number} cols-count
|
|
680
|
+
-->
|
|
681
|
+
<slot name="footer" :cols-count="colsCount" />
|
|
682
|
+
</tr>
|
|
683
|
+
</tfoot>
|
|
684
|
+
</table>
|
|
685
|
+
</div>
|
|
686
|
+
</div>
|
|
687
|
+
</template>
|