@vuecs/table 1.0.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/LICENSE +201 -0
- package/README.md +59 -0
- package/dist/components/Table.vue.d.ts +511 -0
- package/dist/components/Table.vue.d.ts.map +1 -0
- package/dist/components/TableBody.vue.d.ts +43 -0
- package/dist/components/TableBody.vue.d.ts.map +1 -0
- package/dist/components/TableCell.vue.d.ts +182 -0
- package/dist/components/TableCell.vue.d.ts.map +1 -0
- package/dist/components/TableEmpty.vue.d.ts +104 -0
- package/dist/components/TableEmpty.vue.d.ts.map +1 -0
- package/dist/components/TableFooter.vue.d.ts +40 -0
- package/dist/components/TableFooter.vue.d.ts.map +1 -0
- package/dist/components/TableHeadCell.vue.d.ts +267 -0
- package/dist/components/TableHeadCell.vue.d.ts.map +1 -0
- package/dist/components/TableHeader.vue.d.ts +40 -0
- package/dist/components/TableHeader.vue.d.ts.map +1 -0
- package/dist/components/TableLite.vue.d.ts +250 -0
- package/dist/components/TableLite.vue.d.ts.map +1 -0
- package/dist/components/TableLoading.vue.d.ts +88 -0
- package/dist/components/TableLoading.vue.d.ts.map +1 -0
- package/dist/components/TableRow.vue.d.ts +116 -0
- package/dist/components/TableRow.vue.d.ts.map +1 -0
- package/dist/components/TableSortIndicators.vue.d.ts +335 -0
- package/dist/components/TableSortIndicators.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +23 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/composables/context.d.ts +104 -0
- package/dist/composables/context.d.ts.map +1 -0
- package/dist/composables/define-table.d.ts +48 -0
- package/dist/composables/define-table.d.ts.map +1 -0
- package/dist/composables/index.d.ts +5 -0
- package/dist/composables/index.d.ts.map +1 -0
- package/dist/composables/selection.d.ts +10 -0
- package/dist/composables/selection.d.ts.map +1 -0
- package/dist/composables/sort.d.ts +61 -0
- package/dist/composables/sort.d.ts.map +1 -0
- package/dist/defaults.d.ts +41 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +2081 -0
- package/dist/index.mjs.map +1 -0
- package/dist/style.css +145 -0
- package/dist/theme.d.ts +13 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/types.d.ts +248 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/auto-render.d.ts +31 -0
- package/dist/utils/auto-render.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/render-utils.d.ts +49 -0
- package/dist/utils/render-utils.d.ts.map +1 -0
- package/dist/utils/sort-rows.d.ts +29 -0
- package/dist/utils/sort-rows.d.ts.map +1 -0
- package/dist/vue.d.ts +27 -0
- package/dist/vue.d.ts.map +1 -0
- package/package.json +62 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2081 @@
|
|
|
1
|
+
import { installDefaultsManager, installThemeManager, isObject, themableProps, useComponentDefaults, useComponentTheme, useSelectionMachine as useRowSelectionMachine, useThemeProps } from "@vuecs/core";
|
|
2
|
+
import { Comment, Fragment, Teleport, Text, computed, defineComponent, h, inject, mergeProps, nextTick, onBeforeUnmount, onMounted, provide, ref, shallowRef, toRef, triggerRef, watch } from "vue";
|
|
3
|
+
//#region src/composables/context.ts
|
|
4
|
+
const TABLE_CONTEXT_KEY = Symbol("vcTableContext");
|
|
5
|
+
function provideTableContext(ctx) {
|
|
6
|
+
provide(TABLE_CONTEXT_KEY, ctx);
|
|
7
|
+
}
|
|
8
|
+
function useTable() {
|
|
9
|
+
return inject(TABLE_CONTEXT_KEY, null);
|
|
10
|
+
}
|
|
11
|
+
const TABLE_ROW_CONTEXT_KEY = Symbol("vcTableRowContext");
|
|
12
|
+
function provideTableRowContext(ctx) {
|
|
13
|
+
provide(TABLE_ROW_CONTEXT_KEY, ctx);
|
|
14
|
+
}
|
|
15
|
+
function useTableRow() {
|
|
16
|
+
return inject(TABLE_ROW_CONTEXT_KEY, null);
|
|
17
|
+
}
|
|
18
|
+
const TABLE_HEAD_CELL_COUNT_CONTEXT_KEY = Symbol("vcTableHeadCellCountContext");
|
|
19
|
+
function provideHeadCellCountContext(ctx) {
|
|
20
|
+
provide(TABLE_HEAD_CELL_COUNT_CONTEXT_KEY, ctx);
|
|
21
|
+
}
|
|
22
|
+
function useHeadCellCountContext() {
|
|
23
|
+
return inject(TABLE_HEAD_CELL_COUNT_CONTEXT_KEY, null);
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/composables/sort.ts
|
|
27
|
+
/**
|
|
28
|
+
* Controlled sort machine. The consumer owns the sort state via
|
|
29
|
+
* `v-model:sort` — the table emits intent only. When `<VCTable
|
|
30
|
+
* :client-sort>` is set, the table additionally sorts data internally
|
|
31
|
+
* via `sortRows()` from `utils/sort-rows.ts`; the controlled v-model
|
|
32
|
+
* still emits, so consumers stay observable.
|
|
33
|
+
*
|
|
34
|
+
* State shape is `SortDescriptor[]` from v1.x-B onward — single-column
|
|
35
|
+
* sort is an array of length 0–1. Multi-key cycling lives entirely
|
|
36
|
+
* here; `<VCTableHeadCell>` reads `directionFor` / `positionFor` to
|
|
37
|
+
* paint the indicator + numeric badge.
|
|
38
|
+
*/
|
|
39
|
+
function useSortMachine(options) {
|
|
40
|
+
const { source, columns, mustSort, maxSortKeys, emit } = options;
|
|
41
|
+
const local = ref(normalize(source.value));
|
|
42
|
+
watch(source, (next) => {
|
|
43
|
+
local.value = normalize(next);
|
|
44
|
+
});
|
|
45
|
+
const state = computed(() => local.value);
|
|
46
|
+
function findInitialDirection(key) {
|
|
47
|
+
return columns.value.find((c) => c.key === key)?.initialSortDirection ?? "asc";
|
|
48
|
+
}
|
|
49
|
+
function setSort(key, opts = {}) {
|
|
50
|
+
const current = local.value;
|
|
51
|
+
const idx = current.findIndex((s) => s.key === key);
|
|
52
|
+
const initial = findInitialDirection(key);
|
|
53
|
+
if (opts.direction !== void 0) {
|
|
54
|
+
const next = opts.append ? upsertAppend(current, key, opts.direction, maxSortKeys.value) : [{
|
|
55
|
+
key,
|
|
56
|
+
direction: opts.direction
|
|
57
|
+
}];
|
|
58
|
+
local.value = next;
|
|
59
|
+
emit(next);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!opts.append) {
|
|
63
|
+
const existing = idx >= 0 ? current[idx] : null;
|
|
64
|
+
let next;
|
|
65
|
+
if (!existing) next = [{
|
|
66
|
+
key,
|
|
67
|
+
direction: initial
|
|
68
|
+
}];
|
|
69
|
+
else if (existing.direction === initial) next = [{
|
|
70
|
+
key,
|
|
71
|
+
direction: initial === "asc" ? "desc" : "asc"
|
|
72
|
+
}];
|
|
73
|
+
else next = mustSort.value ? [{
|
|
74
|
+
key,
|
|
75
|
+
direction: initial
|
|
76
|
+
}] : [];
|
|
77
|
+
local.value = next;
|
|
78
|
+
emit(next);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (idx < 0) {
|
|
82
|
+
const next = appendCapped(current, {
|
|
83
|
+
key,
|
|
84
|
+
direction: initial
|
|
85
|
+
}, maxSortKeys.value);
|
|
86
|
+
local.value = next;
|
|
87
|
+
emit(next);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const existing = current[idx];
|
|
91
|
+
let next;
|
|
92
|
+
if (existing.direction === initial) next = current.map((s, i) => i === idx ? {
|
|
93
|
+
key,
|
|
94
|
+
direction: initial === "asc" ? "desc" : "asc"
|
|
95
|
+
} : s);
|
|
96
|
+
else next = current.filter((_, i) => i !== idx);
|
|
97
|
+
local.value = next;
|
|
98
|
+
emit(next);
|
|
99
|
+
}
|
|
100
|
+
function directionFor(key) {
|
|
101
|
+
const cur = local.value.find((s) => s.key === key);
|
|
102
|
+
return cur ? cur.direction : null;
|
|
103
|
+
}
|
|
104
|
+
function positionFor(key) {
|
|
105
|
+
const i = local.value.findIndex((s) => s.key === key);
|
|
106
|
+
return i < 0 ? null : i + 1;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Replace the entire sort state. Bypasses cycle logic + multi-sort
|
|
110
|
+
* gating — for callers that know exactly what they want (e.g. the
|
|
111
|
+
* sort-indicator chip row removes / reorders / clears descriptors
|
|
112
|
+
* directly without going through Shift-click semantics).
|
|
113
|
+
*/
|
|
114
|
+
function setState(next) {
|
|
115
|
+
local.value = next;
|
|
116
|
+
emit(next);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
state,
|
|
120
|
+
setSort,
|
|
121
|
+
setState,
|
|
122
|
+
directionFor,
|
|
123
|
+
positionFor
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Coerce v-model input into the canonical array shape. `undefined` /
|
|
128
|
+
* `null` (which Vue may bind through during the v1.x-B migration
|
|
129
|
+
* window when consumers still hold `ref<...>(null)`) become `[]`.
|
|
130
|
+
*/
|
|
131
|
+
function normalize(v) {
|
|
132
|
+
if (v == null) return [];
|
|
133
|
+
return v;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Append a new descriptor, evicting the oldest when over the cap.
|
|
137
|
+
* Cap of `<= 0` is treated as "unlimited" so consumers can opt out.
|
|
138
|
+
*/
|
|
139
|
+
function appendCapped(state, descriptor, max) {
|
|
140
|
+
const next = [...state, descriptor];
|
|
141
|
+
if (max > 0 && next.length > max) return next.slice(next.length - max);
|
|
142
|
+
return next;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* In-place semantic: if `key` is already in the state, update its
|
|
146
|
+
* direction; else append (capped). Used by the explicit-direction
|
|
147
|
+
* path when `append` is set.
|
|
148
|
+
*/
|
|
149
|
+
function upsertAppend(state, key, direction, max) {
|
|
150
|
+
const idx = state.findIndex((s) => s.key === key);
|
|
151
|
+
if (idx >= 0) return state.map((s, i) => i === idx ? {
|
|
152
|
+
key,
|
|
153
|
+
direction
|
|
154
|
+
} : s);
|
|
155
|
+
return appendCapped(state, {
|
|
156
|
+
key,
|
|
157
|
+
direction
|
|
158
|
+
}, max);
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/components/TableBody.vue?vue&type=script&lang.ts
|
|
162
|
+
const tableBodyThemeDefaults$1 = { classes: { root: "vc-table-body" } };
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/components/TableBody.vue
|
|
165
|
+
var TableBody_default = defineComponent({
|
|
166
|
+
name: "VCTableBody",
|
|
167
|
+
inheritAttrs: false,
|
|
168
|
+
props: { ...themableProps() },
|
|
169
|
+
slots: Object,
|
|
170
|
+
setup(props, { attrs, slots }) {
|
|
171
|
+
const theme = useComponentTheme("tableBody", useThemeProps(props), tableBodyThemeDefaults$1);
|
|
172
|
+
const ctx = useTable();
|
|
173
|
+
return () => {
|
|
174
|
+
const rowSlot = slots.row;
|
|
175
|
+
const data = ctx?.data.value ?? [];
|
|
176
|
+
let children;
|
|
177
|
+
if (rowSlot) {
|
|
178
|
+
if (data.length === 0) return null;
|
|
179
|
+
children = data.map((row, index) => rowSlot({
|
|
180
|
+
row,
|
|
181
|
+
index
|
|
182
|
+
}));
|
|
183
|
+
} else children = [slots.default?.() ?? null];
|
|
184
|
+
return h("tbody", mergeProps(attrs, { class: theme.value.root || void 0 }), children);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/utils/render-utils.ts
|
|
190
|
+
/**
|
|
191
|
+
* Convert a key like `'firstName'` / `'first_name'` / `'first-name'` to
|
|
192
|
+
* `'First Name'`. Used by the bare-string column shorthand to derive
|
|
193
|
+
* a label when only the key is given.
|
|
194
|
+
*/
|
|
195
|
+
function startCase(input) {
|
|
196
|
+
if (!input) return "";
|
|
197
|
+
return input.replace(/[_-]+/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\s+/g, " ").trim().split(" ").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Normalize the `:columns` prop into a uniform `TableColumn[]`.
|
|
201
|
+
*
|
|
202
|
+
* - **Explicit array** (including empty `[]`): pass through, fill `label`
|
|
203
|
+
* from `startCase(key)`. Treating `[]` as authoritative lets consumers
|
|
204
|
+
* deliberately render a header-less table during loading without the
|
|
205
|
+
* auto-derive kicking in the moment data arrives.
|
|
206
|
+
* - **Bare-string shorthand**: `['id', 'name']` → `[{ key, label }, ...]`.
|
|
207
|
+
* - **Auto-derive**: only when `columns` is `undefined` AND `data[0]` is an
|
|
208
|
+
* object, derive the columns from `Object.keys(data[0])`, skipping
|
|
209
|
+
* underscore-prefixed row-meta keys (`_rowVariant` / `_cellVariants`).
|
|
210
|
+
*/
|
|
211
|
+
function normalizeColumns(columns, data) {
|
|
212
|
+
const fromRaw = (col) => {
|
|
213
|
+
if (typeof col === "string") return {
|
|
214
|
+
key: col,
|
|
215
|
+
label: startCase(col)
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
...col,
|
|
219
|
+
label: col.label ?? startCase(col.key)
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
if (columns !== void 0) return columns.map(fromRaw);
|
|
223
|
+
if (data.length === 0 || !isObject(data[0])) return [];
|
|
224
|
+
const first = data[0];
|
|
225
|
+
return Object.keys(first).filter((key) => !key.startsWith("_")).map((key) => ({
|
|
226
|
+
key,
|
|
227
|
+
label: startCase(key)
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Resolve a column's value against a row.
|
|
232
|
+
*
|
|
233
|
+
* - String accessor: dot-path lookup (e.g. `'profile.email'` → `row.profile.email`)
|
|
234
|
+
* - Function accessor: invoked with the row
|
|
235
|
+
* - Falls back to `row[column.key]`
|
|
236
|
+
*/
|
|
237
|
+
function resolveCellValue(column, row) {
|
|
238
|
+
const { accessor } = column;
|
|
239
|
+
if (typeof accessor === "function") return accessor(row);
|
|
240
|
+
if (typeof accessor === "string") return resolveDotPath(row, accessor);
|
|
241
|
+
if (isObject(row)) return row[column.key];
|
|
242
|
+
}
|
|
243
|
+
function resolveDotPath(obj, path) {
|
|
244
|
+
if (!isObject(obj)) return void 0;
|
|
245
|
+
let cur = obj;
|
|
246
|
+
for (const part of path.split(".")) {
|
|
247
|
+
if (!isObject(cur)) return void 0;
|
|
248
|
+
cur = cur[part];
|
|
249
|
+
}
|
|
250
|
+
return cur;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Resolve attribute objects, supporting both static-object and function forms
|
|
254
|
+
* used by `cellAttrs` and `headerAttrs`.
|
|
255
|
+
*/
|
|
256
|
+
function resolveAttrs(attrs, ctx) {
|
|
257
|
+
if (!attrs) return void 0;
|
|
258
|
+
return typeof attrs === "function" ? attrs(ctx) : attrs;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Selectors that mark an element (or an ancestor of it) as interactive —
|
|
262
|
+
* row-click handlers should NOT fire when the click originates inside one
|
|
263
|
+
* of these. Plan 028 D5 ships this list; the closest-ancestor matching
|
|
264
|
+
* (vs direct-match) is what catches clicks on text INSIDE a button or
|
|
265
|
+
* anchor.
|
|
266
|
+
*/
|
|
267
|
+
const INTERACTIVE_SELECTOR = [
|
|
268
|
+
"a",
|
|
269
|
+
"button",
|
|
270
|
+
"input",
|
|
271
|
+
"select",
|
|
272
|
+
"textarea",
|
|
273
|
+
"label[for]",
|
|
274
|
+
"[role=\"button\"]",
|
|
275
|
+
"[role=\"link\"]",
|
|
276
|
+
"[contenteditable]:not([contenteditable=\"false\"])",
|
|
277
|
+
"[tabindex]:not([tabindex=\"-1\"])",
|
|
278
|
+
".vc-overlay-portal-content"
|
|
279
|
+
].join(",");
|
|
280
|
+
/**
|
|
281
|
+
* Returns `true` when a row-level click should be suppressed because the
|
|
282
|
+
* event originated inside an interactive descendant — a button, anchor,
|
|
283
|
+
* form input, role=button/link, an explicitly-focusable element, or a
|
|
284
|
+
* portal'd overlay rendered into the document body (e.g. a
|
|
285
|
+
* `<VCDropdownMenu>` action menu inside the row).
|
|
286
|
+
*
|
|
287
|
+
* Pass the `<tr>` element as `rowEl` so the helper can disambiguate
|
|
288
|
+
* legitimate row clicks (where the `closest()` ancestor walk reaches the
|
|
289
|
+
* `<tr>` itself, which carries `tabindex="0"` when the row is clickable)
|
|
290
|
+
* from clicks on a genuine interactive descendant.
|
|
291
|
+
*
|
|
292
|
+
* Use at the row click handler: `if (filterRowClickEvent(event, tr)) return;`.
|
|
293
|
+
*/
|
|
294
|
+
function filterRowClickEvent(event, rowEl) {
|
|
295
|
+
const { target } = event;
|
|
296
|
+
if (!(target instanceof Element)) return false;
|
|
297
|
+
const hit = target.closest(INTERACTIVE_SELECTOR);
|
|
298
|
+
if (hit === null) return false;
|
|
299
|
+
if (rowEl && hit === rowEl) return false;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/components/TableCell.vue?vue&type=script&lang.ts
|
|
304
|
+
const tableCellThemeDefaults$1 = { classes: { root: "vc-table-cell" } };
|
|
305
|
+
const tableCellProps = {
|
|
306
|
+
/** Render as `<th scope="row">` instead of `<td>` (mirror of `column.isRowHeader`). */
|
|
307
|
+
isRowHeader: {
|
|
308
|
+
type: Boolean,
|
|
309
|
+
default: false
|
|
310
|
+
},
|
|
311
|
+
/**
|
|
312
|
+
* Column key this cell corresponds to — used to resolve
|
|
313
|
+
* `_cellVariants[key]` from row meta AND to look up the column's
|
|
314
|
+
* `accessor` / `formatter` for the default-render path.
|
|
315
|
+
*/
|
|
316
|
+
columnKey: {
|
|
317
|
+
type: String,
|
|
318
|
+
default: void 0
|
|
319
|
+
},
|
|
320
|
+
/** Forward-compat for stacked-mode CSS — emitted as `data-label`. */
|
|
321
|
+
dataLabel: {
|
|
322
|
+
type: String,
|
|
323
|
+
default: void 0
|
|
324
|
+
},
|
|
325
|
+
/** Alignment helper — selects the `align.<value>` theme variant. */
|
|
326
|
+
align: {
|
|
327
|
+
type: String,
|
|
328
|
+
default: void 0
|
|
329
|
+
},
|
|
330
|
+
/** `position: sticky` on this cell. Forwarded as `themeVariant.stickyColumn`. */
|
|
331
|
+
stickyColumn: {
|
|
332
|
+
type: Boolean,
|
|
333
|
+
default: void 0
|
|
334
|
+
},
|
|
335
|
+
/**
|
|
336
|
+
* Renders a selection checkbox/radio for this row. Pairs with
|
|
337
|
+
* `<VCTableHeadCell isSelector>` to build a selection column.
|
|
338
|
+
* State mirrors `selection.isSelected(rowKey)`; clicking toggles
|
|
339
|
+
* that row independently of any row-click handler. Falls back
|
|
340
|
+
* to the default slot when selection is off.
|
|
341
|
+
*/
|
|
342
|
+
isSelector: {
|
|
343
|
+
type: Boolean,
|
|
344
|
+
default: false
|
|
345
|
+
},
|
|
346
|
+
/** `aria-label` for the per-row checkbox (defaults to `'Select row'`). */
|
|
347
|
+
selectorAriaLabel: {
|
|
348
|
+
type: String,
|
|
349
|
+
default: "Select row"
|
|
350
|
+
},
|
|
351
|
+
...themableProps()
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* Detect whether the consumer-passed default slot's rendered vnodes
|
|
355
|
+
* contain any meaningful content (vs being an empty / whitespace-only
|
|
356
|
+
* / comment-only render).
|
|
357
|
+
*
|
|
358
|
+
* Vue passes `slots.default` as a render fn that's always present when
|
|
359
|
+
* a `<template>` slot is declared (even if empty), so a naive
|
|
360
|
+
* `slots.default?.()` truthy check would always pick the slot path
|
|
361
|
+
* and skip the auto-render. Walk the rendered vnodes and recurse into
|
|
362
|
+
* `Fragment` children — a `<template v-if>` / `<template v-for>` /
|
|
363
|
+
* multi-child template renders as a Fragment whose `children` is the
|
|
364
|
+
* actual content. Without recursion those would be falsely classified
|
|
365
|
+
* as empty.
|
|
366
|
+
*
|
|
367
|
+
* Takes the pre-rendered vnodes (not the slot fn) so the caller can
|
|
368
|
+
* invoke the slot at most once per render — slot fns can be
|
|
369
|
+
* non-trivial and should not be called twice.
|
|
370
|
+
*/
|
|
371
|
+
function hasMeaningfulSlotContent(nodes) {
|
|
372
|
+
if (nodes == null || nodes === false) return false;
|
|
373
|
+
if (typeof nodes === "string") return nodes.trim().length > 0;
|
|
374
|
+
if (typeof nodes === "number") return true;
|
|
375
|
+
if (Array.isArray(nodes)) {
|
|
376
|
+
for (const child of nodes) if (hasMeaningfulSlotContent(child)) return true;
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
if (typeof nodes !== "object") return false;
|
|
380
|
+
const v = nodes;
|
|
381
|
+
if (v.type === Comment) return false;
|
|
382
|
+
if (v.type === Text) return typeof v.children === "string" && v.children.trim().length > 0;
|
|
383
|
+
if (v.type === Fragment) return hasMeaningfulSlotContent(v.children);
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/components/TableCell.vue
|
|
388
|
+
var TableCell_default = defineComponent({
|
|
389
|
+
name: "VCTableCell",
|
|
390
|
+
inheritAttrs: false,
|
|
391
|
+
props: tableCellProps,
|
|
392
|
+
setup(props, { attrs, slots }) {
|
|
393
|
+
const tableCtx = useTable();
|
|
394
|
+
const rowCtx = useTableRow();
|
|
395
|
+
const themeProps = useThemeProps(props, "align", "stickyColumn");
|
|
396
|
+
const theme = useComponentTheme("tableCell", {
|
|
397
|
+
get themeClass() {
|
|
398
|
+
return themeProps.themeClass;
|
|
399
|
+
},
|
|
400
|
+
get themeVariant() {
|
|
401
|
+
const cellVariant = props.columnKey ? rowCtx?.cellVariants.value[props.columnKey] ?? void 0 : void 0;
|
|
402
|
+
return {
|
|
403
|
+
...themeProps.themeVariant ?? {},
|
|
404
|
+
...cellVariant ? { cellVariant } : {}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}, tableCellThemeDefaults$1);
|
|
408
|
+
return () => {
|
|
409
|
+
if (props.isSelector && tableCtx?.selection.mode.value !== void 0 && rowCtx) {
|
|
410
|
+
const rowKey = rowCtx.selectionKey.value;
|
|
411
|
+
const mode = tableCtx.selection.mode.value;
|
|
412
|
+
const checked = tableCtx.selection.isSelected(rowKey);
|
|
413
|
+
return h(props.isRowHeader ? "th" : "td", mergeProps(attrs, {
|
|
414
|
+
class: theme.value.root || void 0,
|
|
415
|
+
"data-label": props.dataLabel || void 0,
|
|
416
|
+
"data-sticky-column": props.stickyColumn ? "" : void 0,
|
|
417
|
+
scope: props.isRowHeader ? "row" : void 0,
|
|
418
|
+
role: props.isRowHeader ? "rowheader" : "gridcell"
|
|
419
|
+
}), [h("input", {
|
|
420
|
+
type: mode === "single" ? "radio" : "checkbox",
|
|
421
|
+
class: "vc-table-selector-checkbox",
|
|
422
|
+
"aria-label": props.selectorAriaLabel,
|
|
423
|
+
checked,
|
|
424
|
+
onClick: (e) => {
|
|
425
|
+
e.stopPropagation();
|
|
426
|
+
if (rowKey === void 0) return;
|
|
427
|
+
tableCtx.selection.toggle(rowKey);
|
|
428
|
+
}
|
|
429
|
+
})]);
|
|
430
|
+
}
|
|
431
|
+
const slotVNodes = slots.default?.();
|
|
432
|
+
let content = slotVNodes;
|
|
433
|
+
if (props.columnKey && tableCtx && rowCtx && !hasMeaningfulSlotContent(slotVNodes)) {
|
|
434
|
+
const column = tableCtx.columns.value.find((c) => c.key === props.columnKey);
|
|
435
|
+
if (column) {
|
|
436
|
+
const value = resolveCellValue(column, rowCtx.row.value);
|
|
437
|
+
if (column.formatter) content = column.formatter({
|
|
438
|
+
value,
|
|
439
|
+
key: column.key,
|
|
440
|
+
row: rowCtx.row.value
|
|
441
|
+
});
|
|
442
|
+
else if (value === void 0 || value === null) content = "";
|
|
443
|
+
else content = String(value);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const inGrid = tableCtx?.selection.mode.value !== void 0;
|
|
447
|
+
let cellRole;
|
|
448
|
+
if (inGrid) cellRole = props.isRowHeader ? "rowheader" : "gridcell";
|
|
449
|
+
const ariaAttrs = inGrid ? { role: cellRole } : {};
|
|
450
|
+
return h(props.isRowHeader ? "th" : "td", mergeProps(attrs, {
|
|
451
|
+
class: theme.value.root || void 0,
|
|
452
|
+
"data-label": props.dataLabel || void 0,
|
|
453
|
+
"data-sticky-column": props.stickyColumn ? "" : void 0,
|
|
454
|
+
scope: props.isRowHeader ? "row" : void 0,
|
|
455
|
+
...ariaAttrs
|
|
456
|
+
}), content);
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
//#endregion
|
|
461
|
+
//#region src/components/TableFooter.vue?vue&type=script&lang.ts
|
|
462
|
+
const tableFooterThemeDefaults$1 = { classes: { root: "vc-table-footer" } };
|
|
463
|
+
//#endregion
|
|
464
|
+
//#region src/components/TableFooter.vue
|
|
465
|
+
var TableFooter_default = defineComponent({
|
|
466
|
+
name: "VCTableFooter",
|
|
467
|
+
inheritAttrs: false,
|
|
468
|
+
props: { ...themableProps() },
|
|
469
|
+
setup(props, { attrs, slots }) {
|
|
470
|
+
const theme = useComponentTheme("tableFooter", useThemeProps(props), tableFooterThemeDefaults$1);
|
|
471
|
+
provideHeadCellCountContext({
|
|
472
|
+
register: () => {},
|
|
473
|
+
unregister: () => {}
|
|
474
|
+
});
|
|
475
|
+
return () => h("tfoot", mergeProps(attrs, { class: theme.value.root || void 0 }), slots.default?.());
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/components/TableHeadCell.vue?vue&type=script&lang.ts
|
|
480
|
+
const tableHeadCellThemeDefaults$1 = { classes: {
|
|
481
|
+
root: "vc-table-head-cell",
|
|
482
|
+
sortIcon: "vc-table-head-cell-sort-icon"
|
|
483
|
+
} };
|
|
484
|
+
function renderHeadContent(args) {
|
|
485
|
+
if (!args.isSelector) return args.defaultSlot?.();
|
|
486
|
+
if (args.selectionMode === "single") return null;
|
|
487
|
+
if (args.selectionMode === void 0) return args.defaultSlot?.();
|
|
488
|
+
return h("input", {
|
|
489
|
+
type: "checkbox",
|
|
490
|
+
class: "vc-table-selector-checkbox",
|
|
491
|
+
"aria-label": args.selectorAriaLabel,
|
|
492
|
+
checked: args.selectAllState === "all",
|
|
493
|
+
indeterminate: args.selectAllState === "some",
|
|
494
|
+
onClick: (e) => {
|
|
495
|
+
e.stopPropagation();
|
|
496
|
+
args.onSelectorClick();
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
//#endregion
|
|
501
|
+
//#region src/components/TableHeadCell.vue
|
|
502
|
+
var TableHeadCell_default = defineComponent({
|
|
503
|
+
name: "VCTableHeadCell",
|
|
504
|
+
inheritAttrs: false,
|
|
505
|
+
props: {
|
|
506
|
+
/** Column key — required for sort wiring (omit for purely presentational heads). */
|
|
507
|
+
columnKey: {
|
|
508
|
+
type: String,
|
|
509
|
+
default: void 0
|
|
510
|
+
},
|
|
511
|
+
/** Mark this header sortable. When set, click + Enter/Space cycle sort state. */
|
|
512
|
+
sortable: {
|
|
513
|
+
type: Boolean,
|
|
514
|
+
default: false
|
|
515
|
+
},
|
|
516
|
+
/** Override scope ("col" / "colgroup" / "row" / "rowgroup"). Smart default: colspan→colgroup, rowspan→rowgroup, else col. */
|
|
517
|
+
scope: {
|
|
518
|
+
type: String,
|
|
519
|
+
default: void 0
|
|
520
|
+
},
|
|
521
|
+
/** Native `<th colspan>` (forwarded; also drives the smart scope default). */
|
|
522
|
+
colspan: {
|
|
523
|
+
type: Number,
|
|
524
|
+
default: void 0
|
|
525
|
+
},
|
|
526
|
+
/** Native `<th rowspan>` (forwarded; also drives the smart scope default). */
|
|
527
|
+
rowspan: {
|
|
528
|
+
type: Number,
|
|
529
|
+
default: void 0
|
|
530
|
+
},
|
|
531
|
+
/** Native `<th title>`. */
|
|
532
|
+
title: {
|
|
533
|
+
type: String,
|
|
534
|
+
default: void 0
|
|
535
|
+
},
|
|
536
|
+
/** Native `<th abbr>`. */
|
|
537
|
+
abbr: {
|
|
538
|
+
type: String,
|
|
539
|
+
default: void 0
|
|
540
|
+
},
|
|
541
|
+
/** Alignment helper — selects the `align.<value>` theme variant. */
|
|
542
|
+
align: {
|
|
543
|
+
type: String,
|
|
544
|
+
default: void 0
|
|
545
|
+
},
|
|
546
|
+
/** `position: sticky` on this header cell. Forwarded as `themeVariant.stickyColumn`. */
|
|
547
|
+
stickyColumn: {
|
|
548
|
+
type: Boolean,
|
|
549
|
+
default: void 0
|
|
550
|
+
},
|
|
551
|
+
/**
|
|
552
|
+
* Renders a select-all checkbox when the parent table has
|
|
553
|
+
* `:selection-mode="multi"`. State is derived from the current
|
|
554
|
+
* selection against the visible data set: checked = all visible
|
|
555
|
+
* rows selected; indeterminate = some visible rows selected;
|
|
556
|
+
* unchecked = none.
|
|
557
|
+
*
|
|
558
|
+
* Click semantics — Gmail / GitHub style: when state is `none`
|
|
559
|
+
* or `some`, ADDS every visible row's key to the selection
|
|
560
|
+
* (preserving any off-screen / paginated selection); when state
|
|
561
|
+
* is `all`, REMOVES every visible row's key (also preserving
|
|
562
|
+
* off-screen selection). Cross-page selection persists across
|
|
563
|
+
* pagination flips.
|
|
564
|
+
*
|
|
565
|
+
* In `single` mode the header renders empty (only one row can
|
|
566
|
+
* be selected); when selection is off, the slot's default
|
|
567
|
+
* content renders so the column collapses gracefully.
|
|
568
|
+
*/
|
|
569
|
+
isSelector: {
|
|
570
|
+
type: Boolean,
|
|
571
|
+
default: false
|
|
572
|
+
},
|
|
573
|
+
/** `aria-label` for the select-all checkbox (defaults to `'Select all rows'`). */
|
|
574
|
+
selectorAriaLabel: {
|
|
575
|
+
type: String,
|
|
576
|
+
default: "Select all rows"
|
|
577
|
+
},
|
|
578
|
+
...themableProps()
|
|
579
|
+
},
|
|
580
|
+
setup(props, { attrs, slots }) {
|
|
581
|
+
const ctx = useTable();
|
|
582
|
+
const countCtx = useHeadCellCountContext();
|
|
583
|
+
onMounted(() => countCtx?.register());
|
|
584
|
+
onBeforeUnmount(() => countCtx?.unregister());
|
|
585
|
+
const themeProps = useThemeProps(props, "align", "stickyColumn");
|
|
586
|
+
const sortDirection = computed(() => {
|
|
587
|
+
if (!props.sortable || !props.columnKey || !ctx) return null;
|
|
588
|
+
const found = ctx.sort.value.find((s) => s.key === props.columnKey);
|
|
589
|
+
return found ? found.direction : null;
|
|
590
|
+
});
|
|
591
|
+
const sortIndex = computed(() => {
|
|
592
|
+
if (!props.sortable || !props.columnKey || !ctx) return null;
|
|
593
|
+
const i = ctx.sort.value.findIndex((s) => s.key === props.columnKey);
|
|
594
|
+
return i < 0 ? null : i + 1;
|
|
595
|
+
});
|
|
596
|
+
const theme = useComponentTheme("tableHeadCell", {
|
|
597
|
+
get themeClass() {
|
|
598
|
+
return themeProps.themeClass;
|
|
599
|
+
},
|
|
600
|
+
get themeVariant() {
|
|
601
|
+
return {
|
|
602
|
+
...themeProps.themeVariant ?? {},
|
|
603
|
+
sorted: sortDirection.value ?? "none"
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
}, tableHeadCellThemeDefaults$1);
|
|
607
|
+
const resolvedScope = computed(() => {
|
|
608
|
+
if (props.scope) return props.scope;
|
|
609
|
+
if (props.colspan && props.colspan > 1) return "colgroup";
|
|
610
|
+
if (props.rowspan && props.rowspan > 1) return "rowgroup";
|
|
611
|
+
return "col";
|
|
612
|
+
});
|
|
613
|
+
const ariaSort = computed(() => {
|
|
614
|
+
if (!props.sortable) return void 0;
|
|
615
|
+
const d = sortDirection.value;
|
|
616
|
+
if (d === "asc") return "ascending";
|
|
617
|
+
if (d === "desc") return "descending";
|
|
618
|
+
return "none";
|
|
619
|
+
});
|
|
620
|
+
function onClick(event) {
|
|
621
|
+
if (!props.sortable || !props.columnKey) return;
|
|
622
|
+
event.preventDefault();
|
|
623
|
+
ctx?.setSort(props.columnKey, { append: event.shiftKey });
|
|
624
|
+
}
|
|
625
|
+
const allRowKeys = computed(() => {
|
|
626
|
+
if (!ctx) return [];
|
|
627
|
+
return ctx.data.value.map((row, i) => ctx.getRowKey(row, i) ?? i);
|
|
628
|
+
});
|
|
629
|
+
const selectAllState = computed(() => {
|
|
630
|
+
if (!ctx || ctx.selection.mode.value !== "multi") return "none";
|
|
631
|
+
const keys = allRowKeys.value;
|
|
632
|
+
if (keys.length === 0) return "none";
|
|
633
|
+
const sel = ctx.selection.value.value;
|
|
634
|
+
const selectedSet = new Set(Array.isArray(sel) ? sel : []);
|
|
635
|
+
if (selectedSet.size === 0) return "none";
|
|
636
|
+
let selectedCount = 0;
|
|
637
|
+
for (const k of keys) if (selectedSet.has(k)) selectedCount += 1;
|
|
638
|
+
if (selectedCount === 0) return "none";
|
|
639
|
+
if (selectedCount === keys.length) return "all";
|
|
640
|
+
return "some";
|
|
641
|
+
});
|
|
642
|
+
function onSelectorClick() {
|
|
643
|
+
if (!ctx || ctx.selection.mode.value !== "multi") return;
|
|
644
|
+
const current = ctx.selection.value.value;
|
|
645
|
+
const existing = new Set(Array.isArray(current) ? current : []);
|
|
646
|
+
if (selectAllState.value === "all") for (const k of allRowKeys.value) existing.delete(k);
|
|
647
|
+
else for (const k of allRowKeys.value) existing.add(k);
|
|
648
|
+
ctx.selection.setValue(Array.from(existing));
|
|
649
|
+
}
|
|
650
|
+
function onKeydown(event) {
|
|
651
|
+
if (!props.sortable || !props.columnKey) return;
|
|
652
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
653
|
+
event.preventDefault();
|
|
654
|
+
ctx?.setSort(props.columnKey, { append: event.shiftKey });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return () => h("th", mergeProps(attrs, {
|
|
658
|
+
class: theme.value.root || void 0,
|
|
659
|
+
scope: resolvedScope.value,
|
|
660
|
+
"data-sticky-column": props.stickyColumn ? "" : void 0,
|
|
661
|
+
colspan: props.colspan,
|
|
662
|
+
rowspan: props.rowspan,
|
|
663
|
+
title: props.title,
|
|
664
|
+
abbr: props.abbr,
|
|
665
|
+
"aria-sort": ariaSort.value,
|
|
666
|
+
"data-sort-index": sortIndex.value !== null && sortIndex.value > 1 ? String(sortIndex.value) : void 0,
|
|
667
|
+
tabindex: props.sortable ? 0 : void 0,
|
|
668
|
+
role: props.sortable || ctx?.selection.mode.value !== void 0 ? "columnheader" : void 0,
|
|
669
|
+
onClick: props.sortable ? onClick : void 0,
|
|
670
|
+
onKeydown: props.sortable ? onKeydown : void 0
|
|
671
|
+
}), [renderHeadContent({
|
|
672
|
+
isSelector: props.isSelector,
|
|
673
|
+
selectionMode: ctx?.selection.mode.value,
|
|
674
|
+
selectAllState: selectAllState.value,
|
|
675
|
+
selectorAriaLabel: props.selectorAriaLabel,
|
|
676
|
+
onSelectorClick,
|
|
677
|
+
defaultSlot: slots.default
|
|
678
|
+
}), props.sortable && sortDirection.value ? h("span", {
|
|
679
|
+
class: theme.value.sortIcon || void 0,
|
|
680
|
+
"aria-hidden": "true",
|
|
681
|
+
"data-sort": sortDirection.value
|
|
682
|
+
}, sortDirection.value === "asc" ? "↑" : "↓") : null]);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region src/components/TableHeader.vue?vue&type=script&lang.ts
|
|
687
|
+
const tableHeaderThemeDefaults$1 = { classes: { root: "vc-table-header" } };
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region src/components/TableHeader.vue
|
|
690
|
+
var TableHeader_default = defineComponent({
|
|
691
|
+
name: "VCTableHeader",
|
|
692
|
+
inheritAttrs: false,
|
|
693
|
+
props: { ...themableProps() },
|
|
694
|
+
setup(props, { attrs, slots }) {
|
|
695
|
+
const theme = useComponentTheme("tableHeader", useThemeProps(props), tableHeaderThemeDefaults$1);
|
|
696
|
+
return () => h("thead", mergeProps(attrs, { class: theme.value.root || void 0 }), slots.default?.());
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
//#endregion
|
|
700
|
+
//#region src/components/TableRow.vue?vue&type=script&lang.ts
|
|
701
|
+
const tableRowThemeDefaults$1 = { classes: { root: "vc-table-row" } };
|
|
702
|
+
//#endregion
|
|
703
|
+
//#region src/components/TableRow.vue
|
|
704
|
+
var TableRow_default = defineComponent({
|
|
705
|
+
name: "VCTableRow",
|
|
706
|
+
inheritAttrs: false,
|
|
707
|
+
props: {
|
|
708
|
+
/** Current row data — used to resolve `_rowVariant` / `_cellVariants` and to emit `@row-click`. */
|
|
709
|
+
row: {
|
|
710
|
+
type: null,
|
|
711
|
+
default: void 0
|
|
712
|
+
},
|
|
713
|
+
/** Row index — used by row keyboard navigation + focused-row tracking. */
|
|
714
|
+
index: {
|
|
715
|
+
type: Number,
|
|
716
|
+
default: void 0
|
|
717
|
+
},
|
|
718
|
+
/** Mark the row disabled — forwarded to `themeVariant.disabled`. */
|
|
719
|
+
disabled: {
|
|
720
|
+
type: Boolean,
|
|
721
|
+
default: void 0
|
|
722
|
+
},
|
|
723
|
+
/**
|
|
724
|
+
* Manual override for the row's selected state. When set, wins over
|
|
725
|
+
* the auto-resolved selection from `useTable().selection`. Leave
|
|
726
|
+
* undefined to let `<VCTable :selection>` drive selection state.
|
|
727
|
+
*/
|
|
728
|
+
selected: {
|
|
729
|
+
type: Boolean,
|
|
730
|
+
default: void 0
|
|
731
|
+
},
|
|
732
|
+
...themableProps()
|
|
733
|
+
},
|
|
734
|
+
setup(props, { attrs, slots }) {
|
|
735
|
+
const ctx = useTable();
|
|
736
|
+
const rowVariant = computed(() => {
|
|
737
|
+
if (!isObject(props.row)) return null;
|
|
738
|
+
const v = props.row._rowVariant;
|
|
739
|
+
return typeof v === "string" ? v : null;
|
|
740
|
+
});
|
|
741
|
+
const cellVariants = computed(() => {
|
|
742
|
+
if (!isObject(props.row)) return {};
|
|
743
|
+
const v = props.row._cellVariants;
|
|
744
|
+
return isObject(v) ? v : {};
|
|
745
|
+
});
|
|
746
|
+
const focused = computed(() => {
|
|
747
|
+
if (props.index === void 0) return false;
|
|
748
|
+
return ctx?.focusedRow.value === props.index;
|
|
749
|
+
});
|
|
750
|
+
const selectionKey = computed(() => {
|
|
751
|
+
if (props.index === void 0) return -1;
|
|
752
|
+
return ctx?.getRowKey(props.row, props.index) ?? props.index;
|
|
753
|
+
});
|
|
754
|
+
const selectionMode = computed(() => ctx?.selection.mode.value);
|
|
755
|
+
const autoSelected = computed(() => {
|
|
756
|
+
if (props.selected !== void 0) return props.selected;
|
|
757
|
+
if (props.index === void 0) return false;
|
|
758
|
+
return ctx?.selection.isSelected(selectionKey.value) ?? false;
|
|
759
|
+
});
|
|
760
|
+
const themeProps = useThemeProps(props, "disabled");
|
|
761
|
+
const theme = useComponentTheme("tableRow", {
|
|
762
|
+
get themeClass() {
|
|
763
|
+
return themeProps.themeClass;
|
|
764
|
+
},
|
|
765
|
+
get themeVariant() {
|
|
766
|
+
return {
|
|
767
|
+
...themeProps.themeVariant ?? {},
|
|
768
|
+
...rowVariant.value ? { rowVariant: rowVariant.value } : {},
|
|
769
|
+
focused: focused.value,
|
|
770
|
+
selected: autoSelected.value
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}, tableRowThemeDefaults$1);
|
|
774
|
+
if (props.index !== void 0) provideTableRowContext({
|
|
775
|
+
row: toRef(props, "row"),
|
|
776
|
+
index: toRef(props, "index"),
|
|
777
|
+
rowVariant,
|
|
778
|
+
cellVariants,
|
|
779
|
+
focused,
|
|
780
|
+
selectionKey,
|
|
781
|
+
selected: autoSelected
|
|
782
|
+
});
|
|
783
|
+
const selectionActive = computed(() => selectionMode.value !== void 0);
|
|
784
|
+
const isInteractive = computed(() => (ctx?.rowClickable.value || selectionActive.value) && props.index !== void 0 && !props.disabled);
|
|
785
|
+
watch([isInteractive, () => props.index], ([active, idx], [, prevIdx]) => {
|
|
786
|
+
if (!ctx) return;
|
|
787
|
+
if (prevIdx !== void 0 && prevIdx !== idx) ctx.unregisterInteractiveRow(prevIdx);
|
|
788
|
+
if (active && idx !== void 0) ctx.registerInteractiveRow(idx);
|
|
789
|
+
else if (idx !== void 0) ctx.unregisterInteractiveRow(idx);
|
|
790
|
+
}, { immediate: true });
|
|
791
|
+
onBeforeUnmount(() => {
|
|
792
|
+
if (ctx && props.index !== void 0) ctx.unregisterInteractiveRow(props.index);
|
|
793
|
+
});
|
|
794
|
+
const tabindex = computed(() => {
|
|
795
|
+
if (!isInteractive.value) return void 0;
|
|
796
|
+
if (!selectionActive.value) return 0;
|
|
797
|
+
const focusIdx = ctx?.focusedRow.value;
|
|
798
|
+
const interactives = ctx?.interactiveRows.value;
|
|
799
|
+
if (focusIdx !== null && focusIdx !== void 0 && interactives !== void 0 && interactives.has(focusIdx)) return props.index === focusIdx ? 0 : -1;
|
|
800
|
+
if (!interactives || interactives.size === 0) return -1;
|
|
801
|
+
let firstInteractive = null;
|
|
802
|
+
for (const idx of interactives) if (firstInteractive === null || idx < firstInteractive) firstInteractive = idx;
|
|
803
|
+
return props.index === firstInteractive ? 0 : -1;
|
|
804
|
+
});
|
|
805
|
+
function activateSelection(event) {
|
|
806
|
+
if (!selectionActive.value || props.index === void 0) return;
|
|
807
|
+
ctx?.selection.toggle(selectionKey.value, {
|
|
808
|
+
range: event.shiftKey,
|
|
809
|
+
toggle: event.metaKey || event.ctrlKey
|
|
810
|
+
});
|
|
811
|
+
ctx?.setFocusedRow(props.index);
|
|
812
|
+
}
|
|
813
|
+
function onClick(event) {
|
|
814
|
+
if (!isInteractive.value) return;
|
|
815
|
+
const rowEl = event.currentTarget;
|
|
816
|
+
if (filterRowClickEvent(event, rowEl)) return;
|
|
817
|
+
if (props.index === void 0) return;
|
|
818
|
+
ctx?.setFocusedRow(props.index);
|
|
819
|
+
activateSelection(event);
|
|
820
|
+
if (ctx?.rowClickable.value) ctx?.emitRowClick(props.row, props.index, event);
|
|
821
|
+
}
|
|
822
|
+
function onKeydown(event) {
|
|
823
|
+
if (!isInteractive.value || props.index === void 0) return;
|
|
824
|
+
const i = props.index;
|
|
825
|
+
const interactives = ctx?.interactiveRows.value;
|
|
826
|
+
const sorted = interactives && interactives.size > 0 ? Array.from(interactives).sort((a, b) => a - b) : [];
|
|
827
|
+
const posInSorted = sorted.indexOf(i);
|
|
828
|
+
const moveTo = (target) => {
|
|
829
|
+
event.preventDefault();
|
|
830
|
+
if (sorted.length === 0) return;
|
|
831
|
+
const nextIdx = sorted[Math.max(0, Math.min(sorted.length - 1, target))];
|
|
832
|
+
ctx?.setFocusedRow(nextIdx);
|
|
833
|
+
const tr = event.currentTarget;
|
|
834
|
+
if (!tr) return;
|
|
835
|
+
nextTick(() => {
|
|
836
|
+
const direction = nextIdx > i ? "nextElementSibling" : "previousElementSibling";
|
|
837
|
+
let sibling = tr[direction];
|
|
838
|
+
while (sibling) {
|
|
839
|
+
if (sibling instanceof globalThis.HTMLElement && sibling.tagName === "TR" && sibling.getAttribute("tabindex") === "0") {
|
|
840
|
+
sibling.focus();
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
sibling = sibling[direction];
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
if (event.shiftKey && selectionActive.value && selectionMode.value === "multi" && ctx) {
|
|
847
|
+
if (ctx.selection.rangeAnchor.value === null) {
|
|
848
|
+
const currentRow = ctx.data.value[i];
|
|
849
|
+
if (currentRow !== void 0) ctx.selection.rangeAnchor.value = ctx.getRowKey(currentRow, i);
|
|
850
|
+
}
|
|
851
|
+
const targetRow = ctx.data.value[nextIdx];
|
|
852
|
+
if (targetRow !== void 0) {
|
|
853
|
+
const targetKey = ctx.getRowKey(targetRow, nextIdx);
|
|
854
|
+
ctx.selection.toggle(targetKey, { range: true });
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
if (event.key === "ArrowDown") moveTo(posInSorted + 1);
|
|
859
|
+
else if (event.key === "ArrowUp") moveTo(posInSorted - 1);
|
|
860
|
+
else if (event.key === "Home") moveTo(0);
|
|
861
|
+
else if (event.key === "End") moveTo(sorted.length - 1);
|
|
862
|
+
else if (event.key === "Enter" || event.key === " ") {
|
|
863
|
+
event.preventDefault();
|
|
864
|
+
activateSelection(event);
|
|
865
|
+
if (ctx?.rowClickable.value) ctx?.emitRowClick(props.row, i, event);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function onFocus() {
|
|
869
|
+
if (!isInteractive.value || props.index === void 0) return;
|
|
870
|
+
ctx?.setFocusedRow(props.index);
|
|
871
|
+
}
|
|
872
|
+
return () => {
|
|
873
|
+
const selectionAttrs = selectionActive.value ? {
|
|
874
|
+
role: "row",
|
|
875
|
+
"aria-selected": autoSelected.value ? "true" : "false"
|
|
876
|
+
} : {};
|
|
877
|
+
return h("tr", mergeProps(attrs, {
|
|
878
|
+
class: theme.value.root || void 0,
|
|
879
|
+
tabindex: tabindex.value,
|
|
880
|
+
"data-row-variant": rowVariant.value || void 0,
|
|
881
|
+
...selectionAttrs,
|
|
882
|
+
onClick: isInteractive.value ? onClick : void 0,
|
|
883
|
+
onKeydown: isInteractive.value ? onKeydown : void 0,
|
|
884
|
+
onFocus: isInteractive.value ? onFocus : void 0
|
|
885
|
+
}), slots.default?.());
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
//#endregion
|
|
890
|
+
//#region src/utils/auto-render.ts
|
|
891
|
+
/**
|
|
892
|
+
* Recursively check whether `nodes` (a slot return) contains a vnode
|
|
893
|
+
* whose `type` equals `target`. Recurses into Vue `Fragment` vnodes so
|
|
894
|
+
* `<template v-if>` / `<template v-for>` wrapping around a part
|
|
895
|
+
* doesn't hide it from the auto-render check.
|
|
896
|
+
*
|
|
897
|
+
* Used by `<VCTable>` and `<VCTableLite>` to detect whether the
|
|
898
|
+
* consumer wrote a manual `<VCTableHeader>` / `<VCTableBody>` in the
|
|
899
|
+
* default slot — if not, and `:columns` is set, the table renders the
|
|
900
|
+
* missing band itself.
|
|
901
|
+
*/
|
|
902
|
+
function containsComponent(nodes, target) {
|
|
903
|
+
if (nodes == null || nodes === false) return false;
|
|
904
|
+
if (Array.isArray(nodes)) {
|
|
905
|
+
for (const child of nodes) if (containsComponent(child, target)) return true;
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
if (typeof nodes !== "object") return false;
|
|
909
|
+
const v = nodes;
|
|
910
|
+
if (v.type === target) return true;
|
|
911
|
+
if (v.type === Fragment) return containsComponent(v.children, target);
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Flatten a slot return into a single-level array, unwrapping
|
|
916
|
+
* `Fragment` vnodes so partition logic operates on a flat sequence.
|
|
917
|
+
* Fragments only serve as grouping markers and lose no DOM semantics
|
|
918
|
+
* when flattened.
|
|
919
|
+
*/
|
|
920
|
+
function flattenSlot(nodes) {
|
|
921
|
+
if (nodes == null || nodes === false) return [];
|
|
922
|
+
if (Array.isArray(nodes)) return nodes.flatMap(flattenSlot);
|
|
923
|
+
if (typeof nodes !== "object") return [nodes];
|
|
924
|
+
const v = nodes;
|
|
925
|
+
if (v.type === Fragment) return flattenSlot(v.children);
|
|
926
|
+
return [nodes];
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Partition slot children into vnodes that should render BEFORE the
|
|
930
|
+
* auto-body and vnodes that should render AFTER it. Body precedence
|
|
931
|
+
* rules:
|
|
932
|
+
*
|
|
933
|
+
* <thead> ← always first
|
|
934
|
+
* <tbody> ← auto-rendered band
|
|
935
|
+
* <VCTableEmpty> ← own <tbody>, gated on empty data
|
|
936
|
+
* <VCTableLoading> ← own <tbody>, gated on busy
|
|
937
|
+
* <tfoot> ← must come last (HTML5 says SHOULD; matches
|
|
938
|
+
* browser visual placement so the source order
|
|
939
|
+
* lines up with the rendered order)
|
|
940
|
+
*
|
|
941
|
+
* Only `<VCTableFooter>` is partitioned to "after"; everything else
|
|
942
|
+
* keeps source order in "before".
|
|
943
|
+
*/
|
|
944
|
+
function partitionBeforeAfterBody(nodes) {
|
|
945
|
+
const flat = flattenSlot(nodes);
|
|
946
|
+
const before = [];
|
|
947
|
+
const after = [];
|
|
948
|
+
for (const n of flat) if (n && typeof n === "object" && n.type === TableFooter_default) after.push(n);
|
|
949
|
+
else before.push(n);
|
|
950
|
+
return {
|
|
951
|
+
before,
|
|
952
|
+
after
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Compose the inner children of a `<table>` for the driver auto-render
|
|
957
|
+
* path (plan 033 v0.2-B). Returns the array of vnodes in the order
|
|
958
|
+
* the table should render them:
|
|
959
|
+
*
|
|
960
|
+
* caption? · colgroup? · autoHeader? · slotChildren · autoBody?
|
|
961
|
+
*
|
|
962
|
+
* Header / body auto-render fires when `columns.length > 0` AND the
|
|
963
|
+
* matching part isn't already present in `slotChildren` (Fragment-
|
|
964
|
+
* aware via `containsComponent`).
|
|
965
|
+
*/
|
|
966
|
+
function composeTableInner(opts) {
|
|
967
|
+
const { cols, slotChildren, captionSlot, colgroupSlot } = opts;
|
|
968
|
+
const inner = [];
|
|
969
|
+
if (captionSlot) inner.push(h("caption", null, captionSlot()));
|
|
970
|
+
if (colgroupSlot) inner.push(h("colgroup", null, colgroupSlot()));
|
|
971
|
+
const autoRender = cols.length > 0;
|
|
972
|
+
const hasHeader = autoRender && containsComponent(slotChildren, TableHeader_default);
|
|
973
|
+
const hasBody = autoRender && containsComponent(slotChildren, TableBody_default);
|
|
974
|
+
if (autoRender && !hasHeader) inner.push(h(TableHeader_default, null, () => h(TableRow_default, null, () => cols.map((col) => h(TableHeadCell_default, {
|
|
975
|
+
key: col.key,
|
|
976
|
+
columnKey: col.key,
|
|
977
|
+
sortable: col.sortable,
|
|
978
|
+
stickyColumn: col.stickyColumn,
|
|
979
|
+
title: col.headerTitle,
|
|
980
|
+
abbr: col.headerAbbr,
|
|
981
|
+
class: [col.class, col.headerClass]
|
|
982
|
+
}, () => col.label)))));
|
|
983
|
+
const { before, after } = partitionBeforeAfterBody(slotChildren);
|
|
984
|
+
if (before.length > 0) inner.push(before);
|
|
985
|
+
if (autoRender && !hasBody) inner.push(h(TableBody_default, null, { row: ({ row, index }) => h(TableRow_default, {
|
|
986
|
+
row,
|
|
987
|
+
index
|
|
988
|
+
}, () => cols.map((col) => h(TableCell_default, {
|
|
989
|
+
key: col.key,
|
|
990
|
+
columnKey: col.key,
|
|
991
|
+
isRowHeader: col.isRowHeader,
|
|
992
|
+
stickyColumn: col.stickyColumn,
|
|
993
|
+
dataLabel: col.label,
|
|
994
|
+
class: [col.class, col.cellClass]
|
|
995
|
+
}))) }));
|
|
996
|
+
if (after.length > 0) inner.push(after);
|
|
997
|
+
return inner;
|
|
998
|
+
}
|
|
999
|
+
//#endregion
|
|
1000
|
+
//#region src/utils/sort-rows.ts
|
|
1001
|
+
/**
|
|
1002
|
+
* Client-side row sort (plan 033 v1.x-B).
|
|
1003
|
+
*
|
|
1004
|
+
* Returns a new array — the input is untouched. Honors:
|
|
1005
|
+
*
|
|
1006
|
+
* - **Sort key**: `column.sortByFormatted` → use `formatter` output;
|
|
1007
|
+
* else → use `accessor` resolved value (default, matches v0.1
|
|
1008
|
+
* `resolveCellValue`).
|
|
1009
|
+
* - **Comparator**: `column.sortFn(a, b) => number` when present;
|
|
1010
|
+
* else a built-in compare that handles numbers, Dates, booleans,
|
|
1011
|
+
* and uses `localeCompare` (with `numeric: true`) for everything
|
|
1012
|
+
* else — so `'item 2'` sorts before `'item 10'`.
|
|
1013
|
+
* - **Null handling**: `null` / `undefined` sort LAST regardless of
|
|
1014
|
+
* direction. Per-column `nullsFirst: true` floats them to the top.
|
|
1015
|
+
* - **Multi-key tie-break**: descriptors are applied in order. The
|
|
1016
|
+
* first non-zero comparison wins.
|
|
1017
|
+
* - **Stability**: relies on `Array.prototype.sort`'s stable
|
|
1018
|
+
* guarantee (ES2019+, every supported runtime).
|
|
1019
|
+
*
|
|
1020
|
+
* Empty `sorts` returns a shallow copy — never the input reference
|
|
1021
|
+
* itself, so callers can safely mutate the result.
|
|
1022
|
+
*/
|
|
1023
|
+
function sortRows(rows, options) {
|
|
1024
|
+
if (options.sorts.length === 0) return rows.slice();
|
|
1025
|
+
const columnByKey = /* @__PURE__ */ new Map();
|
|
1026
|
+
for (const col of options.columns) columnByKey.set(col.key, col);
|
|
1027
|
+
return rows.slice().sort((a, b) => {
|
|
1028
|
+
for (const sort of options.sorts) {
|
|
1029
|
+
const column = columnByKey.get(sort.key);
|
|
1030
|
+
if (!column) continue;
|
|
1031
|
+
const cmp = compareWithColumn(a, b, column, sort.direction);
|
|
1032
|
+
if (cmp !== 0) return cmp;
|
|
1033
|
+
}
|
|
1034
|
+
return 0;
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
function compareWithColumn(a, b, column, direction) {
|
|
1038
|
+
const av = resolveSortValue(column, a);
|
|
1039
|
+
const bv = resolveSortValue(column, b);
|
|
1040
|
+
const aMissing = av === null || av === void 0;
|
|
1041
|
+
const bMissing = bv === null || bv === void 0;
|
|
1042
|
+
if (aMissing && bMissing) return 0;
|
|
1043
|
+
if (aMissing) return column.nullsFirst ? -1 : 1;
|
|
1044
|
+
if (bMissing) return column.nullsFirst ? 1 : -1;
|
|
1045
|
+
const cmp = column.sortFn ? column.sortFn(av, bv) : defaultCompare(av, bv);
|
|
1046
|
+
return direction === "asc" ? cmp : -cmp;
|
|
1047
|
+
}
|
|
1048
|
+
function resolveSortValue(column, row) {
|
|
1049
|
+
const value = resolveCellValue(column, row);
|
|
1050
|
+
if (!column.sortByFormatted || !column.formatter) return value;
|
|
1051
|
+
return column.formatter({
|
|
1052
|
+
value,
|
|
1053
|
+
key: column.key,
|
|
1054
|
+
row
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Default comparator. Locale-aware for strings, numeric for numbers,
|
|
1059
|
+
* chronological for Dates. Falls back to coerced-string compare so
|
|
1060
|
+
* mixed-type columns don't throw.
|
|
1061
|
+
*/
|
|
1062
|
+
function defaultCompare(a, b) {
|
|
1063
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
1064
|
+
if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
|
|
1065
|
+
if (typeof a === "boolean" && typeof b === "boolean") {
|
|
1066
|
+
if (a === b) return 0;
|
|
1067
|
+
return a ? 1 : -1;
|
|
1068
|
+
}
|
|
1069
|
+
const as = typeof a === "string" ? a : String(a);
|
|
1070
|
+
const bs = typeof b === "string" ? b : String(b);
|
|
1071
|
+
return as.localeCompare(bs, void 0, { numeric: true });
|
|
1072
|
+
}
|
|
1073
|
+
//#endregion
|
|
1074
|
+
//#region src/components/Table.vue?vue&type=script&lang.ts
|
|
1075
|
+
const tableThemeDefaults$1 = { classes: {
|
|
1076
|
+
root: "vc-table",
|
|
1077
|
+
scrollContainer: "vc-table-scroll-container"
|
|
1078
|
+
} };
|
|
1079
|
+
//#endregion
|
|
1080
|
+
//#region src/components/Table.vue
|
|
1081
|
+
var Table_default = defineComponent({
|
|
1082
|
+
name: "VCTable",
|
|
1083
|
+
inheritAttrs: false,
|
|
1084
|
+
props: {
|
|
1085
|
+
/** Row data array. */
|
|
1086
|
+
data: {
|
|
1087
|
+
type: Array,
|
|
1088
|
+
default: () => []
|
|
1089
|
+
},
|
|
1090
|
+
/** Column definitions (TableColumn or bare-string shorthand). When omitted, columns are derived from `Object.keys(data[0])`. */
|
|
1091
|
+
columns: {
|
|
1092
|
+
type: Array,
|
|
1093
|
+
default: void 0
|
|
1094
|
+
},
|
|
1095
|
+
/** Busy flag — drives `aria-busy` on the `<table>` and gates the loading-band render. */
|
|
1096
|
+
busy: {
|
|
1097
|
+
type: Boolean,
|
|
1098
|
+
default: false
|
|
1099
|
+
},
|
|
1100
|
+
/**
|
|
1101
|
+
* Controlled sort state as `SortDescriptor[]`. Use `v-model:sort`.
|
|
1102
|
+
* Empty array means "no sort". Since v1.x-B this is always an
|
|
1103
|
+
* array (BREAKING change from v0.1's `{ key, direction } | null`).
|
|
1104
|
+
* Single-column sort is just an array of length 0 or 1.
|
|
1105
|
+
*/
|
|
1106
|
+
sort: {
|
|
1107
|
+
type: Array,
|
|
1108
|
+
default: () => []
|
|
1109
|
+
},
|
|
1110
|
+
/** When `true`, the cycle skips the `null` step: `null → asc → desc → asc`. */
|
|
1111
|
+
mustSort: {
|
|
1112
|
+
type: Boolean,
|
|
1113
|
+
default: false
|
|
1114
|
+
},
|
|
1115
|
+
/**
|
|
1116
|
+
* Enable multi-column sort. When `true`, Shift-click on a
|
|
1117
|
+
* sortable header adds it as a secondary descriptor (or cycles
|
|
1118
|
+
* its direction if already present). Plain click without Shift
|
|
1119
|
+
* replaces multi-sort with single-sort of the clicked column.
|
|
1120
|
+
* Default `false` — Shift-click behaves identically to plain
|
|
1121
|
+
* click and the sort array stays length 0–1.
|
|
1122
|
+
*/
|
|
1123
|
+
multiSort: {
|
|
1124
|
+
type: Boolean,
|
|
1125
|
+
default: false
|
|
1126
|
+
},
|
|
1127
|
+
/**
|
|
1128
|
+
* Maximum number of sort keys retained when `:multi-sort` is on.
|
|
1129
|
+
* Adding a key past the cap drops the oldest descriptor. `0`
|
|
1130
|
+
* means unlimited. Default `3` (matches Excel / bvnext / TanStack
|
|
1131
|
+
* convention).
|
|
1132
|
+
*/
|
|
1133
|
+
maxSortKeys: {
|
|
1134
|
+
type: Number,
|
|
1135
|
+
default: 3
|
|
1136
|
+
},
|
|
1137
|
+
/**
|
|
1138
|
+
* When `true`, the table reorders `:data` internally using
|
|
1139
|
+
* `accessor` (or `formatter` output if `column.sortByFormatted`),
|
|
1140
|
+
* honoring `column.sortFn` / `nullsFirst` if set. `v-model:sort`
|
|
1141
|
+
* still emits intent so consumers stay observable. Default
|
|
1142
|
+
* `false` — v0.1 controlled-sort behaviour preserved.
|
|
1143
|
+
*/
|
|
1144
|
+
clientSort: {
|
|
1145
|
+
type: Boolean,
|
|
1146
|
+
default: false
|
|
1147
|
+
},
|
|
1148
|
+
/** Wrap the `<table>` in an overflow scroll container. */
|
|
1149
|
+
scrollable: {
|
|
1150
|
+
type: Boolean,
|
|
1151
|
+
default: false
|
|
1152
|
+
},
|
|
1153
|
+
/** When `:scrollable`, sticks the `<thead>` to the top of the scroll container. */
|
|
1154
|
+
stickyHeader: {
|
|
1155
|
+
type: Boolean,
|
|
1156
|
+
default: false
|
|
1157
|
+
},
|
|
1158
|
+
/** When `:scrollable`, applied as `max-height` on the scroll container (any CSS length, e.g. `'24rem'`). */
|
|
1159
|
+
maxHeight: {
|
|
1160
|
+
type: String,
|
|
1161
|
+
default: void 0
|
|
1162
|
+
},
|
|
1163
|
+
/** Opt-in row-click affordance — adds `tabindex` + cursor-pointer on every row and emits `@row-click`. */
|
|
1164
|
+
rowClickable: {
|
|
1165
|
+
type: Boolean,
|
|
1166
|
+
default: false
|
|
1167
|
+
},
|
|
1168
|
+
/**
|
|
1169
|
+
* Row selection mode (plan 033 v1.x-A). When set, the table flips
|
|
1170
|
+
* to ARIA `role="grid"` (+ `aria-multiselectable` for multi) and
|
|
1171
|
+
* rows render with `aria-selected`. `undefined` keeps the v0.1
|
|
1172
|
+
* plain-table semantics.
|
|
1173
|
+
*/
|
|
1174
|
+
selectionMode: {
|
|
1175
|
+
type: String,
|
|
1176
|
+
default: void 0
|
|
1177
|
+
},
|
|
1178
|
+
/**
|
|
1179
|
+
* Controlled selection state. Use `v-model:selection`. Type is
|
|
1180
|
+
* `string|number` for single mode, `(string|number)[]` for multi.
|
|
1181
|
+
* `null` clears the selection.
|
|
1182
|
+
*/
|
|
1183
|
+
selection: {
|
|
1184
|
+
type: [
|
|
1185
|
+
String,
|
|
1186
|
+
Number,
|
|
1187
|
+
Array,
|
|
1188
|
+
null
|
|
1189
|
+
],
|
|
1190
|
+
default: null
|
|
1191
|
+
},
|
|
1192
|
+
/**
|
|
1193
|
+
* Resolve a row's selection key. Defaults to `row.id` (falling
|
|
1194
|
+
* back to the row index when absent). Pass a function for richer
|
|
1195
|
+
* mappings (e.g. `(row) => row.uuid`).
|
|
1196
|
+
*/
|
|
1197
|
+
getRowKey: {
|
|
1198
|
+
type: Function,
|
|
1199
|
+
default: void 0
|
|
1200
|
+
},
|
|
1201
|
+
/**
|
|
1202
|
+
* Stacked responsive mode (v0.2-D). When `true`, sets
|
|
1203
|
+
* `data-responsive="true"` on the `<table>` so the structural CSS
|
|
1204
|
+
* (and any theme-specific overrides) can collapse the table into
|
|
1205
|
+
* per-row cards at narrow viewports. Uses each cell's `data-label`
|
|
1206
|
+
* as the per-row column label.
|
|
1207
|
+
*/
|
|
1208
|
+
responsive: {
|
|
1209
|
+
type: Boolean,
|
|
1210
|
+
default: false
|
|
1211
|
+
},
|
|
1212
|
+
/** Density shorthand for `themeVariant.density`. */
|
|
1213
|
+
density: {
|
|
1214
|
+
type: String,
|
|
1215
|
+
default: void 0
|
|
1216
|
+
},
|
|
1217
|
+
/** Alternating row backgrounds — shorthand for `themeVariant.striped`. */
|
|
1218
|
+
striped: {
|
|
1219
|
+
type: Boolean,
|
|
1220
|
+
default: void 0
|
|
1221
|
+
},
|
|
1222
|
+
/** Cell borders — shorthand for `themeVariant.bordered`. */
|
|
1223
|
+
bordered: {
|
|
1224
|
+
type: Boolean,
|
|
1225
|
+
default: void 0
|
|
1226
|
+
},
|
|
1227
|
+
/** Row hover highlight — shorthand for `themeVariant.hover`. */
|
|
1228
|
+
hover: {
|
|
1229
|
+
type: Boolean,
|
|
1230
|
+
default: void 0
|
|
1231
|
+
},
|
|
1232
|
+
/** HTML tag to render. */
|
|
1233
|
+
tag: {
|
|
1234
|
+
type: String,
|
|
1235
|
+
default: "table"
|
|
1236
|
+
},
|
|
1237
|
+
...themableProps()
|
|
1238
|
+
},
|
|
1239
|
+
emits: [
|
|
1240
|
+
"update:sort",
|
|
1241
|
+
"update:selection",
|
|
1242
|
+
"row-click"
|
|
1243
|
+
],
|
|
1244
|
+
slots: Object,
|
|
1245
|
+
setup(props, { attrs, slots, emit }) {
|
|
1246
|
+
const theme = useComponentTheme("table", useThemeProps(props, "density", "striped", "bordered", "hover", "stickyHeader"), tableThemeDefaults$1);
|
|
1247
|
+
const dataRef = toRef(props, "data");
|
|
1248
|
+
const rawColumns = toRef(props, "columns");
|
|
1249
|
+
const columns = computed(() => normalizeColumns(rawColumns.value, dataRef.value));
|
|
1250
|
+
const sortSource = toRef(props, "sort");
|
|
1251
|
+
const mustSortRef = toRef(props, "mustSort");
|
|
1252
|
+
const maxSortKeysRef = toRef(props, "maxSortKeys");
|
|
1253
|
+
const sortMachine = useSortMachine({
|
|
1254
|
+
source: sortSource,
|
|
1255
|
+
columns,
|
|
1256
|
+
mustSort: mustSortRef,
|
|
1257
|
+
maxSortKeys: maxSortKeysRef,
|
|
1258
|
+
emit: (next) => emit("update:sort", next)
|
|
1259
|
+
});
|
|
1260
|
+
const visibleData = computed(() => {
|
|
1261
|
+
if (!props.clientSort || sortMachine.state.value.length === 0) return dataRef.value;
|
|
1262
|
+
return sortRows(dataRef.value, {
|
|
1263
|
+
columns: columns.value,
|
|
1264
|
+
sorts: sortMachine.state.value
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
const childCellCount = ref(0);
|
|
1268
|
+
provideHeadCellCountContext({
|
|
1269
|
+
register: () => {
|
|
1270
|
+
childCellCount.value += 1;
|
|
1271
|
+
},
|
|
1272
|
+
unregister: () => {
|
|
1273
|
+
childCellCount.value = Math.max(0, childCellCount.value - 1);
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
const colspan = computed(() => {
|
|
1277
|
+
if (columns.value.length > 0) return columns.value.length;
|
|
1278
|
+
return Math.max(1, childCellCount.value);
|
|
1279
|
+
});
|
|
1280
|
+
watch(() => columns.value.length > 0, (hasColumns) => {
|
|
1281
|
+
if (hasColumns) childCellCount.value = 0;
|
|
1282
|
+
});
|
|
1283
|
+
const focusedRow = ref(null);
|
|
1284
|
+
const setFocusedRow = (index) => {
|
|
1285
|
+
focusedRow.value = index;
|
|
1286
|
+
};
|
|
1287
|
+
const emitRowClick = (row, index, event) => {
|
|
1288
|
+
emit("row-click", row, index, event);
|
|
1289
|
+
};
|
|
1290
|
+
const wrapperEl = ref(null);
|
|
1291
|
+
const interactiveRows = shallowRef(/* @__PURE__ */ new Set());
|
|
1292
|
+
const registerInteractiveRow = (index) => {
|
|
1293
|
+
if (interactiveRows.value.has(index)) return;
|
|
1294
|
+
interactiveRows.value.add(index);
|
|
1295
|
+
triggerRef(interactiveRows);
|
|
1296
|
+
};
|
|
1297
|
+
const unregisterInteractiveRow = (index) => {
|
|
1298
|
+
if (!interactiveRows.value.has(index)) return;
|
|
1299
|
+
interactiveRows.value.delete(index);
|
|
1300
|
+
triggerRef(interactiveRows);
|
|
1301
|
+
};
|
|
1302
|
+
const selectionMode = computed(() => props.selectionMode);
|
|
1303
|
+
const selectionValue = computed(() => props.selection);
|
|
1304
|
+
const getRowKey = (row, index) => {
|
|
1305
|
+
const custom = props.getRowKey;
|
|
1306
|
+
if (typeof custom === "function") return custom(row, index);
|
|
1307
|
+
if (row && typeof row === "object") {
|
|
1308
|
+
const { id } = row;
|
|
1309
|
+
if (typeof id === "string" || typeof id === "number") return id;
|
|
1310
|
+
}
|
|
1311
|
+
return index;
|
|
1312
|
+
};
|
|
1313
|
+
const selection = useRowSelectionMachine({
|
|
1314
|
+
mode: selectionMode,
|
|
1315
|
+
value: selectionValue,
|
|
1316
|
+
emit: (next) => emit("update:selection", next),
|
|
1317
|
+
keyAt: (index) => {
|
|
1318
|
+
const view = visibleData.value;
|
|
1319
|
+
if (index < 0 || index >= view.length) return void 0;
|
|
1320
|
+
return getRowKey(view[index], index);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
provideTableContext({
|
|
1324
|
+
data: visibleData,
|
|
1325
|
+
busy: toRef(props, "busy"),
|
|
1326
|
+
columns,
|
|
1327
|
+
sort: sortMachine.state,
|
|
1328
|
+
setSort: (key, opts) => sortMachine.setSort(key, {
|
|
1329
|
+
...opts,
|
|
1330
|
+
append: props.multiSort ? opts?.append : false
|
|
1331
|
+
}),
|
|
1332
|
+
setSortState: sortMachine.setState,
|
|
1333
|
+
maxSortKeys: maxSortKeysRef,
|
|
1334
|
+
supportsSortMutation: true,
|
|
1335
|
+
rowClickable: toRef(props, "rowClickable"),
|
|
1336
|
+
focusedRow,
|
|
1337
|
+
setFocusedRow,
|
|
1338
|
+
selection,
|
|
1339
|
+
getRowKey,
|
|
1340
|
+
interactiveRows,
|
|
1341
|
+
registerInteractiveRow,
|
|
1342
|
+
unregisterInteractiveRow,
|
|
1343
|
+
colspan,
|
|
1344
|
+
emitRowClick,
|
|
1345
|
+
wrapperEl
|
|
1346
|
+
});
|
|
1347
|
+
const slotProps = computed(() => ({
|
|
1348
|
+
data: visibleData.value,
|
|
1349
|
+
busy: props.busy,
|
|
1350
|
+
columns: columns.value,
|
|
1351
|
+
sort: sortMachine.state.value,
|
|
1352
|
+
setSort: sortMachine.setSort
|
|
1353
|
+
}));
|
|
1354
|
+
const setWrapperRef = (el) => {
|
|
1355
|
+
wrapperEl.value = el ?? null;
|
|
1356
|
+
};
|
|
1357
|
+
return () => {
|
|
1358
|
+
const inner = composeTableInner({
|
|
1359
|
+
cols: columns.value,
|
|
1360
|
+
slotChildren: slots.default?.(slotProps.value),
|
|
1361
|
+
captionSlot: slots.caption,
|
|
1362
|
+
colgroupSlot: slots.colgroup
|
|
1363
|
+
});
|
|
1364
|
+
const mode = selectionMode.value;
|
|
1365
|
+
const selectionAttrs = mode === void 0 ? {} : {
|
|
1366
|
+
role: "grid",
|
|
1367
|
+
...mode === "multi" ? { "aria-multiselectable": "true" } : {}
|
|
1368
|
+
};
|
|
1369
|
+
const tableNode = h(props.tag, mergeProps(attrs, {
|
|
1370
|
+
class: theme.value.root || void 0,
|
|
1371
|
+
"aria-busy": props.busy ? "true" : void 0,
|
|
1372
|
+
"data-responsive": props.responsive ? "true" : void 0,
|
|
1373
|
+
...selectionAttrs
|
|
1374
|
+
}), inner);
|
|
1375
|
+
const wrapper = h("div", {
|
|
1376
|
+
ref: setWrapperRef,
|
|
1377
|
+
class: "vc-table-wrapper",
|
|
1378
|
+
style: { position: "relative" }
|
|
1379
|
+
}, [tableNode]);
|
|
1380
|
+
if (!props.scrollable) return wrapper;
|
|
1381
|
+
return h("div", {
|
|
1382
|
+
class: theme.value.scrollContainer || void 0,
|
|
1383
|
+
style: props.maxHeight ? {
|
|
1384
|
+
maxHeight: props.maxHeight,
|
|
1385
|
+
overflow: "auto"
|
|
1386
|
+
} : { overflow: "auto" }
|
|
1387
|
+
}, [wrapper]);
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
//#endregion
|
|
1392
|
+
//#region src/components/TableLite.vue?vue&type=script&lang.ts
|
|
1393
|
+
const tableLiteThemeDefaults = { classes: {
|
|
1394
|
+
root: "vc-table",
|
|
1395
|
+
scrollContainer: "vc-table-scroll-container"
|
|
1396
|
+
} };
|
|
1397
|
+
/**
|
|
1398
|
+
* Slim sibling of `<VCTable>` — same columns driver + theme system +
|
|
1399
|
+
* auto-render, but with the sort + row-click + keyboard-nav machinery
|
|
1400
|
+
* stripped out (plan 033 v0.2-C). Consumers who want their own state
|
|
1401
|
+
* plumbing (e.g. tanstack-table on top) import this instead of the
|
|
1402
|
+
* full `<VCTable>`. Lite-only consumers tree-shake `useSortMachine`
|
|
1403
|
+
* out of their bundle.
|
|
1404
|
+
*
|
|
1405
|
+
* Provides the same `TableContext` shape so child components
|
|
1406
|
+
* (`<VCTableRow>`, `<VCTableHeadCell>`, …) work identically — the
|
|
1407
|
+
* sort / row-click hooks are wired to no-ops, so sortable headers and
|
|
1408
|
+
* `:row-clickable` rows visually behave as if the consumer hadn't
|
|
1409
|
+
* opted in.
|
|
1410
|
+
*/
|
|
1411
|
+
const tableLiteProps = {
|
|
1412
|
+
/** Row data array. */
|
|
1413
|
+
data: {
|
|
1414
|
+
type: Array,
|
|
1415
|
+
default: () => []
|
|
1416
|
+
},
|
|
1417
|
+
/** Column definitions (TableColumn or bare-string shorthand). When omitted, columns are derived from `Object.keys(data[0])`. */
|
|
1418
|
+
columns: {
|
|
1419
|
+
type: Array,
|
|
1420
|
+
default: void 0
|
|
1421
|
+
},
|
|
1422
|
+
/** Busy flag — drives `aria-busy` on the `<table>` and gates the loading-band render. */
|
|
1423
|
+
busy: {
|
|
1424
|
+
type: Boolean,
|
|
1425
|
+
default: false
|
|
1426
|
+
},
|
|
1427
|
+
/** Wrap the `<table>` in an overflow scroll container. */
|
|
1428
|
+
scrollable: {
|
|
1429
|
+
type: Boolean,
|
|
1430
|
+
default: false
|
|
1431
|
+
},
|
|
1432
|
+
/** When `:scrollable`, sticks the `<thead>` to the top of the scroll container. */
|
|
1433
|
+
stickyHeader: {
|
|
1434
|
+
type: Boolean,
|
|
1435
|
+
default: false
|
|
1436
|
+
},
|
|
1437
|
+
/** When `:scrollable`, applied as `max-height` on the scroll container. */
|
|
1438
|
+
maxHeight: {
|
|
1439
|
+
type: String,
|
|
1440
|
+
default: void 0
|
|
1441
|
+
},
|
|
1442
|
+
/** Stacked responsive mode opt-in. Same shape as `<VCTable :responsive>`. */
|
|
1443
|
+
responsive: {
|
|
1444
|
+
type: Boolean,
|
|
1445
|
+
default: false
|
|
1446
|
+
},
|
|
1447
|
+
/** Density shorthand for `themeVariant.density`. */
|
|
1448
|
+
density: {
|
|
1449
|
+
type: String,
|
|
1450
|
+
default: void 0
|
|
1451
|
+
},
|
|
1452
|
+
/** Alternating row backgrounds — shorthand for `themeVariant.striped`. */
|
|
1453
|
+
striped: {
|
|
1454
|
+
type: Boolean,
|
|
1455
|
+
default: void 0
|
|
1456
|
+
},
|
|
1457
|
+
/** Cell borders — shorthand for `themeVariant.bordered`. */
|
|
1458
|
+
bordered: {
|
|
1459
|
+
type: Boolean,
|
|
1460
|
+
default: void 0
|
|
1461
|
+
},
|
|
1462
|
+
/** Row hover highlight — shorthand for `themeVariant.hover`. */
|
|
1463
|
+
hover: {
|
|
1464
|
+
type: Boolean,
|
|
1465
|
+
default: void 0
|
|
1466
|
+
},
|
|
1467
|
+
/** HTML tag to render. */
|
|
1468
|
+
tag: {
|
|
1469
|
+
type: String,
|
|
1470
|
+
default: "table"
|
|
1471
|
+
},
|
|
1472
|
+
...themableProps()
|
|
1473
|
+
};
|
|
1474
|
+
const NOOP_SORT_STATE = ref([]);
|
|
1475
|
+
const NOOP_SELECTION_MODE = computed(() => void 0);
|
|
1476
|
+
const NOOP_SELECTION_VALUE = computed(() => null);
|
|
1477
|
+
//#endregion
|
|
1478
|
+
//#region src/components/TableLite.vue
|
|
1479
|
+
var TableLite_default = defineComponent({
|
|
1480
|
+
name: "VCTableLite",
|
|
1481
|
+
inheritAttrs: false,
|
|
1482
|
+
props: tableLiteProps,
|
|
1483
|
+
slots: Object,
|
|
1484
|
+
setup(props, { attrs, slots }) {
|
|
1485
|
+
const theme = useComponentTheme("table", useThemeProps(props, "density", "striped", "bordered", "hover", "stickyHeader"), tableLiteThemeDefaults);
|
|
1486
|
+
const dataRef = toRef(props, "data");
|
|
1487
|
+
const rawColumns = toRef(props, "columns");
|
|
1488
|
+
const columns = computed(() => normalizeColumns(rawColumns.value, dataRef.value));
|
|
1489
|
+
const childCellCount = ref(0);
|
|
1490
|
+
provideHeadCellCountContext({
|
|
1491
|
+
register: () => {
|
|
1492
|
+
childCellCount.value += 1;
|
|
1493
|
+
},
|
|
1494
|
+
unregister: () => {
|
|
1495
|
+
childCellCount.value = Math.max(0, childCellCount.value - 1);
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
const colspan = computed(() => {
|
|
1499
|
+
if (columns.value.length > 0) return columns.value.length;
|
|
1500
|
+
return Math.max(1, childCellCount.value);
|
|
1501
|
+
});
|
|
1502
|
+
watch(() => columns.value.length > 0, (hasColumns) => {
|
|
1503
|
+
if (hasColumns) childCellCount.value = 0;
|
|
1504
|
+
});
|
|
1505
|
+
const wrapperEl = ref(null);
|
|
1506
|
+
const liteSelection = useRowSelectionMachine({
|
|
1507
|
+
mode: NOOP_SELECTION_MODE,
|
|
1508
|
+
value: NOOP_SELECTION_VALUE,
|
|
1509
|
+
emit: () => {},
|
|
1510
|
+
keyAt: () => void 0
|
|
1511
|
+
});
|
|
1512
|
+
const liteGetRowKey = (row, index) => {
|
|
1513
|
+
if (row && typeof row === "object") {
|
|
1514
|
+
const { id } = row;
|
|
1515
|
+
if (typeof id === "string" || typeof id === "number") return id;
|
|
1516
|
+
}
|
|
1517
|
+
return index;
|
|
1518
|
+
};
|
|
1519
|
+
provideTableContext({
|
|
1520
|
+
data: dataRef,
|
|
1521
|
+
busy: toRef(props, "busy"),
|
|
1522
|
+
columns,
|
|
1523
|
+
sort: NOOP_SORT_STATE,
|
|
1524
|
+
setSort: () => {},
|
|
1525
|
+
setSortState: () => {},
|
|
1526
|
+
maxSortKeys: ref(0),
|
|
1527
|
+
supportsSortMutation: false,
|
|
1528
|
+
rowClickable: computed(() => false),
|
|
1529
|
+
focusedRow: ref(null),
|
|
1530
|
+
setFocusedRow: () => {},
|
|
1531
|
+
colspan,
|
|
1532
|
+
emitRowClick: () => {},
|
|
1533
|
+
wrapperEl,
|
|
1534
|
+
selection: liteSelection,
|
|
1535
|
+
getRowKey: liteGetRowKey,
|
|
1536
|
+
interactiveRows: ref(/* @__PURE__ */ new Set()),
|
|
1537
|
+
registerInteractiveRow: () => {},
|
|
1538
|
+
unregisterInteractiveRow: () => {}
|
|
1539
|
+
});
|
|
1540
|
+
const slotProps = computed(() => ({
|
|
1541
|
+
data: dataRef.value,
|
|
1542
|
+
busy: props.busy,
|
|
1543
|
+
columns: columns.value,
|
|
1544
|
+
sort: NOOP_SORT_STATE.value,
|
|
1545
|
+
setSort: () => {}
|
|
1546
|
+
}));
|
|
1547
|
+
const setWrapperRef = (el) => {
|
|
1548
|
+
wrapperEl.value = el ?? null;
|
|
1549
|
+
};
|
|
1550
|
+
return () => {
|
|
1551
|
+
const inner = composeTableInner({
|
|
1552
|
+
cols: columns.value,
|
|
1553
|
+
slotChildren: slots.default?.(slotProps.value),
|
|
1554
|
+
captionSlot: slots.caption,
|
|
1555
|
+
colgroupSlot: slots.colgroup
|
|
1556
|
+
});
|
|
1557
|
+
const tableNode = h(props.tag, mergeProps(attrs, {
|
|
1558
|
+
class: theme.value.root || void 0,
|
|
1559
|
+
"aria-busy": props.busy ? "true" : void 0,
|
|
1560
|
+
"data-responsive": props.responsive ? "true" : void 0
|
|
1561
|
+
}), inner);
|
|
1562
|
+
const wrapper = h("div", {
|
|
1563
|
+
ref: setWrapperRef,
|
|
1564
|
+
class: "vc-table-wrapper",
|
|
1565
|
+
style: { position: "relative" }
|
|
1566
|
+
}, [tableNode]);
|
|
1567
|
+
if (!props.scrollable) return wrapper;
|
|
1568
|
+
return h("div", {
|
|
1569
|
+
class: theme.value.scrollContainer || void 0,
|
|
1570
|
+
style: props.maxHeight ? {
|
|
1571
|
+
maxHeight: props.maxHeight,
|
|
1572
|
+
overflow: "auto"
|
|
1573
|
+
} : { overflow: "auto" }
|
|
1574
|
+
}, [wrapper]);
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
});
|
|
1578
|
+
//#endregion
|
|
1579
|
+
//#region src/components/TableEmpty.vue?vue&type=script&lang.ts
|
|
1580
|
+
const tableEmptyThemeDefaults$1 = { classes: { root: "vc-table-empty" } };
|
|
1581
|
+
const behavioralDefaults$2 = {
|
|
1582
|
+
content: "No results.",
|
|
1583
|
+
filteredContent: "No matches."
|
|
1584
|
+
};
|
|
1585
|
+
//#endregion
|
|
1586
|
+
//#region src/components/TableEmpty.vue
|
|
1587
|
+
var TableEmpty_default = defineComponent({
|
|
1588
|
+
name: "VCTableEmpty",
|
|
1589
|
+
inheritAttrs: false,
|
|
1590
|
+
props: {
|
|
1591
|
+
/** Override the auto-derived `colspan`. When omitted, uses the table's resolved colspan. */
|
|
1592
|
+
colspan: {
|
|
1593
|
+
type: Number,
|
|
1594
|
+
default: void 0
|
|
1595
|
+
},
|
|
1596
|
+
/** Mark this as the empty-after-filter case (distinct copy / icon vs empty-no-data). */
|
|
1597
|
+
filtered: {
|
|
1598
|
+
type: Boolean,
|
|
1599
|
+
default: false
|
|
1600
|
+
},
|
|
1601
|
+
/** Override the rendered text. Falls back to global defaults / hardcoded. */
|
|
1602
|
+
content: {
|
|
1603
|
+
type: String,
|
|
1604
|
+
default: void 0
|
|
1605
|
+
},
|
|
1606
|
+
/** Override the rendered text for the filtered case. */
|
|
1607
|
+
filteredContent: {
|
|
1608
|
+
type: String,
|
|
1609
|
+
default: void 0
|
|
1610
|
+
},
|
|
1611
|
+
...themableProps()
|
|
1612
|
+
},
|
|
1613
|
+
setup(props, { attrs, slots }) {
|
|
1614
|
+
const ctx = useTable();
|
|
1615
|
+
const defaults = useComponentDefaults("tableEmpty", props, behavioralDefaults$2);
|
|
1616
|
+
const themeProps = useThemeProps(props);
|
|
1617
|
+
const theme = useComponentTheme("tableEmpty", {
|
|
1618
|
+
get themeClass() {
|
|
1619
|
+
return themeProps.themeClass;
|
|
1620
|
+
},
|
|
1621
|
+
get themeVariant() {
|
|
1622
|
+
return {
|
|
1623
|
+
...themeProps.themeVariant ?? {},
|
|
1624
|
+
filtered: props.filtered
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
}, tableEmptyThemeDefaults$1);
|
|
1628
|
+
const shouldRender = computed(() => {
|
|
1629
|
+
if (!ctx) return true;
|
|
1630
|
+
return ctx.data.value.length === 0 && !ctx.busy.value;
|
|
1631
|
+
});
|
|
1632
|
+
const resolvedColspan = computed(() => props.colspan ?? ctx?.colspan.value ?? 1);
|
|
1633
|
+
return () => {
|
|
1634
|
+
if (!shouldRender.value) return null;
|
|
1635
|
+
const cellContent = slots.default?.() ?? (props.filtered ? defaults.value.filteredContent : defaults.value.content);
|
|
1636
|
+
return h("tbody", mergeProps(attrs, { class: theme.value.root || void 0 }), [h("tr", null, [h("td", { colspan: resolvedColspan.value }, [h("div", {
|
|
1637
|
+
role: "status",
|
|
1638
|
+
"aria-live": "polite"
|
|
1639
|
+
}, cellContent)])])]);
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
//#endregion
|
|
1644
|
+
//#region src/components/TableLoading.vue?vue&type=script&lang.ts
|
|
1645
|
+
const tableLoadingThemeDefaults$1 = { classes: {
|
|
1646
|
+
root: "vc-table-loading",
|
|
1647
|
+
overlay: "vc-table-loading-overlay"
|
|
1648
|
+
} };
|
|
1649
|
+
const behavioralDefaults$1 = { content: "Loading…" };
|
|
1650
|
+
//#endregion
|
|
1651
|
+
//#region src/components/TableLoading.vue
|
|
1652
|
+
var TableLoading_default = defineComponent({
|
|
1653
|
+
name: "VCTableLoading",
|
|
1654
|
+
inheritAttrs: false,
|
|
1655
|
+
props: {
|
|
1656
|
+
/** Override the auto-derived `colspan` (default mode only — ignored in overlay mode). */
|
|
1657
|
+
colspan: {
|
|
1658
|
+
type: Number,
|
|
1659
|
+
default: void 0
|
|
1660
|
+
},
|
|
1661
|
+
/** Render as a positioned overlay on top of the existing `<tbody>` (refresh feedback). Default mode is a centered tbody/tr/td placeholder. */
|
|
1662
|
+
overlay: {
|
|
1663
|
+
type: Boolean,
|
|
1664
|
+
default: false
|
|
1665
|
+
},
|
|
1666
|
+
/** Override the rendered text. Falls back to global defaults. */
|
|
1667
|
+
content: {
|
|
1668
|
+
type: String,
|
|
1669
|
+
default: void 0
|
|
1670
|
+
},
|
|
1671
|
+
...themableProps()
|
|
1672
|
+
},
|
|
1673
|
+
setup(props, { attrs, slots }) {
|
|
1674
|
+
const ctx = useTable();
|
|
1675
|
+
const defaults = useComponentDefaults("tableLoading", props, behavioralDefaults$1);
|
|
1676
|
+
const themeProps = useThemeProps(props);
|
|
1677
|
+
const theme = useComponentTheme("tableLoading", {
|
|
1678
|
+
get themeClass() {
|
|
1679
|
+
return themeProps.themeClass;
|
|
1680
|
+
},
|
|
1681
|
+
get themeVariant() {
|
|
1682
|
+
return {
|
|
1683
|
+
...themeProps.themeVariant ?? {},
|
|
1684
|
+
overlay: props.overlay
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
}, tableLoadingThemeDefaults$1);
|
|
1688
|
+
const shouldRender = computed(() => {
|
|
1689
|
+
if (!ctx) return true;
|
|
1690
|
+
const busy = ctx.busy.value;
|
|
1691
|
+
const hasData = ctx.data.value.length > 0;
|
|
1692
|
+
return props.overlay ? busy : busy && !hasData;
|
|
1693
|
+
});
|
|
1694
|
+
const resolvedColspan = computed(() => props.colspan ?? ctx?.colspan.value ?? 1);
|
|
1695
|
+
return () => {
|
|
1696
|
+
if (!shouldRender.value) return null;
|
|
1697
|
+
const cellContent = slots.default?.() ?? defaults.value.content;
|
|
1698
|
+
if (props.overlay) {
|
|
1699
|
+
const overlayNode = h("div", mergeProps(attrs, {
|
|
1700
|
+
class: [theme.value.root || void 0, theme.value.overlay || void 0],
|
|
1701
|
+
role: "status",
|
|
1702
|
+
"aria-live": "polite",
|
|
1703
|
+
"aria-busy": "true"
|
|
1704
|
+
}), cellContent);
|
|
1705
|
+
if (ctx?.wrapperEl.value) return h(Teleport, { to: ctx.wrapperEl.value }, [overlayNode]);
|
|
1706
|
+
return overlayNode;
|
|
1707
|
+
}
|
|
1708
|
+
return h("tbody", mergeProps(attrs, { class: theme.value.root || void 0 }), [h("tr", null, [h("td", { colspan: resolvedColspan.value }, [h("div", {
|
|
1709
|
+
role: "status",
|
|
1710
|
+
"aria-live": "polite",
|
|
1711
|
+
"aria-busy": "true"
|
|
1712
|
+
}, cellContent)])])]);
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
});
|
|
1716
|
+
//#endregion
|
|
1717
|
+
//#region src/components/TableSortIndicators.vue?vue&type=script&lang.ts
|
|
1718
|
+
const tableSortIndicatorsThemeDefaults$1 = { classes: {
|
|
1719
|
+
root: "vc-table-sort-indicators",
|
|
1720
|
+
label: "vc-table-sort-indicators-label",
|
|
1721
|
+
empty: "vc-table-sort-indicators-empty",
|
|
1722
|
+
chip: "vc-table-sort-indicators-chip",
|
|
1723
|
+
chipToggle: "vc-table-sort-indicators-chip-toggle",
|
|
1724
|
+
chipPosition: "vc-table-sort-indicators-chip-position",
|
|
1725
|
+
chipLabel: "vc-table-sort-indicators-chip-label",
|
|
1726
|
+
chipArrow: "vc-table-sort-indicators-chip-arrow",
|
|
1727
|
+
chipRemove: "vc-table-sort-indicators-chip-remove",
|
|
1728
|
+
addWrapper: "",
|
|
1729
|
+
add: "vc-table-sort-indicators-add",
|
|
1730
|
+
clear: "vc-table-sort-indicators-clear"
|
|
1731
|
+
} };
|
|
1732
|
+
const behavioralDefaults = {
|
|
1733
|
+
label: "Sort:",
|
|
1734
|
+
emptyContent: "no columns sorted yet",
|
|
1735
|
+
addLabel: "+ Add column",
|
|
1736
|
+
clearLabel: "Clear all",
|
|
1737
|
+
removeAriaLabel: "Remove sort key",
|
|
1738
|
+
toggleAscTitle: "Click to toggle descending",
|
|
1739
|
+
toggleDescTitle: "Click to toggle ascending",
|
|
1740
|
+
arrowAsc: "↑",
|
|
1741
|
+
arrowDesc: "↓",
|
|
1742
|
+
removeGlyph: "×"
|
|
1743
|
+
};
|
|
1744
|
+
//#endregion
|
|
1745
|
+
//#region src/components/TableSortIndicators.vue
|
|
1746
|
+
var TableSortIndicators_default = defineComponent({
|
|
1747
|
+
name: "VCTableSortIndicators",
|
|
1748
|
+
inheritAttrs: false,
|
|
1749
|
+
props: {
|
|
1750
|
+
/**
|
|
1751
|
+
* Sort state to render. Use `v-model:sort` for two-way binding.
|
|
1752
|
+
* `null` is treated as "empty array" so consumers carrying
|
|
1753
|
+
* migration-era `ref<TableSortState | null>(null)` don't crash.
|
|
1754
|
+
* When BOTH `:sort` and `:columns` are omitted, the component
|
|
1755
|
+
* falls back to `useTable()` context — note that with the
|
|
1756
|
+
* default `<table>` rendering, slot children render INSIDE the
|
|
1757
|
+
* `<table>`, so the v-model path is the recommended placement
|
|
1758
|
+
* for chip rows above / below the table.
|
|
1759
|
+
*/
|
|
1760
|
+
sort: {
|
|
1761
|
+
type: Array,
|
|
1762
|
+
default: void 0
|
|
1763
|
+
},
|
|
1764
|
+
/**
|
|
1765
|
+
* Columns the sort can reference. Required when using v-model
|
|
1766
|
+
* mode (to look up labels + filter the add-column dropdown).
|
|
1767
|
+
* Falls back to `useTable()` context when omitted.
|
|
1768
|
+
*/
|
|
1769
|
+
columns: {
|
|
1770
|
+
type: Array,
|
|
1771
|
+
default: void 0
|
|
1772
|
+
},
|
|
1773
|
+
/**
|
|
1774
|
+
* Cap on the sort-state array length (mirrors `<VCTable
|
|
1775
|
+
* :max-sort-keys>`). `0` = unlimited. When the cap is hit,
|
|
1776
|
+
* adding via this component evicts the oldest descriptor.
|
|
1777
|
+
* Falls back to `useTable().maxSortKeys` when omitted; defaults
|
|
1778
|
+
* to `0` (unlimited) in stand-alone v-model mode without a
|
|
1779
|
+
* context.
|
|
1780
|
+
*/
|
|
1781
|
+
maxSortKeys: {
|
|
1782
|
+
type: Number,
|
|
1783
|
+
default: void 0
|
|
1784
|
+
},
|
|
1785
|
+
/** Override the leading label text. Falls back to global defaults. */
|
|
1786
|
+
label: {
|
|
1787
|
+
type: String,
|
|
1788
|
+
default: void 0
|
|
1789
|
+
},
|
|
1790
|
+
/** Override the empty-state copy. Falls back to global defaults. */
|
|
1791
|
+
emptyContent: {
|
|
1792
|
+
type: String,
|
|
1793
|
+
default: void 0
|
|
1794
|
+
},
|
|
1795
|
+
/** Override the add-column trigger text. Falls back to global defaults. */
|
|
1796
|
+
addLabel: {
|
|
1797
|
+
type: String,
|
|
1798
|
+
default: void 0
|
|
1799
|
+
},
|
|
1800
|
+
/** Override the clear-all trigger text. Falls back to global defaults. */
|
|
1801
|
+
clearLabel: {
|
|
1802
|
+
type: String,
|
|
1803
|
+
default: void 0
|
|
1804
|
+
},
|
|
1805
|
+
/** Override the aria-label applied to per-chip remove buttons. */
|
|
1806
|
+
removeAriaLabel: {
|
|
1807
|
+
type: String,
|
|
1808
|
+
default: void 0
|
|
1809
|
+
},
|
|
1810
|
+
/** Hide the add-column dropdown (consumer brings their own affordance). */
|
|
1811
|
+
hideAdd: {
|
|
1812
|
+
type: Boolean,
|
|
1813
|
+
default: false
|
|
1814
|
+
},
|
|
1815
|
+
/** Hide the clear-all button. */
|
|
1816
|
+
hideClear: {
|
|
1817
|
+
type: Boolean,
|
|
1818
|
+
default: false
|
|
1819
|
+
},
|
|
1820
|
+
...themableProps()
|
|
1821
|
+
},
|
|
1822
|
+
emits: ["update:sort"],
|
|
1823
|
+
slots: Object,
|
|
1824
|
+
setup(props, { attrs, emit, slots }) {
|
|
1825
|
+
const ctx = useTable();
|
|
1826
|
+
const defaults = useComponentDefaults("tableSortIndicators", props, behavioralDefaults);
|
|
1827
|
+
const theme = useComponentTheme("tableSortIndicators", useThemeProps(props), tableSortIndicatorsThemeDefaults$1);
|
|
1828
|
+
const sortPropProvided = computed(() => props.sort !== void 0);
|
|
1829
|
+
const resolvedSort = computed(() => {
|
|
1830
|
+
if (sortPropProvided.value) return props.sort ?? [];
|
|
1831
|
+
return ctx?.sort.value ?? [];
|
|
1832
|
+
});
|
|
1833
|
+
const resolvedColumns = computed(() => {
|
|
1834
|
+
if (props.columns !== void 0) return props.columns;
|
|
1835
|
+
return ctx?.columns.value ?? [];
|
|
1836
|
+
});
|
|
1837
|
+
function commitSort(next) {
|
|
1838
|
+
if (sortPropProvided.value) {
|
|
1839
|
+
emit("update:sort", next);
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
if (ctx) {
|
|
1843
|
+
if (globalThis.process?.env?.NODE_ENV !== "production" && !ctx.supportsSortMutation) console.warn("[VCTableSortIndicators] mounted inside a table context that does not support sort mutation (likely <VCTableLite>). Clicks will be swallowed. Either bind :sort + :columns directly for v-model mode, or use <VCTable> instead of Lite.");
|
|
1844
|
+
ctx.setSortState(next);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
const columnByKey = computed(() => {
|
|
1848
|
+
const map = /* @__PURE__ */ new Map();
|
|
1849
|
+
for (const col of resolvedColumns.value) map.set(col.key, col);
|
|
1850
|
+
return map;
|
|
1851
|
+
});
|
|
1852
|
+
const unsortedSortable = computed(() => {
|
|
1853
|
+
const inSort = new Set(resolvedSort.value.map((s) => s.key));
|
|
1854
|
+
return resolvedColumns.value.filter((c) => c.sortable && !inSort.has(c.key));
|
|
1855
|
+
});
|
|
1856
|
+
function toggleDirection(key) {
|
|
1857
|
+
commitSort(resolvedSort.value.map((s) => s.key === key ? {
|
|
1858
|
+
...s,
|
|
1859
|
+
direction: s.direction === "asc" ? "desc" : "asc"
|
|
1860
|
+
} : s));
|
|
1861
|
+
}
|
|
1862
|
+
function removeKey(key) {
|
|
1863
|
+
commitSort(resolvedSort.value.filter((s) => s.key !== key));
|
|
1864
|
+
}
|
|
1865
|
+
const resolvedMaxSortKeys = computed(() => {
|
|
1866
|
+
if (props.maxSortKeys !== void 0) return props.maxSortKeys;
|
|
1867
|
+
return ctx?.maxSortKeys.value ?? 0;
|
|
1868
|
+
});
|
|
1869
|
+
function appendKey(key) {
|
|
1870
|
+
if (!key) return;
|
|
1871
|
+
const col = columnByKey.value.get(key);
|
|
1872
|
+
if (!col || !col.sortable) return;
|
|
1873
|
+
if (resolvedSort.value.some((s) => s.key === key)) return;
|
|
1874
|
+
const next = [...resolvedSort.value, {
|
|
1875
|
+
key,
|
|
1876
|
+
direction: "asc"
|
|
1877
|
+
}];
|
|
1878
|
+
const cap = resolvedMaxSortKeys.value;
|
|
1879
|
+
commitSort(cap > 0 && next.length > cap ? next.slice(next.length - cap) : next);
|
|
1880
|
+
}
|
|
1881
|
+
function clearAll() {
|
|
1882
|
+
commitSort([]);
|
|
1883
|
+
}
|
|
1884
|
+
const chipSlotProps = computed(() => resolvedSort.value.map((descriptor, index) => ({
|
|
1885
|
+
descriptor,
|
|
1886
|
+
index,
|
|
1887
|
+
position: index + 1,
|
|
1888
|
+
column: columnByKey.value.get(descriptor.key),
|
|
1889
|
+
toggle: () => toggleDirection(descriptor.key),
|
|
1890
|
+
remove: () => removeKey(descriptor.key)
|
|
1891
|
+
})));
|
|
1892
|
+
return () => {
|
|
1893
|
+
if (!sortPropProvided.value && !ctx) return null;
|
|
1894
|
+
const sortState = resolvedSort.value;
|
|
1895
|
+
const t = theme.value;
|
|
1896
|
+
const d = defaults.value;
|
|
1897
|
+
const addSlotProps = {
|
|
1898
|
+
options: unsortedSortable.value,
|
|
1899
|
+
add: appendKey
|
|
1900
|
+
};
|
|
1901
|
+
const clearSlotProps = { clear: clearAll };
|
|
1902
|
+
const renderChip = (chip) => {
|
|
1903
|
+
if (slots.chip) return slots.chip(chip);
|
|
1904
|
+
const isAsc = chip.descriptor.direction === "asc";
|
|
1905
|
+
return h("div", {
|
|
1906
|
+
key: chip.descriptor.key,
|
|
1907
|
+
class: t.chip || void 0,
|
|
1908
|
+
"data-sort-key": chip.descriptor.key,
|
|
1909
|
+
"data-direction": chip.descriptor.direction
|
|
1910
|
+
}, [h("button", {
|
|
1911
|
+
type: "button",
|
|
1912
|
+
class: t.chipToggle || void 0,
|
|
1913
|
+
title: isAsc ? d.toggleAscTitle : d.toggleDescTitle,
|
|
1914
|
+
onClick: chip.toggle
|
|
1915
|
+
}, [
|
|
1916
|
+
h("span", { class: t.chipPosition || void 0 }, `${chip.position}.`),
|
|
1917
|
+
h("span", { class: t.chipLabel || void 0 }, chip.column?.label ?? chip.descriptor.key),
|
|
1918
|
+
h("span", { class: t.chipArrow || void 0 }, isAsc ? d.arrowAsc : d.arrowDesc)
|
|
1919
|
+
]), h("button", {
|
|
1920
|
+
type: "button",
|
|
1921
|
+
class: t.chipRemove || void 0,
|
|
1922
|
+
"aria-label": d.removeAriaLabel,
|
|
1923
|
+
onClick: chip.remove
|
|
1924
|
+
}, d.removeGlyph)]);
|
|
1925
|
+
};
|
|
1926
|
+
const renderLabel = () => slots.label ? slots.label() : h("span", { class: t.label || void 0 }, d.label);
|
|
1927
|
+
const renderEmpty = () => slots.empty ? slots.empty() : h("span", { class: t.empty || void 0 }, d.emptyContent);
|
|
1928
|
+
const renderAdd = () => {
|
|
1929
|
+
if (props.hideAdd || addSlotProps.options.length === 0) return null;
|
|
1930
|
+
if (slots.add) return slots.add(addSlotProps);
|
|
1931
|
+
const selectNode = h("select", {
|
|
1932
|
+
class: t.add || void 0,
|
|
1933
|
+
"aria-label": d.addLabel,
|
|
1934
|
+
onChange: (e) => {
|
|
1935
|
+
const target = e.target;
|
|
1936
|
+
appendKey(target.value);
|
|
1937
|
+
target.value = "";
|
|
1938
|
+
}
|
|
1939
|
+
}, [h("option", { value: "" }, d.addLabel), ...addSlotProps.options.map((col) => h("option", {
|
|
1940
|
+
key: col.key,
|
|
1941
|
+
value: col.key
|
|
1942
|
+
}, col.label ?? col.key))]);
|
|
1943
|
+
if (!t.addWrapper) return selectNode;
|
|
1944
|
+
return h("div", { class: t.addWrapper }, [selectNode]);
|
|
1945
|
+
};
|
|
1946
|
+
const renderClear = () => {
|
|
1947
|
+
if (props.hideClear || sortState.length === 0) return null;
|
|
1948
|
+
if (slots.clear) return slots.clear(clearSlotProps);
|
|
1949
|
+
return h("button", {
|
|
1950
|
+
type: "button",
|
|
1951
|
+
class: t.clear || void 0,
|
|
1952
|
+
onClick: clearAll
|
|
1953
|
+
}, d.clearLabel);
|
|
1954
|
+
};
|
|
1955
|
+
const rootAttrs = {
|
|
1956
|
+
class: t.root || void 0,
|
|
1957
|
+
role: "toolbar",
|
|
1958
|
+
"aria-label": "Active sort columns"
|
|
1959
|
+
};
|
|
1960
|
+
if (slots.default) return h("div", mergeProps(attrs, rootAttrs), slots.default({
|
|
1961
|
+
sort: sortState,
|
|
1962
|
+
chips: chipSlotProps.value,
|
|
1963
|
+
add: addSlotProps,
|
|
1964
|
+
clear: clearSlotProps
|
|
1965
|
+
}));
|
|
1966
|
+
const chipsContent = sortState.length === 0 ? [renderEmpty()] : chipSlotProps.value.map(renderChip);
|
|
1967
|
+
return h("div", mergeProps(attrs, rootAttrs), [
|
|
1968
|
+
renderLabel(),
|
|
1969
|
+
...chipsContent,
|
|
1970
|
+
renderAdd(),
|
|
1971
|
+
renderClear()
|
|
1972
|
+
]);
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
});
|
|
1976
|
+
//#endregion
|
|
1977
|
+
//#region src/composables/define-table.ts
|
|
1978
|
+
/**
|
|
1979
|
+
* Pinia-style factory mirroring `defineList()`. Returns reactive state
|
|
1980
|
+
* containers + a `setSort` mutator. Useful when a consumer wants to share
|
|
1981
|
+
* a table's state across multiple components without prop drilling, or
|
|
1982
|
+
* when wiring a controlled table outside the SFC.
|
|
1983
|
+
*
|
|
1984
|
+
* const usersTable = defineTable<User>({
|
|
1985
|
+
* columns: ['id', 'name', 'email'],
|
|
1986
|
+
* data: [],
|
|
1987
|
+
* busy: false,
|
|
1988
|
+
* });
|
|
1989
|
+
*
|
|
1990
|
+
* <VCTable :data="usersTable.data" :columns="usersTable.columns" ... />
|
|
1991
|
+
*
|
|
1992
|
+
* The sort cycle composes `useSortMachine` internally — same semantics
|
|
1993
|
+
* as the `<VCTable>` SFC path (honors per-column `initialSortDirection`
|
|
1994
|
+
* + `mustSort`).
|
|
1995
|
+
*/
|
|
1996
|
+
function defineTable(options = {}) {
|
|
1997
|
+
const data = ref(options.data ?? []);
|
|
1998
|
+
const rawColumns = ref(options.columns ?? []);
|
|
1999
|
+
const busy = ref(options.busy ?? false);
|
|
2000
|
+
const mustSort = ref(options.mustSort ?? false);
|
|
2001
|
+
const sort = ref(options.sort ?? []);
|
|
2002
|
+
const maxSortKeys = ref(options.maxSortKeys ?? 3);
|
|
2003
|
+
const columns = computed(() => normalizeColumns(rawColumns.value, data.value));
|
|
2004
|
+
return {
|
|
2005
|
+
data,
|
|
2006
|
+
columns,
|
|
2007
|
+
rawColumns,
|
|
2008
|
+
busy,
|
|
2009
|
+
mustSort,
|
|
2010
|
+
sort,
|
|
2011
|
+
setSort: useSortMachine({
|
|
2012
|
+
source: sort,
|
|
2013
|
+
columns,
|
|
2014
|
+
mustSort,
|
|
2015
|
+
maxSortKeys,
|
|
2016
|
+
emit: (next) => {
|
|
2017
|
+
sort.value = next;
|
|
2018
|
+
}
|
|
2019
|
+
}).setSort
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
//#endregion
|
|
2023
|
+
//#region src/theme.ts
|
|
2024
|
+
const tableThemeDefaults = { classes: {
|
|
2025
|
+
root: "vc-table",
|
|
2026
|
+
scrollContainer: "vc-table-scroll-container"
|
|
2027
|
+
} };
|
|
2028
|
+
const tableHeaderThemeDefaults = { classes: { root: "vc-table-header" } };
|
|
2029
|
+
const tableBodyThemeDefaults = { classes: { root: "vc-table-body" } };
|
|
2030
|
+
const tableFooterThemeDefaults = { classes: { root: "vc-table-footer" } };
|
|
2031
|
+
const tableRowThemeDefaults = { classes: { root: "vc-table-row" } };
|
|
2032
|
+
const tableCellThemeDefaults = { classes: { root: "vc-table-cell" } };
|
|
2033
|
+
const tableHeadCellThemeDefaults = { classes: {
|
|
2034
|
+
root: "vc-table-head-cell",
|
|
2035
|
+
sortIcon: "vc-table-head-cell-sort-icon"
|
|
2036
|
+
} };
|
|
2037
|
+
const tableEmptyThemeDefaults = { classes: { root: "vc-table-empty" } };
|
|
2038
|
+
const tableLoadingThemeDefaults = { classes: {
|
|
2039
|
+
root: "vc-table-loading",
|
|
2040
|
+
overlay: "vc-table-loading-overlay"
|
|
2041
|
+
} };
|
|
2042
|
+
const tableSortIndicatorsThemeDefaults = { classes: {
|
|
2043
|
+
root: "vc-table-sort-indicators",
|
|
2044
|
+
label: "vc-table-sort-indicators-label",
|
|
2045
|
+
empty: "vc-table-sort-indicators-empty",
|
|
2046
|
+
chip: "vc-table-sort-indicators-chip",
|
|
2047
|
+
chipToggle: "vc-table-sort-indicators-chip-toggle",
|
|
2048
|
+
chipPosition: "vc-table-sort-indicators-chip-position",
|
|
2049
|
+
chipLabel: "vc-table-sort-indicators-chip-label",
|
|
2050
|
+
chipArrow: "vc-table-sort-indicators-chip-arrow",
|
|
2051
|
+
chipRemove: "vc-table-sort-indicators-chip-remove",
|
|
2052
|
+
addWrapper: "",
|
|
2053
|
+
add: "vc-table-sort-indicators-add",
|
|
2054
|
+
clear: "vc-table-sort-indicators-clear"
|
|
2055
|
+
} };
|
|
2056
|
+
//#endregion
|
|
2057
|
+
//#region src/index.ts
|
|
2058
|
+
function install(app, options = {}) {
|
|
2059
|
+
installThemeManager(app, options);
|
|
2060
|
+
installDefaultsManager(app, options);
|
|
2061
|
+
Object.entries({
|
|
2062
|
+
VCTable: Table_default,
|
|
2063
|
+
VCTableLite: TableLite_default,
|
|
2064
|
+
VCTableHeader: TableHeader_default,
|
|
2065
|
+
VCTableBody: TableBody_default,
|
|
2066
|
+
VCTableFooter: TableFooter_default,
|
|
2067
|
+
VCTableRow: TableRow_default,
|
|
2068
|
+
VCTableCell: TableCell_default,
|
|
2069
|
+
VCTableHeadCell: TableHeadCell_default,
|
|
2070
|
+
VCTableEmpty: TableEmpty_default,
|
|
2071
|
+
VCTableLoading: TableLoading_default,
|
|
2072
|
+
VCTableSortIndicators: TableSortIndicators_default
|
|
2073
|
+
}).forEach(([name, component]) => {
|
|
2074
|
+
app.component(name, component);
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
var src_default = { install };
|
|
2078
|
+
//#endregion
|
|
2079
|
+
export { Table_default as VCTable, TableBody_default as VCTableBody, TableCell_default as VCTableCell, TableEmpty_default as VCTableEmpty, TableFooter_default as VCTableFooter, TableHeadCell_default as VCTableHeadCell, TableHeader_default as VCTableHeader, TableLite_default as VCTableLite, TableLoading_default as VCTableLoading, TableRow_default as VCTableRow, TableSortIndicators_default as VCTableSortIndicators, src_default as default, defineTable, filterRowClickEvent, install, normalizeColumns, provideHeadCellCountContext, provideTableContext, provideTableRowContext, resolveAttrs, resolveCellValue, sortRows, startCase, tableBodyThemeDefaults, tableCellThemeDefaults, tableEmptyThemeDefaults, tableFooterThemeDefaults, tableHeadCellThemeDefaults, tableHeaderThemeDefaults, tableLoadingThemeDefaults, tableRowThemeDefaults, tableSortIndicatorsThemeDefaults, tableThemeDefaults, useHeadCellCountContext, useRowSelectionMachine, useSortMachine, useTable, useTableRow };
|
|
2080
|
+
|
|
2081
|
+
//# sourceMappingURL=index.mjs.map
|