@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 @@
|
|
|
1
|
+
{"version":3,"file":"createDataTable.test.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/core/createDataTable.test.svelte.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { flushSync } from "svelte";
|
|
3
|
+
import { createDataTable } from "./createDataTable.svelte.js";
|
|
4
|
+
const MEMBERS = [
|
|
5
|
+
{ id: "1", name: "Ada", role: "Owner", seats: 3 },
|
|
6
|
+
{ id: "2", name: "Grace", role: "Admin", seats: 1 },
|
|
7
|
+
{ id: "3", name: "Alan", role: "Admin", seats: 7 },
|
|
8
|
+
{ id: "4", name: "Katherine", role: "Analyst", seats: 2 },
|
|
9
|
+
];
|
|
10
|
+
const COLUMNS = [
|
|
11
|
+
{ accessorKey: "name", header: "Name", meta: { primary: true, filter: true } },
|
|
12
|
+
{ accessorKey: "role", header: "Role", meta: { filter: { type: "enum" } } },
|
|
13
|
+
{ accessorKey: "seats", header: "Seats", meta: { align: "end" } },
|
|
14
|
+
];
|
|
15
|
+
describe("createDataTable", () => {
|
|
16
|
+
let dispose = null;
|
|
17
|
+
beforeEach(() => vi.useFakeTimers());
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
dispose?.();
|
|
20
|
+
dispose = null;
|
|
21
|
+
vi.useRealTimers();
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* `$effect.root` because the runes inside `createDataTable` need an owner and
|
|
25
|
+
* there is no component here — the same thing a component's lifetime provides.
|
|
26
|
+
*/
|
|
27
|
+
function mount(overrides = {}) {
|
|
28
|
+
let options = $state({
|
|
29
|
+
data: MEMBERS,
|
|
30
|
+
columns: COLUMNS,
|
|
31
|
+
label: "Members",
|
|
32
|
+
getRowId: (row) => row.id,
|
|
33
|
+
...overrides,
|
|
34
|
+
});
|
|
35
|
+
let api;
|
|
36
|
+
dispose = $effect.root(() => {
|
|
37
|
+
api = createDataTable(() => options);
|
|
38
|
+
});
|
|
39
|
+
flushSync();
|
|
40
|
+
return {
|
|
41
|
+
get api() {
|
|
42
|
+
return api;
|
|
43
|
+
},
|
|
44
|
+
set: (patch) => {
|
|
45
|
+
options = { ...options, ...patch };
|
|
46
|
+
flushSync();
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
it("builds a table from the options it was given", () => {
|
|
51
|
+
const { api } = mount();
|
|
52
|
+
expect(api.rows).toHaveLength(4);
|
|
53
|
+
expect(api.rows.map((row) => row.id)).toEqual(["1", "2", "3", "4"]);
|
|
54
|
+
expect(api.label).toBe("Members");
|
|
55
|
+
expect(api.primaryColumnId).toBe("name");
|
|
56
|
+
});
|
|
57
|
+
it("follows its options when they change", () => {
|
|
58
|
+
// The options arrive as a getter precisely so this works; a snapshot taken
|
|
59
|
+
// at construction would render a correct table that never changes again.
|
|
60
|
+
const table = mount();
|
|
61
|
+
table.set({ data: [...MEMBERS, { id: "5", name: "Linus", role: "Admin", seats: 4 }] });
|
|
62
|
+
expect(table.api.rows).toHaveLength(5);
|
|
63
|
+
table.set({ size: "lg" });
|
|
64
|
+
expect(table.api.size).toBe("lg");
|
|
65
|
+
expect(table.api.chrome.size).toBe("lg");
|
|
66
|
+
});
|
|
67
|
+
it("is a plain table until something can be operated", () => {
|
|
68
|
+
// `interactive` is what decides `role="grid"` and the roving cursor, and it
|
|
69
|
+
// is derived rather than declared.
|
|
70
|
+
const table = mount();
|
|
71
|
+
expect(table.api.interactive).toBe(false);
|
|
72
|
+
table.set({ selection: "multiple" });
|
|
73
|
+
expect(table.api.interactive).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
it("sorts through the engine", () => {
|
|
76
|
+
const { api } = mount();
|
|
77
|
+
api.table.getColumn("name").toggleSorting(false);
|
|
78
|
+
flushSync();
|
|
79
|
+
expect(api.rows.map((row) => row.original.name)).toEqual([
|
|
80
|
+
"Ada",
|
|
81
|
+
"Alan",
|
|
82
|
+
"Grace",
|
|
83
|
+
"Katherine",
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
it("paginates, and moves when the page is set", () => {
|
|
87
|
+
// The path the browser gate's "paged forward" scenario drives: a control
|
|
88
|
+
// calls `setPageIndex`, which lands on `onPaginationChange`, which has to
|
|
89
|
+
// reach the row model.
|
|
90
|
+
const { api } = mount({ paginated: true, pageSize: 2 });
|
|
91
|
+
expect(api.rows).toHaveLength(2);
|
|
92
|
+
expect(api.rows[0].id).toBe("1");
|
|
93
|
+
api.table.setPageIndex(1);
|
|
94
|
+
flushSync();
|
|
95
|
+
expect(api.rows[0].id).toBe("3");
|
|
96
|
+
// `absoluteIndex` is what `aria-rowindex` reports, so it counts from the
|
|
97
|
+
// dataset rather than from the page.
|
|
98
|
+
expect(api.renderRows[0].absoluteIndex).toBe(2);
|
|
99
|
+
});
|
|
100
|
+
it("filters on the deferred value, not the urgent one", async () => {
|
|
101
|
+
const { api } = mount();
|
|
102
|
+
api.setSearch("Ada");
|
|
103
|
+
flushSync();
|
|
104
|
+
// The control is already showing the new value...
|
|
105
|
+
expect(api.search).toBe("Ada");
|
|
106
|
+
// ...and the expensive pass has not run yet.
|
|
107
|
+
expect(api.rows).toHaveLength(4);
|
|
108
|
+
await vi.runAllTimersAsync();
|
|
109
|
+
flushSync();
|
|
110
|
+
expect(api.rows).toHaveLength(1);
|
|
111
|
+
expect(api.rows[0].original.name).toBe("Ada");
|
|
112
|
+
});
|
|
113
|
+
it("renders seeded filters on the first pass", () => {
|
|
114
|
+
// No scheduler has run, and none needs to: a table seeded with
|
|
115
|
+
// `defaultFilters` has to be filtered before anything is scheduled.
|
|
116
|
+
const { api } = mount({
|
|
117
|
+
defaultFilters: [{ columnId: "role", operator: "is", values: ["Admin"] }],
|
|
118
|
+
});
|
|
119
|
+
expect(api.rows).toHaveLength(2);
|
|
120
|
+
expect(api.filters.conditions).toHaveLength(1);
|
|
121
|
+
});
|
|
122
|
+
/**
|
|
123
|
+
* The chip's field segment. An enum's values never mean anything on another
|
|
124
|
+
* column, so moving one starts over on the new column's default operator.
|
|
125
|
+
*/
|
|
126
|
+
it("moves a condition to another column from the field segment", () => {
|
|
127
|
+
const { api } = mount({
|
|
128
|
+
defaultFilters: [{ columnId: "role", operator: "is-any-of", values: ["Admin"] }],
|
|
129
|
+
});
|
|
130
|
+
const [condition] = api.filters.conditions;
|
|
131
|
+
api.filters.changeField(condition.id, "name");
|
|
132
|
+
flushSync();
|
|
133
|
+
expect(api.filters.conditions).toEqual([
|
|
134
|
+
{ id: condition.id, columnId: "name", operator: "contains", values: [] },
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
it("selects rows, and reports what the header checkbox should show", () => {
|
|
138
|
+
const { api } = mount({ selection: "multiple" });
|
|
139
|
+
expect(api.selection.header).toEqual({ checked: false, indeterminate: false });
|
|
140
|
+
api.selection.toggle("1", true);
|
|
141
|
+
flushSync();
|
|
142
|
+
expect(api.selection.count).toBe(1);
|
|
143
|
+
expect(api.selection.isSelected("1")).toBe(true);
|
|
144
|
+
expect(api.selection.header.indeterminate).toBe(true);
|
|
145
|
+
api.selection.togglePage(true);
|
|
146
|
+
flushSync();
|
|
147
|
+
expect(api.selection.count).toBe(4);
|
|
148
|
+
expect(api.selection.header).toEqual({ checked: true, indeterminate: false });
|
|
149
|
+
});
|
|
150
|
+
it("puts the tab stop in the header when there are no rows", () => {
|
|
151
|
+
const { api } = mount({ data: [] });
|
|
152
|
+
// Row -1 is the header, which is still operable (sorting) and still one tab
|
|
153
|
+
// stop — a body with nothing in it has nowhere to hold one.
|
|
154
|
+
expect(api.focus.cursor).toEqual({ row: -1, col: 0 });
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte's answer to React's `useDeferredValue`, which Svelte does not have.
|
|
3
|
+
*
|
|
4
|
+
* Named `createDeferred` rather than `useDeferred`, which is what the Vue
|
|
5
|
+
* package calls it: `use*` is a React and Vue convention, and here it only makes
|
|
6
|
+
* `eslint-plugin-react-hooks` treat a Svelte function as a React hook.
|
|
7
|
+
*
|
|
8
|
+
* ── Why this file exists ────────────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* The React table splits every expensive filter into two values: the condition
|
|
11
|
+
* list and the search string update **urgently**, so a controlled input never
|
|
12
|
+
* lags a keystroke, while the row-model pass they drive reads a *deferred* copy.
|
|
13
|
+
* `useDataTable.ts` in the React package records what happens without the split
|
|
14
|
+
* — typing "ada" landed as "a".
|
|
15
|
+
*
|
|
16
|
+
* Svelte has no priority scheduler. Neither does Vue, and this is a deliberate
|
|
17
|
+
* transcription of `@ui-organized/vue-table`'s `useDeferred` rather than a
|
|
18
|
+
* second design: the trade-offs are identical and two different answers to the
|
|
19
|
+
* same problem in one design system would be a defect in itself.
|
|
20
|
+
*
|
|
21
|
+
* - The **source** stays urgent. Inputs bind to it directly, so typing is
|
|
22
|
+
* never delayed and never drops a character.
|
|
23
|
+
* - The **mirror** lags by one idle beat, and only the filtering reads it.
|
|
24
|
+
* - A burst of keystrokes **coalesces** into one filter pass.
|
|
25
|
+
* - The initial value is taken **synchronously**, because a table seeded with
|
|
26
|
+
* `defaultFilters` has to render filtered on its first pass — on the server
|
|
27
|
+
* too, where nothing is ever scheduled.
|
|
28
|
+
*
|
|
29
|
+
* Idle rather than a debounce because a fixed delay is wrong in both directions:
|
|
30
|
+
* too short and it does nothing on a busy machine, too long and a fast typist
|
|
31
|
+
* watches the table sit still. The deadline matters as much as the callback — a
|
|
32
|
+
* table being scrolled while someone types keeps the main thread busy, and
|
|
33
|
+
* without one the filter would never run.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createDeferred<T>(source: () => T): {
|
|
36
|
+
readonly current: T;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=createDeferred.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createDeferred.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/core/createDeferred.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AA6BH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;CAAE,CAwB1E"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte's answer to React's `useDeferredValue`, which Svelte does not have.
|
|
3
|
+
*
|
|
4
|
+
* Named `createDeferred` rather than `useDeferred`, which is what the Vue
|
|
5
|
+
* package calls it: `use*` is a React and Vue convention, and here it only makes
|
|
6
|
+
* `eslint-plugin-react-hooks` treat a Svelte function as a React hook.
|
|
7
|
+
*
|
|
8
|
+
* ── Why this file exists ────────────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* The React table splits every expensive filter into two values: the condition
|
|
11
|
+
* list and the search string update **urgently**, so a controlled input never
|
|
12
|
+
* lags a keystroke, while the row-model pass they drive reads a *deferred* copy.
|
|
13
|
+
* `useDataTable.ts` in the React package records what happens without the split
|
|
14
|
+
* — typing "ada" landed as "a".
|
|
15
|
+
*
|
|
16
|
+
* Svelte has no priority scheduler. Neither does Vue, and this is a deliberate
|
|
17
|
+
* transcription of `@ui-organized/vue-table`'s `useDeferred` rather than a
|
|
18
|
+
* second design: the trade-offs are identical and two different answers to the
|
|
19
|
+
* same problem in one design system would be a defect in itself.
|
|
20
|
+
*
|
|
21
|
+
* - The **source** stays urgent. Inputs bind to it directly, so typing is
|
|
22
|
+
* never delayed and never drops a character.
|
|
23
|
+
* - The **mirror** lags by one idle beat, and only the filtering reads it.
|
|
24
|
+
* - A burst of keystrokes **coalesces** into one filter pass.
|
|
25
|
+
* - The initial value is taken **synchronously**, because a table seeded with
|
|
26
|
+
* `defaultFilters` has to render filtered on its first pass — on the server
|
|
27
|
+
* too, where nothing is ever scheduled.
|
|
28
|
+
*
|
|
29
|
+
* Idle rather than a debounce because a fixed delay is wrong in both directions:
|
|
30
|
+
* too short and it does nothing on a busy machine, too long and a fast typist
|
|
31
|
+
* watches the table sit still. The deadline matters as much as the callback — a
|
|
32
|
+
* table being scrolled while someone types keeps the main thread busy, and
|
|
33
|
+
* without one the filter would never run.
|
|
34
|
+
*/
|
|
35
|
+
/** Long enough to coalesce a burst of typing, short enough to feel caused by it. */
|
|
36
|
+
const IDLE_TIMEOUT = 100;
|
|
37
|
+
function scheduleIdle(run) {
|
|
38
|
+
// SSR: no scheduler, and no rendering to protect either.
|
|
39
|
+
if (typeof window === "undefined") {
|
|
40
|
+
let cancelled = false;
|
|
41
|
+
queueMicrotask(() => {
|
|
42
|
+
if (!cancelled)
|
|
43
|
+
run();
|
|
44
|
+
});
|
|
45
|
+
return () => {
|
|
46
|
+
cancelled = true;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const request = window.requestIdleCallback;
|
|
50
|
+
if (typeof request === "function") {
|
|
51
|
+
const handle = request(() => run(), { timeout: IDLE_TIMEOUT });
|
|
52
|
+
return () => window.cancelIdleCallback?.(handle);
|
|
53
|
+
}
|
|
54
|
+
// Safari has no requestIdleCallback. A macrotask still yields to paint and to
|
|
55
|
+
// input, which is the property being bought here.
|
|
56
|
+
const handle = window.setTimeout(run, 0);
|
|
57
|
+
return () => window.clearTimeout(handle);
|
|
58
|
+
}
|
|
59
|
+
export function createDeferred(source) {
|
|
60
|
+
let deferred = $state(source());
|
|
61
|
+
let cancel = null;
|
|
62
|
+
$effect(() => {
|
|
63
|
+
// Read the source so this effect depends on it, then schedule outside the
|
|
64
|
+
// tracking scope — `untrack` is unnecessary because the callback runs later.
|
|
65
|
+
const value = source();
|
|
66
|
+
cancel?.();
|
|
67
|
+
cancel = scheduleIdle(() => {
|
|
68
|
+
cancel = null;
|
|
69
|
+
deferred = value;
|
|
70
|
+
});
|
|
71
|
+
return () => {
|
|
72
|
+
cancel?.();
|
|
73
|
+
cancel = null;
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
get current() {
|
|
78
|
+
return deferred;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Action } from "svelte/action";
|
|
2
|
+
/**
|
|
3
|
+
* Hand an element to a callback when it mounts, and `null` when it goes.
|
|
4
|
+
*
|
|
5
|
+
* React takes a callback ref for the root and the viewport because it needs a
|
|
6
|
+
* *render* when the node appears; Vue binds a template ref and watches it.
|
|
7
|
+
* Svelte 5 has `bind:this`, but a plain variable assignment is not something a
|
|
8
|
+
* `$derived` elsewhere can watch — and the resize and scroll observers have to
|
|
9
|
+
* re-run when the node arrives. An action calling into the api's own `$state` is
|
|
10
|
+
* what makes that reactive.
|
|
11
|
+
*
|
|
12
|
+
* ```svelte
|
|
13
|
+
* <div use:elementRef={table.rootRef}>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare const elementRef: Action<HTMLElement, (element: HTMLElement | null) => void>;
|
|
17
|
+
//# sourceMappingURL=elementRef.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementRef.d.ts","sourceRoot":"","sources":["../../src/lib/core/elementRef.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAQjF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand an element to a callback when it mounts, and `null` when it goes.
|
|
3
|
+
*
|
|
4
|
+
* React takes a callback ref for the root and the viewport because it needs a
|
|
5
|
+
* *render* when the node appears; Vue binds a template ref and watches it.
|
|
6
|
+
* Svelte 5 has `bind:this`, but a plain variable assignment is not something a
|
|
7
|
+
* `$derived` elsewhere can watch — and the resize and scroll observers have to
|
|
8
|
+
* re-run when the node arrives. An action calling into the api's own `$state` is
|
|
9
|
+
* what makes that reactive.
|
|
10
|
+
*
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <div use:elementRef={table.rootRef}>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export const elementRef = (node, set) => {
|
|
16
|
+
set?.(node);
|
|
17
|
+
return {
|
|
18
|
+
destroy: () => set?.(null),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../src/lib/core/props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,0BAA0B,CAAC;AA0CzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOxE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one translation at the framework boundary.
|
|
3
|
+
*
|
|
4
|
+
* Core returns plain objects of `className` / `style` / `aria-*` / `data-*`,
|
|
5
|
+
* typed against its own narrow `TableStyle` rather than any framework's. Svelte
|
|
6
|
+
* needs two things done to them, and the second is the one that bites:
|
|
7
|
+
*
|
|
8
|
+
* 1. **`className` → `class`**, which is what a spread onto an element wants.
|
|
9
|
+
* 2. **Numeric lengths get `px`, and `style` becomes a string.** React appends
|
|
10
|
+
* the unit for us; Svelte does not, and a spread `style` has to be a string
|
|
11
|
+
* rather than an object. A column width of `180` would otherwise reach the
|
|
12
|
+
* DOM as `width:180`, which is not a length — so every column falls back to
|
|
13
|
+
* auto and the whole `table-layout: fixed` contract comes apart. It is
|
|
14
|
+
* invisible to the parity gate, which does not compare `style`, and obvious
|
|
15
|
+
* the moment anyone looks at the table. `@ui-organized/vue-table` hit exactly
|
|
16
|
+
* this.
|
|
17
|
+
*
|
|
18
|
+
* Everything else passes through untouched: `aria-*` and `data-*` are already
|
|
19
|
+
* attribute names, and Svelte lowercases `tabIndex` and `colSpan` itself.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The properties whose numeric values are **not** lengths. Core's `TableStyle`
|
|
23
|
+
* is narrow enough that the whole unitless list is one property — written as an
|
|
24
|
+
* exception so a length added later gets its unit automatically.
|
|
25
|
+
*/
|
|
26
|
+
const UNITLESS = new Set(["zIndex"]);
|
|
27
|
+
/** `{ width: 180 }` → `"width: 180px"`. Empty style becomes `undefined`. */
|
|
28
|
+
function styleString(style) {
|
|
29
|
+
const parts = [];
|
|
30
|
+
for (const [key, value] of Object.entries(style)) {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
const property = key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
34
|
+
const unit = typeof value === "number" && !UNITLESS.has(key) ? "px" : "";
|
|
35
|
+
parts.push(`${property}: ${value}${unit}`);
|
|
36
|
+
}
|
|
37
|
+
return parts.length ? parts.join("; ") : undefined;
|
|
38
|
+
}
|
|
39
|
+
export function svelteProps(props) {
|
|
40
|
+
const { className, style, ...rest } = props;
|
|
41
|
+
return {
|
|
42
|
+
...rest,
|
|
43
|
+
...(className === undefined ? {} : { class: className }),
|
|
44
|
+
...(style === undefined ? {} : { style: styleString(style) }),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type RowData } from "@ui-organized/table-core";
|
|
2
|
+
import type { TableColumn } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The two columns the table adds for itself.
|
|
5
|
+
*
|
|
6
|
+
* They are built here rather than in the composable because their cells render
|
|
7
|
+
* real components — `Checkbox`, `Menu` — and each reads the table from context,
|
|
8
|
+
* which works because cells render inside the provider.
|
|
9
|
+
*
|
|
10
|
+
* `renderComponent` rather than a bare component: the Svelte adapter's
|
|
11
|
+
* `FlexRender` identifies a component by the wrapper class this returns, which
|
|
12
|
+
* is how it tells "a component to mount" from "a string to print".
|
|
13
|
+
*/
|
|
14
|
+
export declare function selectionColumn<T extends RowData>(): TableColumn<T>;
|
|
15
|
+
export declare function actionsColumn<T extends RowData>(): TableColumn<T>;
|
|
16
|
+
//# sourceMappingURL=systemColumns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemColumns.d.ts","sourceRoot":"","sources":["../../src/lib/core/systemColumns.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAKhG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,CAqBnE;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,CAajE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { renderComponent } from "@tanstack/svelte-table";
|
|
2
|
+
import { ACTIONS_COLUMN_ID, SELECTION_COLUMN_ID } from "@ui-organized/table-core";
|
|
3
|
+
import TableRowActions from "../components/TableRowActions/TableRowActions.svelte";
|
|
4
|
+
import SelectAllCell from "./SelectAllCell.svelte";
|
|
5
|
+
import SelectCell from "./SelectCell.svelte";
|
|
6
|
+
import ActionsHeader from "./ActionsHeader.svelte";
|
|
7
|
+
/**
|
|
8
|
+
* The two columns the table adds for itself.
|
|
9
|
+
*
|
|
10
|
+
* They are built here rather than in the composable because their cells render
|
|
11
|
+
* real components — `Checkbox`, `Menu` — and each reads the table from context,
|
|
12
|
+
* which works because cells render inside the provider.
|
|
13
|
+
*
|
|
14
|
+
* `renderComponent` rather than a bare component: the Svelte adapter's
|
|
15
|
+
* `FlexRender` identifies a component by the wrapper class this returns, which
|
|
16
|
+
* is how it tells "a component to mount" from "a string to print".
|
|
17
|
+
*/
|
|
18
|
+
export function selectionColumn() {
|
|
19
|
+
return {
|
|
20
|
+
id: SELECTION_COLUMN_ID,
|
|
21
|
+
header: () => renderComponent(SelectAllCell, {}),
|
|
22
|
+
cell: ({ row, table }) => {
|
|
23
|
+
// The identifying column's value is the only thing that makes one row's
|
|
24
|
+
// checkbox distinguishable from another's in a screen reader's forms list.
|
|
25
|
+
const primary = table
|
|
26
|
+
.getAllLeafColumns()
|
|
27
|
+
.find((column) => column.columnDef.meta?.primary)?.id;
|
|
28
|
+
const name = primary ? String(row.getValue(primary) ?? row.id) : row.id;
|
|
29
|
+
return renderComponent(SelectCell, { rowId: row.id, rowLabel: `Select ${name}` });
|
|
30
|
+
},
|
|
31
|
+
size: 48,
|
|
32
|
+
minSize: 48,
|
|
33
|
+
maxSize: 48,
|
|
34
|
+
enableSorting: false,
|
|
35
|
+
enableResizing: false,
|
|
36
|
+
enableHiding: false,
|
|
37
|
+
meta: { align: "center", hideFromViewOptions: true },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function actionsColumn() {
|
|
41
|
+
return {
|
|
42
|
+
id: ACTIONS_COLUMN_ID,
|
|
43
|
+
header: () => renderComponent(ActionsHeader, {}),
|
|
44
|
+
cell: ({ row }) => renderComponent(TableRowActions, { row: row.original }),
|
|
45
|
+
size: 56,
|
|
46
|
+
minSize: 56,
|
|
47
|
+
maxSize: 56,
|
|
48
|
+
enableSorting: false,
|
|
49
|
+
enableResizing: false,
|
|
50
|
+
enableHiding: false,
|
|
51
|
+
meta: { align: "end", sticky: "right", hideFromViewOptions: true },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RowData } from "@ui-organized/table-core";
|
|
2
|
+
import type { DataTableApi } from "./types.js";
|
|
3
|
+
export declare function setTable<T extends RowData>(api: DataTableApi<T>): void;
|
|
4
|
+
export declare function getTable<T extends RowData = RowData>(): DataTableApi<T>;
|
|
5
|
+
/** For parts that are legitimately optional inside a table (toolbars reused elsewhere). */
|
|
6
|
+
export declare function getOptionalTable<T extends RowData = RowData>(): DataTableApi<T> | null;
|
|
7
|
+
//# sourceMappingURL=tableContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tableContext.d.ts","sourceRoot":"","sources":["../../src/lib/core/tableContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiB/C,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAEtE;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,CASvE;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAEtF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getContext, setContext } from "svelte";
|
|
2
|
+
/**
|
|
3
|
+
* The styled parts are usable on their own — that is the whole point of layer 2
|
|
4
|
+
* — so they need somewhere to find the table without every one of them taking
|
|
5
|
+
* the api as a prop through four levels of markup.
|
|
6
|
+
*
|
|
7
|
+
* `setContext`/`getContext` rather than React's context object, but the
|
|
8
|
+
* arrangement is the same: `<DataTable>` sets, every part gets, and a part
|
|
9
|
+
* rendered outside one says so with a message that names the fix.
|
|
10
|
+
*
|
|
11
|
+
* The api is a bag of getters over runes, so it can be put in context directly:
|
|
12
|
+
* context is read once at construction and the reactivity travels with the
|
|
13
|
+
* object rather than with the read.
|
|
14
|
+
*/
|
|
15
|
+
const TABLE_KEY = Symbol("ui-organized:data-table");
|
|
16
|
+
export function setTable(api) {
|
|
17
|
+
setContext(TABLE_KEY, api);
|
|
18
|
+
}
|
|
19
|
+
export function getTable() {
|
|
20
|
+
const api = getContext(TABLE_KEY);
|
|
21
|
+
if (!api) {
|
|
22
|
+
throw new Error("A table part was rendered outside a table. Provide one with " +
|
|
23
|
+
"`setTable(createDataTable(() => options))` — or use <DataTable>, which does that for you.");
|
|
24
|
+
}
|
|
25
|
+
return api;
|
|
26
|
+
}
|
|
27
|
+
/** For parts that are legitimately optional inside a table (toolbars reused elsewhere). */
|
|
28
|
+
export function getOptionalTable() {
|
|
29
|
+
return getContext(TABLE_KEY) ?? null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Store } from "@ui-organized/table-core";
|
|
2
|
+
/**
|
|
3
|
+
* Binds one of core's framework-free stores to Svelte 5 runes.
|
|
4
|
+
*
|
|
5
|
+
* `@ui-organized/table-core`'s `state.ts` predicted this binding in its own
|
|
6
|
+
* header — "Svelte with a `readable`" — and Svelte 5 makes it simpler than that:
|
|
7
|
+
* `$state` plus the store's own subscription, with no store contract in between.
|
|
8
|
+
*
|
|
9
|
+
* The store's two invariants are what make it safe: `getState()` returns the
|
|
10
|
+
* same reference until something actually changes, and a no-op action notifies
|
|
11
|
+
* nobody. So the mirror only ever changes when the state did.
|
|
12
|
+
*
|
|
13
|
+
* `$effect` rather than a bare `subscribe`, so the subscription is torn down
|
|
14
|
+
* with the component that created it.
|
|
15
|
+
*
|
|
16
|
+
* Named `trackStore` rather than `useStore`, which is what the React and Vue
|
|
17
|
+
* packages call it: `use*` is those two frameworks' convention, and here it only
|
|
18
|
+
* succeeds in making `eslint-plugin-react-hooks` treat a Svelte function as a
|
|
19
|
+
* React hook and complain that it is called in the wrong place.
|
|
20
|
+
*/
|
|
21
|
+
export declare function trackStore<S, A>(store: Store<S, A>): {
|
|
22
|
+
readonly current: S;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=trackStore.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trackStore.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/core/trackStore.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;CAAE,CAU5E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binds one of core's framework-free stores to Svelte 5 runes.
|
|
3
|
+
*
|
|
4
|
+
* `@ui-organized/table-core`'s `state.ts` predicted this binding in its own
|
|
5
|
+
* header — "Svelte with a `readable`" — and Svelte 5 makes it simpler than that:
|
|
6
|
+
* `$state` plus the store's own subscription, with no store contract in between.
|
|
7
|
+
*
|
|
8
|
+
* The store's two invariants are what make it safe: `getState()` returns the
|
|
9
|
+
* same reference until something actually changes, and a no-op action notifies
|
|
10
|
+
* nobody. So the mirror only ever changes when the state did.
|
|
11
|
+
*
|
|
12
|
+
* `$effect` rather than a bare `subscribe`, so the subscription is torn down
|
|
13
|
+
* with the component that created it.
|
|
14
|
+
*
|
|
15
|
+
* Named `trackStore` rather than `useStore`, which is what the React and Vue
|
|
16
|
+
* packages call it: `use*` is those two frameworks' convention, and here it only
|
|
17
|
+
* succeeds in making `eslint-plugin-react-hooks` treat a Svelte function as a
|
|
18
|
+
* React hook and complain that it is called in the wrong place.
|
|
19
|
+
*/
|
|
20
|
+
export function trackStore(store) {
|
|
21
|
+
let snapshot = $state(store.getState());
|
|
22
|
+
$effect(() => store.subscribe(() => (snapshot = store.getState())));
|
|
23
|
+
return {
|
|
24
|
+
get current() {
|
|
25
|
+
return snapshot;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|