@ui-organized/svelte-table 0.0.0-stage → 6.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 +99 -2
- package/dist/DataTable/DataTable.svelte +62 -0
- package/dist/DataTable/DataTable.svelte.d.ts +29 -0
- package/dist/DataTable/DataTable.svelte.d.ts.map +1 -0
- package/dist/DataTable/DataTable.test.svelte.d.ts +2 -0
- package/dist/DataTable/DataTable.test.svelte.d.ts.map +1 -0
- package/dist/DataTable/DataTable.test.svelte.js +360 -0
- package/dist/DataTable/index.d.ts +2 -0
- package/dist/DataTable/index.d.ts.map +1 -0
- package/dist/DataTable/index.js +1 -0
- package/dist/components/Table/CellEditor.svelte +29 -0
- package/dist/components/Table/CellEditor.svelte.d.ts +8 -0
- package/dist/components/Table/CellEditor.svelte.d.ts.map +1 -0
- package/dist/components/Table/MoveButtons.svelte +60 -0
- package/dist/components/Table/MoveButtons.svelte.d.ts +8 -0
- package/dist/components/Table/MoveButtons.svelte.d.ts.map +1 -0
- package/dist/components/Table/ResizeHandle.svelte +82 -0
- package/dist/components/Table/ResizeHandle.svelte.d.ts +28 -0
- package/dist/components/Table/ResizeHandle.svelte.d.ts.map +1 -0
- package/dist/components/Table/Table.svelte +17 -0
- package/dist/components/Table/Table.svelte.d.ts +9 -0
- package/dist/components/Table/Table.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableBody.svelte +78 -0
- package/dist/components/Table/TableBody.svelte.d.ts +30 -0
- package/dist/components/Table/TableBody.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableCell.svelte +106 -0
- package/dist/components/Table/TableCell.svelte.d.ts +34 -0
- package/dist/components/Table/TableCell.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableFooter.svelte +34 -0
- package/dist/components/Table/TableFooter.svelte.d.ts +9 -0
- package/dist/components/Table/TableFooter.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableHeadCell.svelte +157 -0
- package/dist/components/Table/TableHeadCell.svelte.d.ts +29 -0
- package/dist/components/Table/TableHeadCell.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableHeader.svelte +22 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +7 -0
- package/dist/components/Table/TableHeader.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableRow.svelte +84 -0
- package/dist/components/Table/TableRow.svelte.d.ts +32 -0
- package/dist/components/Table/TableRow.svelte.d.ts.map +1 -0
- package/dist/components/Table/TableViewport.svelte +69 -0
- package/dist/components/Table/TableViewport.svelte.d.ts +10 -0
- package/dist/components/Table/TableViewport.svelte.d.ts.map +1 -0
- package/dist/components/Table/index.d.ts +23 -0
- package/dist/components/Table/index.d.ts.map +1 -0
- package/dist/components/Table/index.js +22 -0
- package/dist/components/TableCards/TableCard.svelte +96 -0
- package/dist/components/TableCards/TableCard.svelte.d.ts +31 -0
- package/dist/components/TableCards/TableCard.svelte.d.ts.map +1 -0
- package/dist/components/TableCards/TableCards.svelte +61 -0
- package/dist/components/TableCards/TableCards.svelte.d.ts +7 -0
- package/dist/components/TableCards/TableCards.svelte.d.ts.map +1 -0
- package/dist/components/TableCards/index.d.ts +3 -0
- package/dist/components/TableCards/index.d.ts.map +1 -0
- package/dist/components/TableCards/index.js +2 -0
- package/dist/components/TableDetailSheet/TableDetailSheet.svelte +130 -0
- package/dist/components/TableDetailSheet/TableDetailSheet.svelte.d.ts +8 -0
- package/dist/components/TableDetailSheet/TableDetailSheet.svelte.d.ts.map +1 -0
- package/dist/components/TableDetailSheet/index.d.ts +2 -0
- package/dist/components/TableDetailSheet/index.d.ts.map +1 -0
- package/dist/components/TableDetailSheet/index.js +1 -0
- package/dist/components/TableFilters/TableFilterAdd.svelte +89 -0
- package/dist/components/TableFilters/TableFilterAdd.svelte.d.ts +15 -0
- package/dist/components/TableFilters/TableFilterAdd.svelte.d.ts.map +1 -0
- package/dist/components/TableFilters/TableFilterChip.svelte +164 -0
- package/dist/components/TableFilters/TableFilterChip.svelte.d.ts +9 -0
- package/dist/components/TableFilters/TableFilterChip.svelte.d.ts.map +1 -0
- package/dist/components/TableFilters/TableFilterEditor.svelte +40 -0
- package/dist/components/TableFilters/TableFilterEditor.svelte.d.ts +9 -0
- package/dist/components/TableFilters/TableFilterEditor.svelte.d.ts.map +1 -0
- package/dist/components/TableFilters/TableFilterValueEditor.svelte +209 -0
- package/dist/components/TableFilters/TableFilterValueEditor.svelte.d.ts +10 -0
- package/dist/components/TableFilters/TableFilterValueEditor.svelte.d.ts.map +1 -0
- package/dist/components/TableFilters/TableFilters.svelte +51 -0
- package/dist/components/TableFilters/TableFilters.svelte.d.ts +7 -0
- package/dist/components/TableFilters/TableFilters.svelte.d.ts.map +1 -0
- package/dist/components/TableFilters/index.d.ts +7 -0
- package/dist/components/TableFilters/index.d.ts.map +1 -0
- package/dist/components/TableFilters/index.js +6 -0
- package/dist/components/TableFilters/useAutoFocusField.d.ts +30 -0
- package/dist/components/TableFilters/useAutoFocusField.d.ts.map +1 -0
- package/dist/components/TableFilters/useAutoFocusField.js +59 -0
- package/dist/components/TablePagination/TablePagination.svelte +61 -0
- package/dist/components/TablePagination/TablePagination.svelte.d.ts +9 -0
- package/dist/components/TablePagination/TablePagination.svelte.d.ts.map +1 -0
- package/dist/components/TablePagination/index.d.ts +2 -0
- package/dist/components/TablePagination/index.d.ts.map +1 -0
- package/dist/components/TablePagination/index.js +1 -0
- package/dist/components/TableRowActions/TableRowActions.svelte +68 -0
- package/dist/components/TableRowActions/TableRowActions.svelte.d.ts +37 -0
- package/dist/components/TableRowActions/TableRowActions.svelte.d.ts.map +1 -0
- package/dist/components/TableRowActions/index.d.ts +2 -0
- package/dist/components/TableRowActions/index.d.ts.map +1 -0
- package/dist/components/TableRowActions/index.js +1 -0
- package/dist/components/TableSelectionBar/TableSelectionBar.svelte +88 -0
- package/dist/components/TableSelectionBar/TableSelectionBar.svelte.d.ts +30 -0
- package/dist/components/TableSelectionBar/TableSelectionBar.svelte.d.ts.map +1 -0
- package/dist/components/TableSelectionBar/index.d.ts +2 -0
- package/dist/components/TableSelectionBar/index.d.ts.map +1 -0
- package/dist/components/TableSelectionBar/index.js +1 -0
- package/dist/components/TableSelectionFooter/TableSelectionFooter.svelte +38 -0
- package/dist/components/TableSelectionFooter/TableSelectionFooter.svelte.d.ts +7 -0
- package/dist/components/TableSelectionFooter/TableSelectionFooter.svelte.d.ts.map +1 -0
- package/dist/components/TableSelectionFooter/index.d.ts +2 -0
- package/dist/components/TableSelectionFooter/index.d.ts.map +1 -0
- package/dist/components/TableSelectionFooter/index.js +1 -0
- package/dist/components/TableSheets/TableFilterSheet.svelte +150 -0
- package/dist/components/TableSheets/TableFilterSheet.svelte.d.ts +8 -0
- package/dist/components/TableSheets/TableFilterSheet.svelte.d.ts.map +1 -0
- package/dist/components/TableSheets/TableFilterSheetCondition.svelte +53 -0
- package/dist/components/TableSheets/TableFilterSheetCondition.svelte.d.ts +9 -0
- package/dist/components/TableSheets/TableFilterSheetCondition.svelte.d.ts.map +1 -0
- package/dist/components/TableSheets/TableSheetButton.svelte +44 -0
- package/dist/components/TableSheets/TableSheetButton.svelte.d.ts +15 -0
- package/dist/components/TableSheets/TableSheetButton.svelte.d.ts.map +1 -0
- package/dist/components/TableSheets/TableSortSheet.svelte +100 -0
- package/dist/components/TableSheets/TableSortSheet.svelte.d.ts +8 -0
- package/dist/components/TableSheets/TableSortSheet.svelte.d.ts.map +1 -0
- package/dist/components/TableSheets/index.d.ts +4 -0
- package/dist/components/TableSheets/index.d.ts.map +1 -0
- package/dist/components/TableSheets/index.js +3 -0
- package/dist/components/TableStates/TableEmpty.svelte +35 -0
- package/dist/components/TableStates/TableEmpty.svelte.d.ts +11 -0
- package/dist/components/TableStates/TableEmpty.svelte.d.ts.map +1 -0
- package/dist/components/TableStates/TableError.svelte +26 -0
- package/dist/components/TableStates/TableError.svelte.d.ts +10 -0
- package/dist/components/TableStates/TableError.svelte.d.ts.map +1 -0
- package/dist/components/TableStates/TableLoading.svelte +33 -0
- package/dist/components/TableStates/TableLoading.svelte.d.ts +8 -0
- package/dist/components/TableStates/TableLoading.svelte.d.ts.map +1 -0
- package/dist/components/TableStates/index.d.ts +4 -0
- package/dist/components/TableStates/index.d.ts.map +1 -0
- package/dist/components/TableStates/index.js +3 -0
- package/dist/components/TableSubBar/TableSubBar.svelte +34 -0
- package/dist/components/TableSubBar/TableSubBar.svelte.d.ts +7 -0
- package/dist/components/TableSubBar/TableSubBar.svelte.d.ts.map +1 -0
- package/dist/components/TableSubBar/index.d.ts +2 -0
- package/dist/components/TableSubBar/index.d.ts.map +1 -0
- package/dist/components/TableSubBar/index.js +1 -0
- package/dist/components/TableToolbar/TableActions.svelte +40 -0
- package/dist/components/TableToolbar/TableActions.svelte.d.ts +9 -0
- package/dist/components/TableToolbar/TableActions.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableExportMenu.svelte +60 -0
- package/dist/components/TableToolbar/TableExportMenu.svelte.d.ts +8 -0
- package/dist/components/TableToolbar/TableExportMenu.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableReorderToggle.svelte +27 -0
- package/dist/components/TableToolbar/TableReorderToggle.svelte.d.ts +7 -0
- package/dist/components/TableToolbar/TableReorderToggle.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableScrollButtons.svelte +54 -0
- package/dist/components/TableToolbar/TableScrollButtons.svelte.d.ts +9 -0
- package/dist/components/TableToolbar/TableScrollButtons.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableSearch.svelte +24 -0
- package/dist/components/TableToolbar/TableSearch.svelte.d.ts +9 -0
- package/dist/components/TableToolbar/TableSearch.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableSortMenu.svelte +97 -0
- package/dist/components/TableToolbar/TableSortMenu.svelte.d.ts +8 -0
- package/dist/components/TableToolbar/TableSortMenu.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableToolbar.svelte +113 -0
- package/dist/components/TableToolbar/TableToolbar.svelte.d.ts +9 -0
- package/dist/components/TableToolbar/TableToolbar.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/TableViewOptions.svelte +69 -0
- package/dist/components/TableToolbar/TableViewOptions.svelte.d.ts +8 -0
- package/dist/components/TableToolbar/TableViewOptions.svelte.d.ts.map +1 -0
- package/dist/components/TableToolbar/index.d.ts +9 -0
- package/dist/components/TableToolbar/index.d.ts.map +1 -0
- package/dist/components/TableToolbar/index.js +8 -0
- package/dist/core/ActionsHeader.svelte +8 -0
- package/dist/core/ActionsHeader.svelte.d.ts +27 -0
- package/dist/core/ActionsHeader.svelte.d.ts.map +1 -0
- package/dist/core/SelectAllCell.svelte +21 -0
- package/dist/core/SelectAllCell.svelte.d.ts +19 -0
- package/dist/core/SelectAllCell.svelte.d.ts.map +1 -0
- package/dist/core/SelectCell.svelte +37 -0
- package/dist/core/SelectCell.svelte.d.ts +8 -0
- package/dist/core/SelectCell.svelte.d.ts.map +1 -0
- package/dist/core/createDataTable.svelte.d.ts +4 -0
- package/dist/core/createDataTable.svelte.d.ts.map +1 -0
- package/dist/core/createDataTable.svelte.js +1152 -0
- package/dist/core/createDataTable.test.svelte.d.ts +2 -0
- package/dist/core/createDataTable.test.svelte.d.ts.map +1 -0
- package/dist/core/createDataTable.test.svelte.js +156 -0
- package/dist/core/createDeferred.svelte.d.ts +38 -0
- package/dist/core/createDeferred.svelte.d.ts.map +1 -0
- package/dist/core/createDeferred.svelte.js +81 -0
- package/dist/core/elementRef.d.ts +17 -0
- package/dist/core/elementRef.d.ts.map +1 -0
- package/dist/core/elementRef.js +20 -0
- package/dist/core/props.d.ts +3 -0
- package/dist/core/props.d.ts.map +1 -0
- package/dist/core/props.js +46 -0
- package/dist/core/systemColumns.d.ts +16 -0
- package/dist/core/systemColumns.d.ts.map +1 -0
- package/dist/core/systemColumns.js +53 -0
- package/dist/core/tableContext.d.ts +7 -0
- package/dist/core/tableContext.d.ts.map +1 -0
- package/dist/core/tableContext.js +30 -0
- package/dist/core/trackStore.svelte.d.ts +24 -0
- package/dist/core/trackStore.svelte.d.ts.map +1 -0
- package/dist/core/trackStore.svelte.js +28 -0
- package/dist/core/types.d.ts +397 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/styles.css +895 -0
- package/package.json +77 -3
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { createRawSnippet, mount, unmount, flushSync } from "svelte";
|
|
3
|
+
import DataTable from "./DataTable.svelte";
|
|
4
|
+
const DATA = Array.from({ length: 8 }, (_, index) => ({
|
|
5
|
+
id: `m-${index}`,
|
|
6
|
+
name: `Name ${index}`,
|
|
7
|
+
}));
|
|
8
|
+
const COLUMNS = [
|
|
9
|
+
{ accessorKey: "name", header: "Name", meta: { primary: true } },
|
|
10
|
+
];
|
|
11
|
+
function render(props = {}) {
|
|
12
|
+
const target = document.createElement("div");
|
|
13
|
+
document.body.appendChild(target);
|
|
14
|
+
const app = mount(DataTable, {
|
|
15
|
+
target,
|
|
16
|
+
props: { data: DATA, columns: COLUMNS, label: "Members", getRowId: (r) => r.id, ...props },
|
|
17
|
+
});
|
|
18
|
+
flushSync();
|
|
19
|
+
return {
|
|
20
|
+
target,
|
|
21
|
+
destroy: () => {
|
|
22
|
+
unmount(app);
|
|
23
|
+
target.remove();
|
|
24
|
+
},
|
|
25
|
+
rowIds: () => [...target.querySelectorAll("tbody tr[data-row-id]")].map((row) => row.getAttribute("data-row-id")),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
describe("<DataTable>", () => {
|
|
29
|
+
it("renders a table with a caption, a header and every row", () => {
|
|
30
|
+
const view = render();
|
|
31
|
+
expect(view.target.querySelector("caption")?.textContent).toBe("Members");
|
|
32
|
+
expect(view.target.querySelectorAll("thead th")).toHaveLength(1);
|
|
33
|
+
expect(view.rowIds()).toHaveLength(8);
|
|
34
|
+
view.destroy();
|
|
35
|
+
});
|
|
36
|
+
it("pages forward when the next control is used", () => {
|
|
37
|
+
const view = render({ paginated: true, pageSize: 3 });
|
|
38
|
+
expect(view.rowIds()).toEqual(["m-0", "m-1", "m-2"]);
|
|
39
|
+
const next = view.target.querySelector('.data-table__pagination button[aria-label="Next page"]');
|
|
40
|
+
expect(next, "the next-page control is rendered").not.toBeNull();
|
|
41
|
+
next.click();
|
|
42
|
+
flushSync();
|
|
43
|
+
expect(view.rowIds()).toEqual(["m-3", "m-4", "m-5"]);
|
|
44
|
+
view.destroy();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* While rows are selected, the bulk actions take the place of the toolbar's
|
|
49
|
+
* own controls: deciding what to do with the selection comes before sorting or
|
|
50
|
+
* filtering further. Asserted structurally — which element holds what — because
|
|
51
|
+
* that is the part a stylesheet cannot recover from.
|
|
52
|
+
*/
|
|
53
|
+
describe("the selection's bulk actions", () => {
|
|
54
|
+
const withBulk = (defaultFilters) => render({
|
|
55
|
+
// Filterable, so the toolbar has an "Add filter" control to give way.
|
|
56
|
+
columns: [{ accessorKey: "name", header: "Name", meta: { primary: true, filter: true } }],
|
|
57
|
+
selection: "multiple",
|
|
58
|
+
defaultFilters,
|
|
59
|
+
bulkActions: [{ id: "export", label: "Export", onRun: () => { } }],
|
|
60
|
+
});
|
|
61
|
+
const select = (target) => {
|
|
62
|
+
target
|
|
63
|
+
.querySelector('[data-cell="0:1"]')
|
|
64
|
+
?.dispatchEvent(new KeyboardEvent("keydown", { key: " ", bubbles: true }));
|
|
65
|
+
flushSync();
|
|
66
|
+
};
|
|
67
|
+
const toolbar = (target) => target.querySelector(".data-table__toolbar");
|
|
68
|
+
it("are absent, and the sub-bar too, while nothing is filtered or selected", () => {
|
|
69
|
+
const view = withBulk();
|
|
70
|
+
expect(view.target.querySelector(".data-table__selection-bar")).toBeNull();
|
|
71
|
+
expect(view.target.querySelector(".data-table__subbar")).toBeNull();
|
|
72
|
+
expect(toolbar(view.target)?.querySelector('[aria-label="Add filter"]')).not.toBeNull();
|
|
73
|
+
view.destroy();
|
|
74
|
+
});
|
|
75
|
+
it("replace the sort, filter and table actions in the toolbar row", () => {
|
|
76
|
+
const view = withBulk();
|
|
77
|
+
select(view.target);
|
|
78
|
+
const bar = toolbar(view.target)?.querySelector(".data-table__selection-bar");
|
|
79
|
+
expect(bar).not.toBeNull();
|
|
80
|
+
// A plain labelled group, not a `role="toolbar"` with its own surface.
|
|
81
|
+
expect(bar?.getAttribute("role")).toBe("group");
|
|
82
|
+
expect(bar?.classList.contains("toolbar")).toBe(false);
|
|
83
|
+
expect(toolbar(view.target)?.querySelector('[aria-label="Add filter"]')).toBeNull();
|
|
84
|
+
// Search stays: it narrows what is on screen, not what is selected.
|
|
85
|
+
expect(toolbar(view.target)?.querySelector('input[type="search"]')).not.toBeNull();
|
|
86
|
+
expect(view.target.querySelectorAll(".data-table__selection-bar")).toHaveLength(1);
|
|
87
|
+
view.destroy();
|
|
88
|
+
});
|
|
89
|
+
it("leave the filter chips on the line below", () => {
|
|
90
|
+
const view = withBulk([{ columnId: "name", operator: "contains", values: ["Name"] }]);
|
|
91
|
+
select(view.target);
|
|
92
|
+
const subbar = view.target.querySelector(".data-table__subbar");
|
|
93
|
+
expect(subbar?.querySelector(".data-table__filters")).not.toBeNull();
|
|
94
|
+
expect(subbar?.querySelector(".data-table__selection-bar")).toBeNull();
|
|
95
|
+
view.destroy();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* Column reordering: header move buttons, a toolbar-switched mode, and
|
|
100
|
+
* Alt+Arrow on a focused header. Mirrors React's "column reordering"; dragging
|
|
101
|
+
* is native drag and drop, which jsdom does not implement.
|
|
102
|
+
*/
|
|
103
|
+
describe("column reordering", () => {
|
|
104
|
+
const COLS = [
|
|
105
|
+
{ accessorKey: "name", header: "Name", meta: { primary: true } },
|
|
106
|
+
{ accessorKey: "role", header: "Role" },
|
|
107
|
+
{ accessorKey: "seats", header: "Seats" },
|
|
108
|
+
];
|
|
109
|
+
const ROWS = [
|
|
110
|
+
{ id: "a", name: "Ada", role: "Engineer", seats: 3 },
|
|
111
|
+
{ id: "b", name: "Grace", role: "Admin", seats: 1 },
|
|
112
|
+
];
|
|
113
|
+
const reorderable = (setting, extra = {}) => render({ data: ROWS, columns: COLS, label: "Team", reorderable: setting, ...extra });
|
|
114
|
+
const headers = (target) => [...target.querySelectorAll("thead th")]
|
|
115
|
+
.map((cell) => cell.textContent?.replace(/\s+/g, " ").trim())
|
|
116
|
+
.filter(Boolean);
|
|
117
|
+
const button = (target, label) => target.querySelector(`button[aria-label="${label}"]`);
|
|
118
|
+
const click = (element) => {
|
|
119
|
+
element?.click();
|
|
120
|
+
flushSync();
|
|
121
|
+
};
|
|
122
|
+
it("moves a column one place with its header buttons, disabled at the ends", () => {
|
|
123
|
+
const view = reorderable("always");
|
|
124
|
+
expect(headers(view.target)).toEqual(["Name", "Role", "Seats"]);
|
|
125
|
+
expect(button(view.target, "Move Name left")?.disabled).toBe(true);
|
|
126
|
+
expect(button(view.target, "Move Seats right")?.disabled).toBe(true);
|
|
127
|
+
click(button(view.target, "Move Name right"));
|
|
128
|
+
expect(headers(view.target)).toEqual(["Role", "Name", "Seats"]);
|
|
129
|
+
click(button(view.target, "Move Seats left"));
|
|
130
|
+
expect(headers(view.target)).toEqual(["Role", "Seats", "Name"]);
|
|
131
|
+
view.destroy();
|
|
132
|
+
});
|
|
133
|
+
it("in toggle mode, offers moving only inside reorder mode", () => {
|
|
134
|
+
const view = reorderable("toggle");
|
|
135
|
+
expect(button(view.target, "Move Name right")).toBeNull();
|
|
136
|
+
expect(view.target.querySelector('.data-table__head-inner[draggable="true"]')).toBeNull();
|
|
137
|
+
click(button(view.target, "Reorder columns"));
|
|
138
|
+
expect(button(view.target, "Move Name right")).not.toBeNull();
|
|
139
|
+
expect(view.target.querySelectorAll('.data-table__head-inner[draggable="true"]')).toHaveLength(3);
|
|
140
|
+
click(button(view.target, "Done reordering columns"));
|
|
141
|
+
expect(button(view.target, "Move Name right")).toBeNull();
|
|
142
|
+
view.destroy();
|
|
143
|
+
});
|
|
144
|
+
it("leaves the table's own columns where they are", () => {
|
|
145
|
+
const view = reorderable("always", { selection: "multiple" });
|
|
146
|
+
const select = view.target.querySelector("th.data-table__select-cell");
|
|
147
|
+
expect(select?.querySelector('[draggable="true"]')).toBeNull();
|
|
148
|
+
expect(select?.querySelector(".data-table__move")).toBeNull();
|
|
149
|
+
expect(button(view.target, "Move Name left")?.disabled).toBe(true);
|
|
150
|
+
view.destroy();
|
|
151
|
+
});
|
|
152
|
+
it("moves the focused header's column with Alt+Arrow, and focus goes with it", () => {
|
|
153
|
+
const view = reorderable("always", { selection: "multiple" });
|
|
154
|
+
const name = view.target.querySelector('[data-cell="-1:1"]');
|
|
155
|
+
name?.focus();
|
|
156
|
+
flushSync();
|
|
157
|
+
name?.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight", altKey: true, bubbles: true }));
|
|
158
|
+
flushSync();
|
|
159
|
+
expect(headers(view.target)).toEqual(["Role", "Name", "Seats"]);
|
|
160
|
+
expect(view.target.querySelector('[data-cell][tabindex="0"]')?.getAttribute("data-cell")).toBe("-1:2");
|
|
161
|
+
view.destroy();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
/**
|
|
165
|
+
* `meta.date`: US-formatted strings in, the column's own format out, sorted as
|
|
166
|
+
* dates. Mirrors React's "date columns".
|
|
167
|
+
*/
|
|
168
|
+
describe("date columns", () => {
|
|
169
|
+
const parseUs = (value) => {
|
|
170
|
+
const [m, d, y] = String(value).split("/").map(Number);
|
|
171
|
+
return y && m && d ? new Date(y, m - 1, d) : null;
|
|
172
|
+
};
|
|
173
|
+
const dmy = (date) => `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`;
|
|
174
|
+
it("shows each date in the column's format, sorted as dates rather than strings", () => {
|
|
175
|
+
const view = render({
|
|
176
|
+
data: [
|
|
177
|
+
{ id: "a", name: "Ada", joined: "2/1/2026" },
|
|
178
|
+
{ id: "b", name: "Grace", joined: "11/10/2025" },
|
|
179
|
+
{ id: "c", name: "Alan", joined: "12/31/2025" },
|
|
180
|
+
],
|
|
181
|
+
columns: [
|
|
182
|
+
{ accessorKey: "name", header: "Name", meta: { primary: true } },
|
|
183
|
+
{ accessorKey: "joined", header: "Joined", meta: { date: { parse: parseUs, format: dmy } } },
|
|
184
|
+
],
|
|
185
|
+
defaultSorting: [{ id: "joined", desc: false }],
|
|
186
|
+
});
|
|
187
|
+
const joined = [...view.target.querySelectorAll("tbody tr")].map((row) => row.lastElementChild?.textContent?.trim());
|
|
188
|
+
expect(joined).toEqual(["10.11.2025", "31.12.2025", "1.2.2026"]);
|
|
189
|
+
view.destroy();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* How many rows are selected, and the offer to reach the ones off screen, sit
|
|
194
|
+
* under the rows rather than in the toolbar. Mirrors React's "the selection
|
|
195
|
+
* footer".
|
|
196
|
+
*/
|
|
197
|
+
describe("the selection footer", () => {
|
|
198
|
+
const paged = () => render({ selection: "multiple", paginated: true, pageSize: 4 });
|
|
199
|
+
const key = (target, init) => {
|
|
200
|
+
target
|
|
201
|
+
.querySelector('[data-cell="0:1"]')
|
|
202
|
+
?.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, ...init }));
|
|
203
|
+
flushSync();
|
|
204
|
+
};
|
|
205
|
+
const footer = (target) => target.querySelector(".data-table__selection-footer");
|
|
206
|
+
it("is absent while nothing is selected", () => {
|
|
207
|
+
const view = paged();
|
|
208
|
+
expect(footer(view.target)).toBeNull();
|
|
209
|
+
view.destroy();
|
|
210
|
+
});
|
|
211
|
+
it("rides inside the viewport, after the table, and holds the count", () => {
|
|
212
|
+
const view = paged();
|
|
213
|
+
key(view.target, { key: " " });
|
|
214
|
+
const bar = footer(view.target);
|
|
215
|
+
expect(bar?.parentElement?.classList.contains("data-table__viewport")).toBe(true);
|
|
216
|
+
expect(bar?.previousElementSibling?.tagName).toBe("TABLE");
|
|
217
|
+
expect(footer(view.target)?.querySelector(".data-table__selection-count")?.textContent?.trim()).toBe("1 row selected");
|
|
218
|
+
expect(view.target.querySelectorAll(".data-table__selection-count")).toHaveLength(1);
|
|
219
|
+
expect(footer(view.target)?.querySelector("button")).toBeNull();
|
|
220
|
+
view.destroy();
|
|
221
|
+
});
|
|
222
|
+
it("offers the hidden rows as a real button once the page is fully selected", () => {
|
|
223
|
+
const view = paged();
|
|
224
|
+
key(view.target, { key: " " });
|
|
225
|
+
for (let i = 0; i < 3; i++)
|
|
226
|
+
key(view.target, { key: "ArrowDown", shiftKey: true });
|
|
227
|
+
const offer = footer(view.target)?.querySelector("button");
|
|
228
|
+
expect(offer?.classList.contains("btn")).toBe(true);
|
|
229
|
+
// Four of eight on the page are selected, so four are hidden.
|
|
230
|
+
expect(offer?.textContent?.trim()).toBe("Select 4 hidden rows");
|
|
231
|
+
offer?.click();
|
|
232
|
+
flushSync();
|
|
233
|
+
expect(footer(view.target)?.querySelector(".data-table__selection-count")?.textContent?.trim()).toBe("8 rows selected");
|
|
234
|
+
expect(footer(view.target)?.querySelector("button")).toBeNull();
|
|
235
|
+
view.destroy();
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
/**
|
|
239
|
+
* Card mode is the phone layout. Sort and filter open sheets from labelled
|
|
240
|
+
* buttons that count what is applied, the chips row goes away, and each card
|
|
241
|
+
* puts its controls in the top-right corner, checkbox last.
|
|
242
|
+
*/
|
|
243
|
+
describe("card mode", () => {
|
|
244
|
+
const FILTERABLE = [
|
|
245
|
+
{ accessorKey: "name", header: "Name", meta: { primary: true, filter: true } },
|
|
246
|
+
];
|
|
247
|
+
const cards = (defaultFilters) => render({
|
|
248
|
+
columns: FILTERABLE,
|
|
249
|
+
selection: "multiple",
|
|
250
|
+
responsive: { mode: "cards" },
|
|
251
|
+
defaultFilters,
|
|
252
|
+
defaultSorting: [{ id: "name", desc: false }],
|
|
253
|
+
rowActions: [{ id: "edit", label: "Edit", onRun: () => { } }],
|
|
254
|
+
});
|
|
255
|
+
const button = (target, name) => [...target.querySelectorAll(".data-table__toolbar button")].find((b) => name.test(b.textContent?.trim() ?? ""));
|
|
256
|
+
const ONE = [{ columnId: "name", operator: "contains", values: ["Name"] }];
|
|
257
|
+
it("swaps the sort and filter menus for sheet buttons, counted by badges", () => {
|
|
258
|
+
const view = cards(ONE);
|
|
259
|
+
const filter = button(view.target, /^Filter/);
|
|
260
|
+
const sort = button(view.target, /^Sort/);
|
|
261
|
+
expect(filter?.querySelector(".data-table__badge")?.textContent).toBe("1");
|
|
262
|
+
expect(filter?.textContent).toContain(", 1 applied");
|
|
263
|
+
expect(sort?.querySelector(".data-table__badge")?.textContent).toBe("1");
|
|
264
|
+
// The desktop funnel is gone.
|
|
265
|
+
expect(view.target.querySelector('.data-table__toolbar [aria-label="Add filter"]')).toBeNull();
|
|
266
|
+
view.destroy();
|
|
267
|
+
});
|
|
268
|
+
it("shows no badge while nothing is applied", () => {
|
|
269
|
+
const view = render({ columns: FILTERABLE, responsive: { mode: "cards" } });
|
|
270
|
+
expect(button(view.target, /^Filter/)?.querySelector(".data-table__badge")).toBeNull();
|
|
271
|
+
view.destroy();
|
|
272
|
+
});
|
|
273
|
+
it("counts only complete conditions: a draft with no value is not applied", () => {
|
|
274
|
+
const view = cards([{ columnId: "name", operator: "contains", values: [] }]);
|
|
275
|
+
expect(button(view.target, /^Filter/)?.querySelector(".data-table__badge")).toBeNull();
|
|
276
|
+
view.destroy();
|
|
277
|
+
});
|
|
278
|
+
it("keeps filters out of the chips row, which the sheet replaces", () => {
|
|
279
|
+
const view = cards(ONE);
|
|
280
|
+
expect(view.target.querySelector(".data-table__subbar")).toBeNull();
|
|
281
|
+
expect(view.target.querySelector(".data-table__filter-chip")).toBeNull();
|
|
282
|
+
view.destroy();
|
|
283
|
+
});
|
|
284
|
+
it("puts the row actions and then the checkbox at the card's top-right", () => {
|
|
285
|
+
const view = cards();
|
|
286
|
+
const header = view.target.querySelector(".data-table__card-header");
|
|
287
|
+
const children = [...(header?.children ?? [])];
|
|
288
|
+
expect(children[0]?.classList.contains("data-table__card-title")).toBe(true);
|
|
289
|
+
const controls = header?.querySelector(".data-table__card-controls");
|
|
290
|
+
expect(controls).toBe(children[children.length - 1]);
|
|
291
|
+
const last = controls?.lastElementChild;
|
|
292
|
+
expect(last?.querySelector('input[type="checkbox"]') ?? last).not.toBeNull();
|
|
293
|
+
expect(controls?.querySelector('[aria-label^="Actions for"]')).not.toBeNull();
|
|
294
|
+
view.destroy();
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
describe("inline editing", () => {
|
|
298
|
+
// The editor is a raw snippet: a plain <input> wired to the edit context.
|
|
299
|
+
const editable = (onEdit = () => { }) => render({
|
|
300
|
+
columns: [
|
|
301
|
+
{ accessorKey: "name", header: "Name", meta: { primary: true } },
|
|
302
|
+
{
|
|
303
|
+
id: "seats",
|
|
304
|
+
accessorFn: (_row, index) => index + 1,
|
|
305
|
+
header: "Seats",
|
|
306
|
+
meta: {
|
|
307
|
+
edit: {
|
|
308
|
+
render: (ctx) => createRawSnippet(() => ({
|
|
309
|
+
render: () => '<input aria-label="Seats" />',
|
|
310
|
+
setup: (input) => {
|
|
311
|
+
input.value = String(ctx.value ?? "");
|
|
312
|
+
input.addEventListener("input", () => ctx.setValue(Number(input.value)));
|
|
313
|
+
input.addEventListener("change", () => ctx.commit());
|
|
314
|
+
},
|
|
315
|
+
})),
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
onEdit,
|
|
321
|
+
});
|
|
322
|
+
it("marks the editable column's header, and only that one", () => {
|
|
323
|
+
const view = editable();
|
|
324
|
+
const seats = view.target.querySelector('thead th[data-column-id="seats"]');
|
|
325
|
+
expect(seats?.classList.contains("data-table__head-cell--editable")).toBe(true);
|
|
326
|
+
expect(seats?.querySelector(".data-table__edit-icon")).not.toBeNull();
|
|
327
|
+
expect(seats?.textContent).toContain(", editable");
|
|
328
|
+
expect(view.target.querySelectorAll("thead .data-table__edit-icon")).toHaveLength(1);
|
|
329
|
+
expect(view.target
|
|
330
|
+
.querySelector('[data-cell="0:1"]')
|
|
331
|
+
?.classList.contains("data-table__cell--editable")).toBe(true);
|
|
332
|
+
expect(view.target
|
|
333
|
+
.querySelector('[data-cell="0:0"]')
|
|
334
|
+
?.classList.contains("data-table__cell--editable")).toBe(false);
|
|
335
|
+
view.destroy();
|
|
336
|
+
});
|
|
337
|
+
it("opens with one click, and a click inside the editor keeps the draft", async () => {
|
|
338
|
+
const committed = [];
|
|
339
|
+
const view = editable((patch) => committed.push(patch.value));
|
|
340
|
+
view.target.querySelector('[data-cell="0:1"]')?.click();
|
|
341
|
+
flushSync();
|
|
342
|
+
const input = view.target.querySelector(".data-table__editor input");
|
|
343
|
+
expect(input).not.toBeNull();
|
|
344
|
+
flushSync();
|
|
345
|
+
expect(document.activeElement).toBe(input);
|
|
346
|
+
input.value = "9";
|
|
347
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
348
|
+
flushSync();
|
|
349
|
+
// Each draft change re-renders the editor snippet, so query the live input.
|
|
350
|
+
const live = () => view.target.querySelector(".data-table__editor input");
|
|
351
|
+
live().click();
|
|
352
|
+
flushSync();
|
|
353
|
+
// Had the click restarted the edit, the draft would be back to 1.
|
|
354
|
+
live().dispatchEvent(new Event("change", { bubbles: true }));
|
|
355
|
+
flushSync();
|
|
356
|
+
await Promise.resolve();
|
|
357
|
+
expect(committed).toEqual([9]);
|
|
358
|
+
view.destroy();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/DataTable/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as DataTable } from "./DataTable.svelte";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The editor's wrapper, and the thing that puts focus inside it.
|
|
3
|
+
|
|
4
|
+
Focus is the package's job, not the consumer's: `meta.edit.render` returns
|
|
5
|
+
whatever control the consumer likes, and most of the library's controls do not
|
|
6
|
+
expose their inner `<input>` — so a consumer trying to do this themselves would
|
|
7
|
+
reach for `autofocus`, which is unreliable for an element mounting
|
|
8
|
+
mid-interaction.
|
|
9
|
+
|
|
10
|
+
A component rather than an effect inside `TableCell` so the mount hook exists
|
|
11
|
+
only while something is being edited, instead of once per rendered cell.
|
|
12
|
+
-->
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import type { Snippet } from "svelte";
|
|
15
|
+
import { onMount } from "svelte";
|
|
16
|
+
|
|
17
|
+
let { children }: { children?: Snippet } = $props();
|
|
18
|
+
let host = $state<HTMLElement | null>(null);
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
const focusable = host?.querySelector<HTMLElement>(
|
|
22
|
+
'input:not([type="hidden"]), textarea, select, [contenteditable="true"], [tabindex]:not([tabindex="-1"])',
|
|
23
|
+
);
|
|
24
|
+
focusable?.focus();
|
|
25
|
+
if (focusable instanceof HTMLInputElement) focusable.select();
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<span bind:this={host} class="data-table__editor">{@render children?.()}</span>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
children?: Snippet;
|
|
4
|
+
};
|
|
5
|
+
declare const CellEditor: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type CellEditor = ReturnType<typeof CellEditor>;
|
|
7
|
+
export default CellEditor;
|
|
8
|
+
//# sourceMappingURL=CellEditor.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CellEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/Table/CellEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,KAAK,gBAAgB,GAAI;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAsBjD,QAAA,MAAM,UAAU,sDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Left and right, for a column that can be moved — the click-and-keyboard half
|
|
3
|
+
of dragging the header. Over the header's end rather than in its flow, so
|
|
4
|
+
showing them does not shove the label. In `"always"` mode the stylesheet
|
|
5
|
+
shows them on hover and focus; in `"toggle"` mode they are up for as long as
|
|
6
|
+
reorder mode is.
|
|
7
|
+
|
|
8
|
+
In an interactive grid they are not tab stops, like every control inside a
|
|
9
|
+
cell; Alt+Arrow on the focused header is the keyboard route there.
|
|
10
|
+
-->
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import { Icon } from "@ui-organized/svelte";
|
|
13
|
+
import { internalGlyph as glyph } from "@ui-organized/svelte";
|
|
14
|
+
import { getTable } from "../../core/tableContext.js";
|
|
15
|
+
|
|
16
|
+
let { columnId, name }: { columnId: string; name: string } = $props();
|
|
17
|
+
const table = getTable();
|
|
18
|
+
|
|
19
|
+
let left = $state<HTMLButtonElement | null>(null);
|
|
20
|
+
let right = $state<HTMLButtonElement | null>(null);
|
|
21
|
+
|
|
22
|
+
const press = (direction: -1 | 1, event: MouseEvent) => {
|
|
23
|
+
const hadFocus = document.activeElement === event.currentTarget;
|
|
24
|
+
table.reorder.move(columnId, direction);
|
|
25
|
+
if (!hadFocus) return;
|
|
26
|
+
// Re-rendering in the new order moves this header's DOM node, and a moved
|
|
27
|
+
// node drops focus. Put it back on the same button — or on the other one,
|
|
28
|
+
// when this move took the column to the end it pointed at.
|
|
29
|
+
requestAnimationFrame(() => {
|
|
30
|
+
const own = direction === -1 ? left : right;
|
|
31
|
+
const other = direction === -1 ? right : left;
|
|
32
|
+
(own && !own.disabled ? own : other)?.focus();
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<span class="data-table__move" data-mode={table.reorder.mode}>
|
|
38
|
+
<button
|
|
39
|
+
bind:this={left}
|
|
40
|
+
type="button"
|
|
41
|
+
class="data-table__move-button"
|
|
42
|
+
aria-label={`Move ${name} left`}
|
|
43
|
+
disabled={!table.reorder.canMove(columnId, -1)}
|
|
44
|
+
tabindex={table.interactive ? -1 : undefined}
|
|
45
|
+
onclick={(event) => press(-1, event)}
|
|
46
|
+
>
|
|
47
|
+
<Icon name={glyph("chevron-left")} size={16} />
|
|
48
|
+
</button>
|
|
49
|
+
<button
|
|
50
|
+
bind:this={right}
|
|
51
|
+
type="button"
|
|
52
|
+
class="data-table__move-button"
|
|
53
|
+
aria-label={`Move ${name} right`}
|
|
54
|
+
disabled={!table.reorder.canMove(columnId, 1)}
|
|
55
|
+
tabindex={table.interactive ? -1 : undefined}
|
|
56
|
+
onclick={(event) => press(1, event)}
|
|
57
|
+
>
|
|
58
|
+
<Icon name={glyph("chevron-right")} size={16} />
|
|
59
|
+
</button>
|
|
60
|
+
</span>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
columnId: string;
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
declare const MoveButtons: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type MoveButtons = ReturnType<typeof MoveButtons>;
|
|
7
|
+
export default MoveButtons;
|
|
8
|
+
//# sourceMappingURL=MoveButtons.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MoveButtons.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/Table/MoveButtons.svelte.ts"],"names":[],"mappings":"AAOC,KAAK,gBAAgB,GAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAuC7D,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Pointer drag and keyboard, on one control.
|
|
3
|
+
|
|
4
|
+
The drag is TanStack's own handler; the keyboard is core's `resizeKeyDown`, so
|
|
5
|
+
the two cannot drift. It is a `role="separator"` with `aria-valuenow`, which is
|
|
6
|
+
what makes the column width something a screen reader can report rather than
|
|
7
|
+
something only a mouse can discover.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts" generics="T extends RowData">
|
|
10
|
+
import {
|
|
11
|
+
getResizeHandleProps,
|
|
12
|
+
resizeKeyDown,
|
|
13
|
+
type RowData,
|
|
14
|
+
type TableHeaderInstance,
|
|
15
|
+
} from "@ui-organized/table-core";
|
|
16
|
+
import { getTable } from "../../core/tableContext.js";
|
|
17
|
+
import { svelteProps } from "../../core/props.js";
|
|
18
|
+
|
|
19
|
+
let { header }: { header: TableHeaderInstance<T> } = $props();
|
|
20
|
+
const table = getTable<T>();
|
|
21
|
+
|
|
22
|
+
const column = $derived(header.column);
|
|
23
|
+
const bounds = $derived.by(() => {
|
|
24
|
+
const width = column.getSize();
|
|
25
|
+
return {
|
|
26
|
+
width,
|
|
27
|
+
min: column.columnDef.minSize ?? 0,
|
|
28
|
+
max: column.columnDef.maxSize ?? width,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const handleProps = $derived.by(() => {
|
|
33
|
+
const headerDef = column.columnDef.header;
|
|
34
|
+
return getResizeHandleProps({
|
|
35
|
+
columnId: column.id,
|
|
36
|
+
columnLabel: typeof headerDef === "string" ? headerDef : column.id,
|
|
37
|
+
width: bounds.width,
|
|
38
|
+
min: bounds.min,
|
|
39
|
+
max: bounds.max,
|
|
40
|
+
active: column.getIsResizing(),
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/** Stops the header's sort button from firing on the same press. */
|
|
45
|
+
function startResize(event: MouseEvent | TouchEvent) {
|
|
46
|
+
event.stopPropagation();
|
|
47
|
+
header.getResizeHandler()(event);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function onKeyDown(event: KeyboardEvent) {
|
|
51
|
+
const action = resizeKeyDown(bounds, {
|
|
52
|
+
key: event.key,
|
|
53
|
+
shift: event.shiftKey,
|
|
54
|
+
ctrl: event.ctrlKey,
|
|
55
|
+
meta: event.metaKey,
|
|
56
|
+
});
|
|
57
|
+
if (action.type === "none") return;
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
event.stopPropagation();
|
|
60
|
+
if (action.type === "reset") {
|
|
61
|
+
table.table.setColumnSizing((old: Record<string, number>) => {
|
|
62
|
+
const next = { ...old };
|
|
63
|
+
delete next[column.id];
|
|
64
|
+
return next;
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
table.table.setColumnSizing((old: Record<string, number>) => ({
|
|
69
|
+
...old,
|
|
70
|
+
[column.id]: action.width,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
{...svelteProps(handleProps)}
|
|
78
|
+
onmousedown={startResize}
|
|
79
|
+
ontouchstart={startResize}
|
|
80
|
+
onclick={(event) => event.stopPropagation()}
|
|
81
|
+
onkeydown={onKeyDown}
|
|
82
|
+
></button>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type RowData, type TableHeaderInstance } from "@ui-organized/table-core";
|
|
2
|
+
declare function $$render<T extends RowData>(): {
|
|
3
|
+
props: {
|
|
4
|
+
header: TableHeaderInstance<T>;
|
|
5
|
+
};
|
|
6
|
+
exports: {};
|
|
7
|
+
bindings: "";
|
|
8
|
+
slots: {};
|
|
9
|
+
events: {};
|
|
10
|
+
};
|
|
11
|
+
declare class __sveltets_Render<T extends RowData> {
|
|
12
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
13
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
14
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
15
|
+
bindings(): "";
|
|
16
|
+
exports(): {};
|
|
17
|
+
}
|
|
18
|
+
interface $$IsomorphicComponent {
|
|
19
|
+
new <T extends RowData>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
20
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
21
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
22
|
+
<T extends RowData>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
23
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
24
|
+
}
|
|
25
|
+
declare const ResizeHandle: $$IsomorphicComponent;
|
|
26
|
+
type ResizeHandle<T extends RowData> = InstanceType<typeof ResizeHandle<T>>;
|
|
27
|
+
export default ResizeHandle;
|
|
28
|
+
//# sourceMappingURL=ResizeHandle.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResizeHandle.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/Table/ResizeHandle.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAGH,KAAK,OAAO,EACZ,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAGpC,iBAAS,QAAQ,CAAC,CAAC,SAAS,OAAO;;gBAIG,mBAAmB,CAAC,CAAC,CAAC;;;;;;EA8D6D;AACzH,cAAM,iBAAiB,CAAC,CAAC,SAAS,OAAO;IACrC,KAAK,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAChD,MAAM,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAClD,KAAK,IAAI,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAChD,QAAQ;IACR,OAAO;CACV;AAED,UAAU,qBAAqB;IAC3B,KAAK,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;KAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5Y,CAAC,CAAC,SAAS,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3I,YAAY,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;CACjE;AACD,QAAA,MAAM,YAAY,EAAE,qBAAmC,CAAC;AACtC,KAAK,YAAY,CAAC,CAAC,SAAS,OAAO,IAAI,YAAY,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9E,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- The root: size, variant, and the element every part measures itself against. -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import { getRootProps } from "@ui-organized/table-core";
|
|
6
|
+
import { getTable } from "../../core/tableContext.js";
|
|
7
|
+
import { elementRef } from "../../core/elementRef.js";
|
|
8
|
+
import { svelteProps } from "../../core/props.js";
|
|
9
|
+
|
|
10
|
+
let { class: className, children }: { class?: string; children?: Snippet } = $props();
|
|
11
|
+
const table = getTable();
|
|
12
|
+
const root = $derived(getRootProps(table.chrome));
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div {...svelteProps(root)} class={clsx(root.className, className)} use:elementRef={table.rootRef}>
|
|
16
|
+
{@render children?.()}
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
class?: string;
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
};
|
|
6
|
+
declare const Table: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type Table = ReturnType<typeof Table>;
|
|
8
|
+
export default Table;
|
|
9
|
+
//# sourceMappingURL=Table.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Table.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/Table/Table.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOrC,KAAK,gBAAgB,GAAI;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAqBjE,QAAA,MAAM,KAAK,sDAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
|