@vueye-table/headless 3.0.0-alpha.7 → 3.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +288 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +469 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { injectDataGrid, injectDataTable, provideDataGrid, provideDataTable, useDataGrid, useVirtualColumns, useVirtualRows } from "@vueye-table/vue";
|
|
2
2
|
import { Fragment, computed, defineComponent, h, inject, nextTick, onMounted, onScopeDispose, provide, ref, shallowRef, useId, watch } from "vue";
|
|
3
|
+
import { getRowItemKey, gridCommand } from "@vueye-table/core";
|
|
3
4
|
|
|
4
5
|
//#region src/shared.ts
|
|
5
6
|
/** The element a component renders. Every headless component accepts `as`. */
|
|
@@ -76,14 +77,16 @@ const DataTableSelectRow = defineComponent({
|
|
|
76
77
|
setup(props, { slots }) {
|
|
77
78
|
const table = injectDataTable("<DataTableSelectRow>");
|
|
78
79
|
return () => {
|
|
79
|
-
const selected = table.isSelected(props.row.key);
|
|
80
|
+
const selected = table.tree ? props.row.selection === "all" : table.isSelected(props.row.key);
|
|
81
|
+
const indeterminate = table.tree && props.row.selection === "some";
|
|
80
82
|
const toggle = () => table.toggleRow(props.row.key);
|
|
81
83
|
return slots.default?.({
|
|
82
84
|
selected,
|
|
85
|
+
indeterminate,
|
|
83
86
|
toggle
|
|
84
87
|
}) ?? checkbox({
|
|
85
88
|
checked: selected,
|
|
86
|
-
indeterminate
|
|
89
|
+
indeterminate,
|
|
87
90
|
label: props.label,
|
|
88
91
|
disabled: table.selectionMode === "none",
|
|
89
92
|
onChange: toggle
|
|
@@ -244,10 +247,6 @@ const DataTableColumnVisibility = defineComponent({
|
|
|
244
247
|
};
|
|
245
248
|
}
|
|
246
249
|
});
|
|
247
|
-
/**
|
|
248
|
-
* A polite live region describing what is shown, such as "1–10 of 57 rows". Screen readers
|
|
249
|
-
* announce it after a search, filter, or page change.
|
|
250
|
-
*/
|
|
251
250
|
const DataTableStatus = defineComponent({
|
|
252
251
|
name: "DataTableStatus",
|
|
253
252
|
slots: Object,
|
|
@@ -260,17 +259,71 @@ const DataTableStatus = defineComponent({
|
|
|
260
259
|
end: table.pageEnd,
|
|
261
260
|
rowCount: table.rowCount,
|
|
262
261
|
totalRowCount: table.totalRowCount,
|
|
263
|
-
selectedCount: table.selectedCount
|
|
262
|
+
selectedCount: table.selectedCount,
|
|
263
|
+
loadState: table.loadState,
|
|
264
|
+
loadedRowCount: table.loadedRowCount,
|
|
265
|
+
loadError: table.loadError,
|
|
266
|
+
canLoadMore: table.canLoadMore,
|
|
267
|
+
retry: () => table.retry(),
|
|
268
|
+
loadNext: () => table.loadNext(),
|
|
269
|
+
loadingChildrenCount: table.tree ? table.processedRows.filter((row) => row.childStatus === "loading").length : 0,
|
|
270
|
+
childErrorCount: table.tree ? table.processedRows.filter((row) => row.childStatus === "error").length : 0
|
|
264
271
|
};
|
|
265
|
-
const
|
|
272
|
+
const count = `Loaded ${table.loadedRowCount.toLocaleString()} ${table.loadedRowCount === 1 ? "row" : "rows"}`;
|
|
273
|
+
const text = table.loadingMode ? table.loadState === "error" ? `${count}. Could not load rows. Retry loading.` : table.loadState === "aborted" ? `${count}. Loading stopped.` : table.loadState === "loading" || table.loadState === "streaming" ? `${count}…` : `${count}${table.loadState === "done" ? ". All rows loaded." : "."}` : table.rowCount === 0 ? "No rows" : `${table.pageStart}–${table.pageEnd} of ${table.rowCount} rows` + (table.selectedCount > 0 ? `, ${table.selectedCount} selected` : "");
|
|
266
274
|
return h(props.as, {
|
|
267
275
|
role: "status",
|
|
268
276
|
"aria-live": "polite"
|
|
269
|
-
}, slots.default?.(slotProps) ?? text);
|
|
277
|
+
}, slots.default?.(slotProps) ?? text + (slotProps.loadingChildrenCount ? " Loading children…" : "") + (slotProps.childErrorCount ? " Could not load children. Retry children in the affected row." : ""));
|
|
270
278
|
};
|
|
271
279
|
}
|
|
272
280
|
});
|
|
273
281
|
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/loading.ts
|
|
284
|
+
/** Keep this visual sentinel out of the live region and logical data-row count. */
|
|
285
|
+
function loadSentinel(table, colspan) {
|
|
286
|
+
return table.loadingMode === "cursor" && table.loadedRowCount > 0 && (table.loadState === "loading" || table.loadState === "streaming") ? h("tr", {
|
|
287
|
+
key: "vt-load-sentinel",
|
|
288
|
+
"data-load-sentinel": "",
|
|
289
|
+
"aria-hidden": "true"
|
|
290
|
+
}, [h("td", {
|
|
291
|
+
colspan: Math.max(1, colspan),
|
|
292
|
+
class: "vt-load-sentinel"
|
|
293
|
+
}, "Loading more rows…")]) : void 0;
|
|
294
|
+
}
|
|
295
|
+
/** A keyboard-accessible alternative to automatic virtual-end loading, with error recovery. */
|
|
296
|
+
const DataTableLoadMore = defineComponent({
|
|
297
|
+
name: "DataTableLoadMore",
|
|
298
|
+
props: {
|
|
299
|
+
label: {
|
|
300
|
+
type: String,
|
|
301
|
+
default: "Load more rows"
|
|
302
|
+
},
|
|
303
|
+
retryLabel: {
|
|
304
|
+
type: String,
|
|
305
|
+
default: "Retry loading"
|
|
306
|
+
},
|
|
307
|
+
loadingLabel: {
|
|
308
|
+
type: String,
|
|
309
|
+
default: "Loading rows…"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
setup(props) {
|
|
313
|
+
const table = injectDataTable("<DataTableLoadMore>");
|
|
314
|
+
return () => table.loadState === "error" ? h("button", {
|
|
315
|
+
type: "button",
|
|
316
|
+
onClick: () => table.retry()
|
|
317
|
+
}, props.retryLabel) : table.loadingMode === "cursor" && table.loadState !== "done" ? h("button", {
|
|
318
|
+
type: "button",
|
|
319
|
+
disabled: !table.canLoadMore,
|
|
320
|
+
onClick: () => {
|
|
321
|
+
table.loadNext();
|
|
322
|
+
}
|
|
323
|
+
}, table.loadState === "loading" || table.loadState === "streaming" ? props.loadingLabel : props.label) : null;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
274
327
|
//#endregion
|
|
275
328
|
//#region src/virtual.ts
|
|
276
329
|
/** Shared opt-in renderer props. Options are applied when the virtual renderer is created. */
|
|
@@ -349,6 +402,15 @@ function createComponentVirtual(table, options, tableElement, grid, virtualColum
|
|
|
349
402
|
grid,
|
|
350
403
|
scrollMargin: () => tableElement()?.querySelector("thead")?.getBoundingClientRect().height ?? 0
|
|
351
404
|
});
|
|
405
|
+
onMounted(() => {
|
|
406
|
+
watch(() => [
|
|
407
|
+
rows.endIndex,
|
|
408
|
+
table.renderItems.length,
|
|
409
|
+
table.canLoadMore
|
|
410
|
+
], () => {
|
|
411
|
+
if (table.loadingMode === "cursor" && table.canLoadMore && rows.endIndex >= table.renderItems.length - 1 - table.endThreshold) table.loadNext();
|
|
412
|
+
}, { immediate: true });
|
|
413
|
+
});
|
|
352
414
|
const columns = grid && virtualColumns ? useVirtualColumns(grid, {
|
|
353
415
|
scrollElement,
|
|
354
416
|
estimateColumnWidth: (index) => table.columns[index]?.width ?? options.columnWidth ?? 120,
|
|
@@ -499,6 +561,269 @@ function virtualRow(binding, row) {
|
|
|
499
561
|
return binding.rowItems.find((item) => item.renderItem.kind === "row" && item.renderItem.row.key === row.key);
|
|
500
562
|
}
|
|
501
563
|
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region src/hierarchy.ts
|
|
566
|
+
const hierarchyProps = {
|
|
567
|
+
treeColumn: {
|
|
568
|
+
type: String,
|
|
569
|
+
default: void 0
|
|
570
|
+
},
|
|
571
|
+
keepAliveDetail: {
|
|
572
|
+
type: Boolean,
|
|
573
|
+
default: false
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
const hierarchyKey = Symbol("vueye-table-hierarchy");
|
|
577
|
+
function provideHierarchy(table, props) {
|
|
578
|
+
const focus = shallowRef();
|
|
579
|
+
const focusKey = computed(() => table.rows.some((row) => row.key === focus.value) ? focus.value : table.rows[0]?.key);
|
|
580
|
+
const context = {
|
|
581
|
+
id: `vt-tree-${useId()}`,
|
|
582
|
+
table,
|
|
583
|
+
get treeColumn() {
|
|
584
|
+
return props.treeColumn;
|
|
585
|
+
},
|
|
586
|
+
get keepAliveDetail() {
|
|
587
|
+
return props.keepAliveDetail;
|
|
588
|
+
},
|
|
589
|
+
focus,
|
|
590
|
+
get focusKey() {
|
|
591
|
+
return focusKey.value;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
provide(hierarchyKey, context);
|
|
595
|
+
return context;
|
|
596
|
+
}
|
|
597
|
+
function injectHierarchy() {
|
|
598
|
+
return inject(hierarchyKey, void 0);
|
|
599
|
+
}
|
|
600
|
+
function resolveTreeColumn(table, column) {
|
|
601
|
+
return table.columns.find((candidate) => candidate.id === column)?.id ?? table.columns[0]?.id;
|
|
602
|
+
}
|
|
603
|
+
function itemId(context, row, detail = false) {
|
|
604
|
+
return context ? `${context.id}-${encodeURIComponent(getRowItemKey(row.key, detail ? "detail" : "row"))}` : void 0;
|
|
605
|
+
}
|
|
606
|
+
const siblingCache = /* @__PURE__ */ new WeakMap();
|
|
607
|
+
function hierarchyRowAttrs(table, row, context) {
|
|
608
|
+
if (!table.tree) return {};
|
|
609
|
+
let positions = siblingCache.get(table.processedRows);
|
|
610
|
+
if (!positions) {
|
|
611
|
+
const groups = /* @__PURE__ */ new Map();
|
|
612
|
+
for (const candidate of table.processedRows) {
|
|
613
|
+
const group = groups.get(candidate.parentKey) ?? [];
|
|
614
|
+
group.push(candidate);
|
|
615
|
+
groups.set(candidate.parentKey, group);
|
|
616
|
+
}
|
|
617
|
+
const indices = new Map(table.processedRows.map((candidate, index) => [candidate.key, index]));
|
|
618
|
+
const result = /* @__PURE__ */ new Map();
|
|
619
|
+
for (const group of groups.values()) group.forEach((candidate, index) => result.set(candidate.key, {
|
|
620
|
+
position: index + 1,
|
|
621
|
+
size: group.length,
|
|
622
|
+
index: indices.get(candidate.key)
|
|
623
|
+
}));
|
|
624
|
+
positions = result;
|
|
625
|
+
siblingCache.set(table.processedRows, result);
|
|
626
|
+
}
|
|
627
|
+
const position = positions.get(row.key);
|
|
628
|
+
return {
|
|
629
|
+
id: itemId(context, row),
|
|
630
|
+
"data-tree-key": getRowItemKey(row.key),
|
|
631
|
+
"aria-level": row.depth + 1,
|
|
632
|
+
"aria-expanded": row.canExpand ? String(row.isExpanded) : void 0,
|
|
633
|
+
"aria-setsize": position?.size,
|
|
634
|
+
"aria-posinset": position?.position,
|
|
635
|
+
"aria-rowindex": position ? position.index + 2 : void 0,
|
|
636
|
+
"aria-busy": row.childStatus === "loading" ? "true" : void 0
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
/** Row-focused table navigation; cell-focused grids use the same pure core commands. */
|
|
640
|
+
function treeKeydown(event, context, virtual) {
|
|
641
|
+
const table = context.table;
|
|
642
|
+
const target = event.target;
|
|
643
|
+
if (!table.tree || !target?.closest) return;
|
|
644
|
+
if (target.closest("input, textarea, select, [contenteditable=true]") || target.closest("button") && !target.closest("[data-expand-toggle]")) return;
|
|
645
|
+
const key = target.closest("tr")?.getAttribute("data-tree-key");
|
|
646
|
+
const index = table.rows.findIndex((row) => getRowItemKey(row.key) === key);
|
|
647
|
+
const row = table.rows[index];
|
|
648
|
+
if (!row) return;
|
|
649
|
+
const command = gridCommand(event, {
|
|
650
|
+
canExpand: row.canExpand,
|
|
651
|
+
expanded: row.isExpanded
|
|
652
|
+
});
|
|
653
|
+
let next = row;
|
|
654
|
+
if (command?.type === "tree") {
|
|
655
|
+
if (command.action === "expand" || command.action === "collapse") table.toggleExpanded(row.key, command.action === "expand");
|
|
656
|
+
else if (command.action === "siblings") for (const sibling of table.processedRows.filter((candidate) => candidate.parentKey === row.parentKey && candidate.canExpand)) table.toggleExpanded(sibling.key, true);
|
|
657
|
+
else if (command.action === "child") next = table.rows.find((candidate) => candidate.parentKey === row.key) ?? row;
|
|
658
|
+
else next = table.rows.find((candidate) => candidate.key === row.parentKey) ?? row;
|
|
659
|
+
} else if (event.key === "ArrowDown" || event.key === "ArrowUp") next = table.rows[index + (event.key === "ArrowDown" ? 1 : -1)] ?? row;
|
|
660
|
+
else if (event.key === "Home" || event.key === "End") next = table.rows[event.key === "Home" ? 0 : table.rows.length - 1] ?? row;
|
|
661
|
+
else return;
|
|
662
|
+
event.preventDefault();
|
|
663
|
+
context.focus.value = next.key;
|
|
664
|
+
virtual?.rows.scrollToKey(next.key);
|
|
665
|
+
const id = itemId(context, next);
|
|
666
|
+
nextTick(() => target.closest("table")?.ownerDocument.getElementById(id ?? "")?.focus());
|
|
667
|
+
}
|
|
668
|
+
const expandToggleProps = {
|
|
669
|
+
row: rowProp,
|
|
670
|
+
controls: {
|
|
671
|
+
type: String,
|
|
672
|
+
default: void 0
|
|
673
|
+
},
|
|
674
|
+
expandLabel: {
|
|
675
|
+
type: String,
|
|
676
|
+
default: "Expand row"
|
|
677
|
+
},
|
|
678
|
+
collapseLabel: {
|
|
679
|
+
type: String,
|
|
680
|
+
default: "Collapse row"
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
const treeCellProps = {
|
|
684
|
+
row: rowProp,
|
|
685
|
+
as: asProp("div")
|
|
686
|
+
};
|
|
687
|
+
const DataTableExpandToggle = defineComponent({
|
|
688
|
+
name: "DataTableExpandToggle",
|
|
689
|
+
props: expandToggleProps,
|
|
690
|
+
slots: Object,
|
|
691
|
+
setup(props, { slots }) {
|
|
692
|
+
const table = injectDataTable("<DataTableExpandToggle>");
|
|
693
|
+
const context = injectHierarchy();
|
|
694
|
+
const grid = injectDataGrid();
|
|
695
|
+
return () => props.row.canExpand ? h("button", {
|
|
696
|
+
type: "button",
|
|
697
|
+
"data-expand-toggle": "",
|
|
698
|
+
tabindex: table.tree ? -1 : void 0,
|
|
699
|
+
"aria-expanded": String(props.row.isExpanded),
|
|
700
|
+
"aria-controls": props.controls ?? (!table.tree ? itemId(context, props.row, true) : props.row.isExpanded ? table.rows.filter((row) => row.parentKey === props.row.key).map((row) => itemId(context, row)).filter(Boolean).join(" ") || void 0 : void 0),
|
|
701
|
+
"aria-label": props.row.isExpanded ? props.collapseLabel : props.expandLabel,
|
|
702
|
+
onMousedown: (event) => event.stopPropagation(),
|
|
703
|
+
onDblclick: (event) => event.stopPropagation(),
|
|
704
|
+
onClick: (event) => {
|
|
705
|
+
event.stopPropagation();
|
|
706
|
+
if (grid?.table === table) grid.focusCell({
|
|
707
|
+
row: table.rows.indexOf(props.row),
|
|
708
|
+
column: Math.max(0, table.columns.findIndex((column) => column.id === resolveTreeColumn(table, context?.treeColumn)))
|
|
709
|
+
});
|
|
710
|
+
table.toggleExpanded(props.row.key);
|
|
711
|
+
if (grid?.table === table) nextTick(() => event.target.closest("table")?.focus());
|
|
712
|
+
}
|
|
713
|
+
}, slots.default?.({
|
|
714
|
+
row: props.row,
|
|
715
|
+
expanded: props.row.isExpanded
|
|
716
|
+
}) ?? (props.row.isExpanded ? props.collapseLabel : props.expandLabel)) : null;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
const DataTableTreeCell = defineComponent({
|
|
720
|
+
name: "DataTableTreeCell",
|
|
721
|
+
props: treeCellProps,
|
|
722
|
+
slots: Object,
|
|
723
|
+
setup(props, { slots }) {
|
|
724
|
+
const table = injectDataTable("<DataTableTreeCell>");
|
|
725
|
+
const context = injectHierarchy();
|
|
726
|
+
const grid = injectDataGrid();
|
|
727
|
+
return () => h(props.as, {
|
|
728
|
+
class: "vt-tree-cell",
|
|
729
|
+
"data-tree-cell": "",
|
|
730
|
+
style: { paddingInlineStart: `calc(var(--vt-tree-indent, 1.25rem) * ${props.row.depth})` }
|
|
731
|
+
}, [
|
|
732
|
+
h("span", { class: "vt-tree-toggle" }, slots.toggle?.({ row: props.row }) ?? h(DataTableExpandToggle, { row: props.row })),
|
|
733
|
+
h("span", { class: "vt-tree-label" }, [slots.default?.()]),
|
|
734
|
+
props.row.childStatus === "loading" ? h("span", { class: "vt-tree-loading" }, "Loading children…") : null,
|
|
735
|
+
props.row.childStatus === "error" ? h("span", { class: "vt-tree-error" }, ["Could not load children. ", h("button", {
|
|
736
|
+
type: "button",
|
|
737
|
+
class: "vt-tree-retry",
|
|
738
|
+
onMousedown: (event) => event.stopPropagation(),
|
|
739
|
+
onClick: (event) => {
|
|
740
|
+
event.stopPropagation();
|
|
741
|
+
const element = event.currentTarget.closest("table");
|
|
742
|
+
if (grid?.table === table) grid.focusCell({
|
|
743
|
+
row: table.rows.indexOf(props.row),
|
|
744
|
+
column: Math.max(0, table.columns.findIndex((column) => column.id === resolveTreeColumn(table, context?.treeColumn)))
|
|
745
|
+
});
|
|
746
|
+
table.toggleExpanded(props.row.key, true);
|
|
747
|
+
nextTick(() => {
|
|
748
|
+
if (grid?.table === table) element?.focus();
|
|
749
|
+
else element?.ownerDocument.getElementById(itemId(context, props.row) ?? "")?.focus();
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}, "Retry children")]) : null
|
|
753
|
+
]);
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
/** One full-width detail; retained collapsed content remains hidden and unmeasured. */
|
|
757
|
+
const DataTableDetailRow = defineComponent({
|
|
758
|
+
name: "DataTableDetailRow",
|
|
759
|
+
props: {
|
|
760
|
+
row: rowProp,
|
|
761
|
+
colspan: {
|
|
762
|
+
type: Number,
|
|
763
|
+
default: void 0
|
|
764
|
+
},
|
|
765
|
+
hidden: {
|
|
766
|
+
type: Boolean,
|
|
767
|
+
default: false
|
|
768
|
+
},
|
|
769
|
+
keepAlive: {
|
|
770
|
+
type: Boolean,
|
|
771
|
+
default: void 0
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
slots: Object,
|
|
775
|
+
setup(props, { slots }) {
|
|
776
|
+
const table = injectDataTable("<DataTableDetailRow>");
|
|
777
|
+
const context = injectHierarchy();
|
|
778
|
+
const virtual = injectVirtual();
|
|
779
|
+
let opened = false;
|
|
780
|
+
return () => {
|
|
781
|
+
if (props.row.isExpanded && !props.hidden) opened = true;
|
|
782
|
+
if (!opened || (!props.row.isExpanded || props.hidden) && !(props.keepAlive ?? context?.keepAliveDetail)) return null;
|
|
783
|
+
const hidden = props.hidden || !props.row.isExpanded;
|
|
784
|
+
return h("tr", {
|
|
785
|
+
id: itemId(context, props.row, true),
|
|
786
|
+
"data-detail": "",
|
|
787
|
+
hidden,
|
|
788
|
+
...virtual && !hidden ? { ref: (target) => virtual.rows.measureElement(target, getRowItemKey(props.row.key, "detail")) } : {}
|
|
789
|
+
}, [h("td", {
|
|
790
|
+
colspan: Math.max(1, props.colspan ?? table.columns.length),
|
|
791
|
+
class: "vt-detail-cell"
|
|
792
|
+
}, [slots.default?.({ row: props.row })])]);
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
function createDetails(table, slot, colspan) {
|
|
797
|
+
const context = injectHierarchy();
|
|
798
|
+
const visited = /* @__PURE__ */ new Set();
|
|
799
|
+
const render = (row, rowIndex, hidden = false) => {
|
|
800
|
+
if (!hidden && context?.keepAliveDetail) visited.add(row.key);
|
|
801
|
+
return h(DataTableDetailRow, {
|
|
802
|
+
key: getRowItemKey(row.key, "detail"),
|
|
803
|
+
row,
|
|
804
|
+
colspan: colspan(),
|
|
805
|
+
hidden
|
|
806
|
+
}, { default: () => slot()?.({
|
|
807
|
+
row,
|
|
808
|
+
rowIndex
|
|
809
|
+
}) });
|
|
810
|
+
};
|
|
811
|
+
return {
|
|
812
|
+
render,
|
|
813
|
+
retained(visible) {
|
|
814
|
+
if (!context?.keepAliveDetail) return [];
|
|
815
|
+
return [...visited].flatMap((key) => {
|
|
816
|
+
const index = table.rows.findIndex((row) => row.key === key);
|
|
817
|
+
if (index < 0) {
|
|
818
|
+
visited.delete(key);
|
|
819
|
+
return [];
|
|
820
|
+
}
|
|
821
|
+
return visible.has(key) ? [] : [render(table.rows[index], index, true)];
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
502
827
|
//#endregion
|
|
503
828
|
//#region src/table.ts
|
|
504
829
|
/**
|
|
@@ -514,15 +839,20 @@ const DataTableRoot = defineComponent({
|
|
|
514
839
|
required: true
|
|
515
840
|
},
|
|
516
841
|
as: asProp("table"),
|
|
517
|
-
...virtualProps
|
|
842
|
+
...virtualProps,
|
|
843
|
+
...hierarchyProps
|
|
518
844
|
},
|
|
519
845
|
setup(props, { slots }) {
|
|
520
846
|
provideDataTable(props.table);
|
|
847
|
+
const hierarchy = provideHierarchy(props.table, props);
|
|
521
848
|
const element = shallowRef();
|
|
522
849
|
const render = (virtual) => h(props.as, {
|
|
523
|
-
|
|
850
|
+
role: props.table.tree ? "treegrid" : void 0,
|
|
851
|
+
"aria-rowcount": (props.table.tree ? props.table.processedRows.length : props.table.rowCount) + 1,
|
|
524
852
|
"aria-colcount": props.table.columns.length,
|
|
853
|
+
"aria-busy": props.table.loadState === "loading" || props.table.loadState === "streaming" ? "true" : void 0,
|
|
525
854
|
"data-empty": flag(props.table.rowCount === 0),
|
|
855
|
+
onKeydown: (event) => treeKeydown(event, hierarchy, virtual),
|
|
526
856
|
...virtual ? {
|
|
527
857
|
ref: element,
|
|
528
858
|
"data-virtual": ""
|
|
@@ -671,19 +1001,28 @@ const DataTableBody = defineComponent({
|
|
|
671
1001
|
setup(props, { slots }) {
|
|
672
1002
|
const table = injectDataTable("<DataTableBody>");
|
|
673
1003
|
const virtual = injectVirtual();
|
|
1004
|
+
const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length);
|
|
674
1005
|
return () => {
|
|
675
1006
|
if (props.virtual && !virtual) return h(VirtualDataTableBody, props, slots);
|
|
676
|
-
if (virtual) return renderTableBody(table, virtual, props.as, props.colspan, slots);
|
|
1007
|
+
if (virtual) return renderTableBody(table, virtual, props.as, props.colspan, slots, details);
|
|
677
1008
|
let content = slots.default?.({ rows: table.rows });
|
|
678
|
-
if (!content) content = table.rows.length === 0 && slots.empty ? h("tr", { "data-empty": "" }, [h("td", { colspan: Math.max(1, table.columns.length) }, slots.empty())]) : table.
|
|
679
|
-
key:
|
|
680
|
-
row
|
|
681
|
-
|
|
682
|
-
|
|
1009
|
+
if (!content) content = table.rows.length === 0 && slots.empty ? h("tr", { "data-empty": "" }, [h("td", { colspan: Math.max(1, table.columns.length) }, slots.empty())]) : table.renderItems.map((item) => item.kind === "row" ? slots.row ? h(Fragment, { key: item.key }, [slots.row(item)]) : h(DataTableRow, {
|
|
1010
|
+
key: item.key,
|
|
1011
|
+
row: item.row,
|
|
1012
|
+
rowIndex: item.rowIndex
|
|
1013
|
+
}) : details.render(item.row, item.rowIndex));
|
|
1014
|
+
const visibleDetails = new Set(table.renderItems.filter((item) => item.kind === "detail").map((item) => item.row.key));
|
|
1015
|
+
const retained = slots.default ? [] : details.retained(visibleDetails);
|
|
1016
|
+
const sentinel = loadSentinel(table, props.colspan ?? table.columns.length);
|
|
1017
|
+
return h(props.as, [
|
|
1018
|
+
...Array.isArray(content) ? content : content ? [content] : [],
|
|
1019
|
+
...retained,
|
|
1020
|
+
...sentinel ? [sentinel] : []
|
|
1021
|
+
]);
|
|
683
1022
|
};
|
|
684
1023
|
}
|
|
685
1024
|
});
|
|
686
|
-
function renderTableBody(table, virtual, as, colspan, slots, element) {
|
|
1025
|
+
function renderTableBody(table, virtual, as, colspan, slots, details, element) {
|
|
687
1026
|
const width = colspan ?? table.columns.length;
|
|
688
1027
|
const attrs = element ? { ref: element } : {};
|
|
689
1028
|
const content = slots.default?.({ rows: virtual.rowItems.filter((item) => item.renderItem.kind === "row").map((item) => item.renderItem.row) });
|
|
@@ -691,17 +1030,18 @@ function renderTableBody(table, virtual, as, colspan, slots, element) {
|
|
|
691
1030
|
if (content) return h(as, attrs, [
|
|
692
1031
|
rowSpacer(virtual.rows.paddingStart, width, "gap-start"),
|
|
693
1032
|
...content,
|
|
694
|
-
rowSpacer(virtual.rows.paddingEnd, width, "gap-end")
|
|
1033
|
+
rowSpacer(virtual.rows.paddingEnd, width, "gap-end"),
|
|
1034
|
+
...loadSentinel(table, width) ? [loadSentinel(table, width)] : []
|
|
1035
|
+
]);
|
|
1036
|
+
return h(as, attrs, [
|
|
1037
|
+
...renderRows(virtual, width, (item) => item.renderItem.kind === "row" ? slots.row ? h(Fragment, { key: item.key }, [slots.row(item.renderItem)]) : h(DataTableRow, {
|
|
1038
|
+
key: item.key,
|
|
1039
|
+
row: item.renderItem.row,
|
|
1040
|
+
rowIndex: item.renderItem.rowIndex
|
|
1041
|
+
}) : details.render(item.renderItem.row, item.renderItem.rowIndex)),
|
|
1042
|
+
...details.retained(new Set(virtual.rowItems.filter((item) => item.renderItem.kind === "detail").map((item) => item.renderItem.row.key))),
|
|
1043
|
+
...loadSentinel(table, width) ? [loadSentinel(table, width)] : []
|
|
695
1044
|
]);
|
|
696
|
-
return h(as, attrs, renderRows(virtual, width, (item) => item.renderItem.kind === "row" ? h(DataTableRow, {
|
|
697
|
-
key: item.key,
|
|
698
|
-
row: item.renderItem.row,
|
|
699
|
-
rowIndex: item.renderItem.rowIndex
|
|
700
|
-
}) : h("tr", {
|
|
701
|
-
key: item.key,
|
|
702
|
-
"data-detail": "",
|
|
703
|
-
ref: (target) => virtual.rows.measureElement(target, item.key)
|
|
704
|
-
}, [h("td", { colspan: Math.max(1, width) }, slots.detail?.(item.renderItem))])));
|
|
705
1045
|
}
|
|
706
1046
|
const VirtualDataTableBody = defineComponent({
|
|
707
1047
|
name: "DataTableVirtualBody",
|
|
@@ -718,7 +1058,8 @@ const VirtualDataTableBody = defineComponent({
|
|
|
718
1058
|
const table = injectDataTable("<DataTableBody>");
|
|
719
1059
|
const element = shallowRef();
|
|
720
1060
|
const virtual = createComponentVirtual(table, virtualOptions(props), () => element.value?.closest("table"));
|
|
721
|
-
|
|
1061
|
+
const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length);
|
|
1062
|
+
return () => renderTableBody(table, virtual, props.as, props.colspan, slots, details, element);
|
|
722
1063
|
}
|
|
723
1064
|
});
|
|
724
1065
|
/** A body row; `aria-selected` and `data-selected` follow the selection. */
|
|
@@ -736,6 +1077,7 @@ const DataTableRow = defineComponent({
|
|
|
736
1077
|
setup(props, { slots }) {
|
|
737
1078
|
const table = injectDataTable("<DataTableRow>");
|
|
738
1079
|
const virtual = injectVirtual();
|
|
1080
|
+
const hierarchy = injectHierarchy();
|
|
739
1081
|
return () => {
|
|
740
1082
|
const { row } = props;
|
|
741
1083
|
const selected = table.isSelected(row.key);
|
|
@@ -746,6 +1088,11 @@ const DataTableRow = defineComponent({
|
|
|
746
1088
|
"aria-selected": table.selectionMode === "none" ? void 0 : String(selected),
|
|
747
1089
|
"data-selected": flag(selected),
|
|
748
1090
|
"data-key": String(row.key),
|
|
1091
|
+
...hierarchyRowAttrs(table, row, hierarchy),
|
|
1092
|
+
tabindex: table.tree ? (hierarchy?.focusKey ?? table.rows[0]?.key) === row.key ? 0 : -1 : void 0,
|
|
1093
|
+
onFocus: () => {
|
|
1094
|
+
if (hierarchy) hierarchy.focus.value = row.key;
|
|
1095
|
+
},
|
|
749
1096
|
...virtual && item ? {
|
|
750
1097
|
ref: (element) => virtual.rows.measureElement(element, item.key),
|
|
751
1098
|
"data-virtual-row": ""
|
|
@@ -769,22 +1116,29 @@ const DataTableCell = defineComponent({
|
|
|
769
1116
|
props: {
|
|
770
1117
|
row: rowProp,
|
|
771
1118
|
column: columnProp,
|
|
772
|
-
as: asProp("td")
|
|
1119
|
+
as: asProp("td"),
|
|
1120
|
+
tree: {
|
|
1121
|
+
type: Boolean,
|
|
1122
|
+
default: true
|
|
1123
|
+
}
|
|
773
1124
|
},
|
|
774
1125
|
setup(props, { slots }) {
|
|
1126
|
+
const table = injectDataTable("<DataTableCell>");
|
|
1127
|
+
const hierarchy = injectHierarchy();
|
|
775
1128
|
return () => {
|
|
776
1129
|
const { row, column } = props;
|
|
777
1130
|
const value = row.getValue(column.id);
|
|
778
1131
|
const display = row.getDisplay(column.id);
|
|
779
|
-
|
|
780
|
-
"data-column": column.id,
|
|
781
|
-
"data-align": column.align
|
|
782
|
-
}, slots.default?.({
|
|
1132
|
+
const content = () => slots.default?.({
|
|
783
1133
|
row,
|
|
784
1134
|
column,
|
|
785
1135
|
value,
|
|
786
1136
|
display
|
|
787
|
-
}) ?? display
|
|
1137
|
+
}) ?? display;
|
|
1138
|
+
return h(props.as, {
|
|
1139
|
+
"data-column": column.id,
|
|
1140
|
+
"data-align": column.align
|
|
1141
|
+
}, props.tree && table.tree && column.id === resolveTreeColumn(table, hierarchy?.treeColumn) ? h(DataTableTreeCell, { row }, { default: content }) : content());
|
|
788
1142
|
};
|
|
789
1143
|
}
|
|
790
1144
|
});
|
|
@@ -833,6 +1187,7 @@ const DataGridRoot = defineComponent({
|
|
|
833
1187
|
default: void 0
|
|
834
1188
|
},
|
|
835
1189
|
...virtualProps,
|
|
1190
|
+
...hierarchyProps,
|
|
836
1191
|
/** Width reserved before data columns, for a row-number/header column. */
|
|
837
1192
|
gutter: {
|
|
838
1193
|
type: Number,
|
|
@@ -841,8 +1196,9 @@ const DataGridRoot = defineComponent({
|
|
|
841
1196
|
},
|
|
842
1197
|
setup(props, { slots, expose }) {
|
|
843
1198
|
provideDataTable(props.table);
|
|
844
|
-
const grid = useDataGrid(props.table);
|
|
1199
|
+
const grid = useDataGrid(props.table, { treeColumn: () => props.treeColumn });
|
|
845
1200
|
provideDataGrid(grid);
|
|
1201
|
+
provideHierarchy(props.table, props);
|
|
846
1202
|
const element = ref();
|
|
847
1203
|
const id = `vt-grid-${useId()}`;
|
|
848
1204
|
const focus = () => {
|
|
@@ -904,12 +1260,13 @@ const DataGridRoot = defineComponent({
|
|
|
904
1260
|
return h(props.as, {
|
|
905
1261
|
ref: element,
|
|
906
1262
|
id,
|
|
907
|
-
role: "grid",
|
|
1263
|
+
role: props.table.tree ? "treegrid" : "grid",
|
|
908
1264
|
tabindex: 0,
|
|
909
1265
|
"aria-label": props.label,
|
|
910
1266
|
"aria-multiselectable": "true",
|
|
911
|
-
"aria-rowcount": (binding ? props.table.rowCount : props.table.rows.length) + 1,
|
|
1267
|
+
"aria-rowcount": (props.table.tree ? props.table.processedRows.length : binding ? props.table.rowCount : props.table.rows.length) + 1,
|
|
912
1268
|
"aria-colcount": props.table.columns.length,
|
|
1269
|
+
"aria-busy": props.table.loadState === "loading" || props.table.loadState === "streaming" ? "true" : void 0,
|
|
913
1270
|
"aria-activedescendant": focusPosition && !grid.editor ? cellId(id, focusPosition.row, focusPosition.column) : void 0,
|
|
914
1271
|
onKeydown,
|
|
915
1272
|
onCopy: (event) => onCopy(event),
|
|
@@ -954,60 +1311,79 @@ const DataGridBody = defineComponent({
|
|
|
954
1311
|
colspan: {
|
|
955
1312
|
type: Number,
|
|
956
1313
|
default: void 0
|
|
1314
|
+
},
|
|
1315
|
+
treeCell: {
|
|
1316
|
+
type: Boolean,
|
|
1317
|
+
default: true
|
|
957
1318
|
}
|
|
958
1319
|
},
|
|
959
1320
|
setup(props, { slots }) {
|
|
960
1321
|
const table = injectDataTable("<DataGridBody>");
|
|
961
1322
|
const { grid } = useGrid("<DataGridBody>");
|
|
962
1323
|
const virtual = injectVirtual();
|
|
1324
|
+
const hierarchy = injectHierarchy();
|
|
1325
|
+
const details = createDetails(table, () => slots.detail, () => props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0));
|
|
963
1326
|
const element = ref();
|
|
964
1327
|
const render = (binding) => {
|
|
965
|
-
if (binding && slots.default)
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1328
|
+
if (binding && slots.default) {
|
|
1329
|
+
const sentinel = loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0));
|
|
1330
|
+
const content = slots.default({ rows: binding.rowItems.filter((item) => item.renderItem.kind === "row").map((item) => item.renderItem.row) });
|
|
1331
|
+
return h(props.as, { ref: element }, sentinel ? [...content, sentinel] : content);
|
|
1332
|
+
}
|
|
1333
|
+
if (binding) return h(props.as, { ref: element }, [
|
|
1334
|
+
...renderRows(binding, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0), (item) => {
|
|
1335
|
+
if (item.renderItem.kind !== "row") return details.render(item.renderItem.row, item.renderItem.rowIndex);
|
|
1336
|
+
const { row, rowIndex } = item.renderItem;
|
|
1337
|
+
const columns = renderColumns(table.columns, binding, (column, columnIndex) => h(DataGridCell, {
|
|
1338
|
+
key: column.id,
|
|
1339
|
+
row,
|
|
1340
|
+
column,
|
|
1341
|
+
rowIndex,
|
|
1342
|
+
columnIndex,
|
|
1343
|
+
tree: props.treeCell
|
|
1344
|
+
}, slots.cell ? { default: (cellProps) => slots.cell?.(cellProps) } : void 0), "td");
|
|
1345
|
+
return h("tr", {
|
|
1346
|
+
key: item.key,
|
|
1347
|
+
role: "row",
|
|
1348
|
+
"aria-rowindex": table.pageStart + rowIndex + 1,
|
|
1349
|
+
"data-key": String(row.key),
|
|
1350
|
+
...hierarchyRowAttrs(table, row, hierarchy),
|
|
1351
|
+
"data-virtual-row": "",
|
|
1352
|
+
ref: (target) => binding.rows.measureElement(target, item.key)
|
|
1353
|
+
}, [slots.rowHeader?.({
|
|
1354
|
+
row,
|
|
1355
|
+
index: rowIndex
|
|
1356
|
+
}), ...columns]);
|
|
1357
|
+
}),
|
|
1358
|
+
...details.retained(new Set(binding.rowItems.filter((item) => item.renderItem.kind === "detail").map((item) => item.renderItem.row.key))),
|
|
1359
|
+
...loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0)) ? [loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0))] : []
|
|
1360
|
+
]);
|
|
1361
|
+
const content = slots.default?.({ rows: table.rows }) ?? table.renderItems.map((item) => item.kind === "detail" ? details.render(item.row, item.rowIndex) : (() => {
|
|
1362
|
+
const { row, rowIndex } = item;
|
|
984
1363
|
return h("tr", {
|
|
985
1364
|
key: item.key,
|
|
986
1365
|
role: "row",
|
|
987
|
-
"aria-rowindex":
|
|
1366
|
+
"aria-rowindex": rowIndex + 2,
|
|
988
1367
|
"data-key": String(row.key),
|
|
989
|
-
|
|
990
|
-
ref: (target) => binding.rows.measureElement(target, item.key)
|
|
1368
|
+
...hierarchyRowAttrs(table, row, hierarchy)
|
|
991
1369
|
}, [slots.rowHeader?.({
|
|
992
1370
|
row,
|
|
993
1371
|
index: rowIndex
|
|
994
|
-
}), ...columns
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
row,
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
columnIndex
|
|
1010
|
-
}, slots.cell ? { default: (cellProps) => slots.cell?.(cellProps) } : void 0))])));
|
|
1372
|
+
}), ...table.columns.map((column, columnIndex) => h(DataGridCell, {
|
|
1373
|
+
key: column.id,
|
|
1374
|
+
row,
|
|
1375
|
+
column,
|
|
1376
|
+
rowIndex,
|
|
1377
|
+
columnIndex,
|
|
1378
|
+
tree: props.treeCell
|
|
1379
|
+
}, slots.cell ? { default: (cellProps) => slots.cell?.(cellProps) } : void 0))]);
|
|
1380
|
+
})());
|
|
1381
|
+
const sentinel = loadSentinel(table, props.colspan ?? table.columns.length + (slots.rowHeader ? 1 : 0));
|
|
1382
|
+
return h(props.as, [
|
|
1383
|
+
...content,
|
|
1384
|
+
...slots.default ? [] : details.retained(new Set(table.renderItems.filter((item) => item.kind === "detail").map((item) => item.row.key))),
|
|
1385
|
+
sentinel
|
|
1386
|
+
]);
|
|
1011
1387
|
};
|
|
1012
1388
|
return () => props.virtual && !virtual ? h(VirtualContent, {
|
|
1013
1389
|
table,
|
|
@@ -1075,6 +1451,10 @@ const DataGridCell = defineComponent({
|
|
|
1075
1451
|
row: rowProp,
|
|
1076
1452
|
column: columnProp,
|
|
1077
1453
|
as: asProp("td"),
|
|
1454
|
+
tree: {
|
|
1455
|
+
type: Boolean,
|
|
1456
|
+
default: true
|
|
1457
|
+
},
|
|
1078
1458
|
/** The row's position on the page. Found by searching the rows when left out. */
|
|
1079
1459
|
rowIndex: {
|
|
1080
1460
|
type: Number,
|
|
@@ -1090,6 +1470,7 @@ const DataGridCell = defineComponent({
|
|
|
1090
1470
|
const table = injectDataTable("<DataGridCell>");
|
|
1091
1471
|
const { grid, context } = useGrid("<DataGridCell>");
|
|
1092
1472
|
const virtual = injectVirtual();
|
|
1473
|
+
const hierarchy = injectHierarchy();
|
|
1093
1474
|
const element = ref();
|
|
1094
1475
|
const position = () => ({
|
|
1095
1476
|
row: props.rowIndex ?? table.rows.indexOf(props.row),
|
|
@@ -1115,13 +1496,16 @@ const DataGridCell = defineComponent({
|
|
|
1115
1496
|
commit: (then) => grid.commitEdit(then),
|
|
1116
1497
|
cancel: () => grid.cancelEdit()
|
|
1117
1498
|
}) ?? h(DataGridEditor, { label: `Edit ${column.header}` });
|
|
1118
|
-
else
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1499
|
+
else {
|
|
1500
|
+
const children = () => slots.default?.({
|
|
1501
|
+
row,
|
|
1502
|
+
column,
|
|
1503
|
+
value,
|
|
1504
|
+
display,
|
|
1505
|
+
editable
|
|
1506
|
+
}) ?? display;
|
|
1507
|
+
content = props.tree && table.tree && column.id === resolveTreeColumn(table, hierarchy?.treeColumn) ? h(DataTableTreeCell, { row }, { default: children }) : children();
|
|
1508
|
+
}
|
|
1125
1509
|
return h(props.as, {
|
|
1126
1510
|
ref: element,
|
|
1127
1511
|
id: cellId(context.id, at.row, at.column),
|
|
@@ -1154,5 +1538,5 @@ const DataGridCell = defineComponent({
|
|
|
1154
1538
|
});
|
|
1155
1539
|
|
|
1156
1540
|
//#endregion
|
|
1157
|
-
export { DataGridBody, DataGridCell, DataGridRoot, DataTableBody, DataTableCaption, DataTableCell, DataTableColumnVisibility, DataTableEmpty, DataTableHeader, DataTableHeaderCell, DataTableHeaderRow, DataTablePageSize, DataTablePagination, DataTableRoot, DataTableRow, DataTableSearch, DataTableSelectAll, DataTableSelectRow, DataTableSortButton, DataTableStatus, DataTableViewport, DataTableVirtualColumns, cellId, columnStyle, injectVirtual as injectVirtualRenderer, virtualProps };
|
|
1541
|
+
export { DataGridBody, DataGridCell, DataGridRoot, DataTableBody, DataTableCaption, DataTableCell, DataTableColumnVisibility, DataTableDetailRow, DataTableEmpty, DataTableExpandToggle, DataTableHeader, DataTableHeaderCell, DataTableHeaderRow, DataTableLoadMore, DataTablePageSize, DataTablePagination, DataTableRoot, DataTableRow, DataTableSearch, DataTableSelectAll, DataTableSelectRow, DataTableSortButton, DataTableStatus, DataTableTreeCell, DataTableViewport, DataTableVirtualColumns, cellId, columnStyle, expandToggleProps, hierarchyProps, injectVirtual as injectVirtualRenderer, resolveTreeColumn, treeCellProps, virtualProps };
|
|
1158
1542
|
//# sourceMappingURL=index.js.map
|