@densedash/core 0.1.1 → 0.2.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.
Files changed (62) hide show
  1. package/README.md +59 -0
  2. package/dist/bare.d.ts +3 -3
  3. package/dist/bare.js +2 -2
  4. package/dist/collections/types.d.ts +0 -46
  5. package/dist/commands/index.d.ts +3 -3
  6. package/dist/commands/index.js +152 -39
  7. package/dist/commands/index.js.map +1 -1
  8. package/dist/commands/keys.js +4 -3
  9. package/dist/commands/keys.js.map +1 -1
  10. package/dist/commands/scopes.js +30 -3
  11. package/dist/commands/scopes.js.map +1 -1
  12. package/dist/commands/trading-preset.d.ts +5 -1
  13. package/dist/commands/trading-preset.js +22 -43
  14. package/dist/commands/trading-preset.js.map +1 -1
  15. package/dist/commands/types.d.ts +32 -5
  16. package/dist/defaults/keyboard.js +55 -10
  17. package/dist/defaults/keyboard.js.map +1 -1
  18. package/dist/index.d.ts +7 -10
  19. package/dist/index.js +3 -7
  20. package/dist/index.js.map +1 -1
  21. package/dist/kernel.d.ts +4 -4
  22. package/dist/kernel.js.map +1 -1
  23. package/dist/navigation/commands.js +200 -0
  24. package/dist/navigation/commands.js.map +1 -0
  25. package/dist/navigation/index.d.ts +31 -0
  26. package/dist/navigation/index.js +148 -0
  27. package/dist/navigation/index.js.map +1 -0
  28. package/dist/{collections → navigation}/move.js +1 -1
  29. package/dist/navigation/move.js.map +1 -0
  30. package/dist/{tree → navigation}/props.js +25 -9
  31. package/dist/navigation/props.js.map +1 -0
  32. package/dist/{collections → navigation}/reconcile.js +1 -1
  33. package/dist/navigation/reconcile.js.map +1 -0
  34. package/dist/navigation/selection.js +21 -0
  35. package/dist/navigation/selection.js.map +1 -0
  36. package/dist/navigation/state.d.ts +3 -0
  37. package/dist/navigation/state.js +265 -0
  38. package/dist/navigation/state.js.map +1 -0
  39. package/dist/navigation/types.d.ts +86 -0
  40. package/dist/ports/keyboard.d.ts +3 -2
  41. package/dist/ports/keyboard.js +9 -0
  42. package/dist/ports/keyboard.js.map +1 -0
  43. package/dist/tree/types.d.ts +7 -36
  44. package/package.json +3 -2
  45. package/dist/collections/flatten.d.ts +0 -8
  46. package/dist/collections/flatten.js +0 -59
  47. package/dist/collections/flatten.js.map +0 -1
  48. package/dist/collections/move.d.ts +0 -7
  49. package/dist/collections/move.js.map +0 -1
  50. package/dist/collections/reconcile.d.ts +0 -9
  51. package/dist/collections/reconcile.js.map +0 -1
  52. package/dist/collections/selection.d.ts +0 -12
  53. package/dist/collections/selection.js +0 -39
  54. package/dist/collections/selection.js.map +0 -1
  55. package/dist/tree/commands.js +0 -119
  56. package/dist/tree/commands.js.map +0 -1
  57. package/dist/tree/index.d.ts +0 -6
  58. package/dist/tree/index.js +0 -71
  59. package/dist/tree/index.js.map +0 -1
  60. package/dist/tree/props.js.map +0 -1
  61. package/dist/tree/state.js +0 -126
  62. package/dist/tree/state.js.map +0 -1
@@ -0,0 +1,265 @@
1
+ import { reconcile } from "./reconcile.js";
2
+ import { functionalUpdate, stateSlicesEqual } from "@tanstack/table-core";
3
+ // src/navigation/state.ts
4
+ function changeDetails({ reason = "programmatic", event } = {}) {
5
+ let canceled = false;
6
+ return {
7
+ reason,
8
+ event,
9
+ cancel() {
10
+ canceled = true;
11
+ },
12
+ get canceled() {
13
+ return canceled;
14
+ }
15
+ };
16
+ }
17
+ const keyState = (keys) => Object.fromEntries(Array.from(keys, (key) => [key, true]));
18
+ const selectedKeys = (state) => new Set(Object.keys(state).filter((key) => state[key]));
19
+ const controls = [
20
+ "expanded",
21
+ "sorting",
22
+ "highlighted",
23
+ "rowSelection"
24
+ ];
25
+ /** Table atoms are the authority. The kernel store publishes one adapter snapshot per transaction. */
26
+ function createNavigationModel(kernel, table, initial) {
27
+ let options = initial;
28
+ let disposed = false;
29
+ let publishing = false;
30
+ let staged;
31
+ let action = {};
32
+ const queued = [];
33
+ const subscriptions = /* @__PURE__ */ new Set();
34
+ function read() {
35
+ return table.store.state;
36
+ }
37
+ let validatedRows;
38
+ function rows() {
39
+ const core = table.getCoreRowModel();
40
+ if (validatedRows !== core.flatRows) {
41
+ const ids = /* @__PURE__ */ new Set();
42
+ for (const row of core.flatRows) {
43
+ if (ids.has(row.id)) throw new Error(`Duplicate row id "${row.id}".`);
44
+ ids.add(row.id);
45
+ }
46
+ validatedRows = core.flatRows;
47
+ }
48
+ return table.getRowModel().rows;
49
+ }
50
+ let projected;
51
+ function project() {
52
+ const state = read();
53
+ const visible = rows();
54
+ if (projected?.source !== visible || projected.expanded !== state.expanded || projected.disabled !== options.isDisabled || projected.text !== options.getTextValue) {
55
+ const siblings = /* @__PURE__ */ new Map();
56
+ for (const row of visible) {
57
+ const group = siblings.get(row.parentId) ?? [];
58
+ if (!siblings.has(row.parentId)) siblings.set(row.parentId, group);
59
+ group.push(row);
60
+ }
61
+ const positions = /* @__PURE__ */ new Map();
62
+ for (const group of siblings.values()) group.forEach((row, index) => positions.set(row.id, index + 1));
63
+ projected = {
64
+ source: visible,
65
+ expanded: state.expanded,
66
+ disabled: options.isDisabled,
67
+ text: options.getTextValue,
68
+ rows: visible.map((row, index) => ({
69
+ key: row.id,
70
+ item: row.original,
71
+ index,
72
+ depth: row.depth,
73
+ parentKey: row.parentId ?? null,
74
+ hasChildren: row.getCanExpand(),
75
+ expanded: row.getCanExpand() && row.getIsExpanded(),
76
+ posInSet: positions.get(row.id),
77
+ setSize: siblings.get(row.parentId).length,
78
+ disabled: options.isDisabled?.(row.original) ?? false,
79
+ textValue: options.getTextValue?.(row.original) ?? row.id
80
+ }))
81
+ };
82
+ }
83
+ return {
84
+ highlighted: state.highlighted,
85
+ anchor: state.anchor,
86
+ selected: selectedKeys(state.rowSelection),
87
+ expanded: state.expanded === true ? new Set(table.getCoreRowModel().flatRows.filter((row) => row.getCanExpand()).map((row) => row.id)) : selectedKeys(state.expanded),
88
+ rows: projected.rows
89
+ };
90
+ }
91
+ const store = kernel.ports.store.create({
92
+ snapshot: read(),
93
+ tree: project()
94
+ });
95
+ function write(patch) {
96
+ const atoms = table.baseAtoms;
97
+ for (const [key, value] of Object.entries(patch)) if (!stateSlicesEqual(atoms[key].get(), value)) atoms[key].set(value);
98
+ }
99
+ function configure(next) {
100
+ table.setOptions((previous) => ({
101
+ ...previous,
102
+ data: next.data,
103
+ columns: next.columns ?? previous.columns,
104
+ getRowId: next.getRowId ?? ((_item, index, parent) => parent ? `${parent.id}.${index}` : `${index}`),
105
+ getSubRows: (item, index) => next.getSubRows?.(item, index),
106
+ enableRowSelection: (row) => !(next.isDisabled?.(row.original) ?? false),
107
+ enableMultiRowSelection: next.selectionMode === "multiple"
108
+ }));
109
+ }
110
+ function publish(patch, why, update) {
111
+ if (disposed) return;
112
+ if (publishing) {
113
+ queued.push(() => publish(patch, why, update));
114
+ return;
115
+ }
116
+ publishing = true;
117
+ const previous = read();
118
+ const previousOptions = options;
119
+ const previousTree = store.get().tree;
120
+ try {
121
+ table._reactivity.batch(() => {
122
+ try {
123
+ if (update !== void 0) {
124
+ const nextOptions = {
125
+ ...options,
126
+ ...update
127
+ };
128
+ configure(nextOptions);
129
+ options = nextOptions;
130
+ rows();
131
+ }
132
+ const baseline = { ...previous };
133
+ if (update !== void 0) for (const key of controls) {
134
+ const value = options[key]?.value;
135
+ if (value !== void 0) Object.assign(baseline, { [key]: value });
136
+ }
137
+ const requested = {
138
+ ...baseline,
139
+ ...patch
140
+ };
141
+ const effective = { ...requested };
142
+ for (const key of controls) if (options[key]?.value !== void 0) Object.assign(effective, { [key]: baseline[key] });
143
+ write(effective);
144
+ const visible = project();
145
+ const reconciled = reconcile(previousTree.rows, visible.rows, {
146
+ highlighted: requested.highlighted,
147
+ anchor: requested.anchor,
148
+ expanded: visible.expanded,
149
+ selected: selectedKeys(requested.rowSelection)
150
+ });
151
+ requested.highlighted = reconciled.highlighted;
152
+ requested.anchor = reconciled.anchor;
153
+ requested.rowSelection = keyState(reconciled.selected);
154
+ const details = changeDetails(why);
155
+ for (const key of controls) {
156
+ if (details.canceled || disposed) break;
157
+ if (stateSlicesEqual(baseline[key], requested[key])) continue;
158
+ switch (key) {
159
+ case "expanded":
160
+ options.expanded?.onChange?.(requested.expanded, details);
161
+ break;
162
+ case "sorting":
163
+ options.sorting?.onChange?.(requested.sorting, details);
164
+ break;
165
+ case "highlighted":
166
+ options.highlighted?.onChange?.(requested.highlighted, details);
167
+ break;
168
+ case "rowSelection": options.rowSelection?.onChange?.(requested.rowSelection, details);
169
+ }
170
+ }
171
+ const final = { ...details.canceled || disposed ? baseline : requested };
172
+ for (const key of controls) if (options[key]?.value !== void 0) Object.assign(final, { [key]: baseline[key] });
173
+ write(final);
174
+ if (!disposed) store.set({
175
+ snapshot: read(),
176
+ tree: project()
177
+ });
178
+ } catch (cause) {
179
+ options = previousOptions;
180
+ if (update !== void 0) configure(previousOptions);
181
+ write(previous);
182
+ throw cause;
183
+ }
184
+ });
185
+ } finally {
186
+ publishing = false;
187
+ while (!publishing && queued.length > 0 && !disposed) queued.shift()();
188
+ }
189
+ }
190
+ const subscription = table.store.subscribe(() => {
191
+ if (!publishing && !disposed) publish({}, { reason: "programmatic" });
192
+ });
193
+ function request(key, updater) {
194
+ if (disposed) return;
195
+ if (publishing) {
196
+ queued.push(() => request(key, updater));
197
+ return;
198
+ }
199
+ const current = staged?.[key] ?? read()[key];
200
+ const patch = { [key]: functionalUpdate(updater, current) };
201
+ if (staged) Object.assign(staged, patch);
202
+ else publish(patch, action);
203
+ }
204
+ function subscribe(listener) {
205
+ if (disposed) return () => {};
206
+ const unsubscribe = store.subscribe((value) => {
207
+ if (!disposed) listener(value.tree);
208
+ });
209
+ const release = () => {
210
+ if (subscriptions.delete(release)) unsubscribe();
211
+ };
212
+ subscriptions.add(release);
213
+ return release;
214
+ }
215
+ return {
216
+ table,
217
+ get: () => store.get().tree,
218
+ options: () => options,
219
+ active: () => !disposed,
220
+ request,
221
+ subscribe,
222
+ state: {
223
+ get: () => store.get().snapshot,
224
+ subscribe(listener) {
225
+ return subscribe(() => listener(store.get().snapshot));
226
+ }
227
+ },
228
+ run(why, run) {
229
+ if (disposed) return;
230
+ const previousAction = action;
231
+ action = why;
232
+ staged = {};
233
+ try {
234
+ run();
235
+ const patch = staged;
236
+ staged = void 0;
237
+ publish(patch, why);
238
+ } finally {
239
+ staged = void 0;
240
+ action = previousAction;
241
+ }
242
+ },
243
+ change(patch, why = {}) {
244
+ const next = {};
245
+ if (patch.highlighted !== void 0) next.highlighted = patch.highlighted;
246
+ if (patch.anchor !== void 0) next.anchor = patch.anchor;
247
+ if (patch.expanded !== void 0) next.expanded = keyState(patch.expanded);
248
+ if (patch.selected !== void 0) next.rowSelection = keyState(patch.selected);
249
+ if (staged) Object.assign(staged, next);
250
+ else publish(next, why);
251
+ },
252
+ update: (next) => publish({}, { reason: "items-changed" }, next),
253
+ dispose() {
254
+ if (disposed) return;
255
+ disposed = true;
256
+ queued.length = 0;
257
+ subscription.unsubscribe();
258
+ for (const release of subscriptions) release();
259
+ subscriptions.clear();
260
+ }
261
+ };
262
+ }
263
+ export { changeDetails, createNavigationModel, keyState };
264
+
265
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","names":[],"sources":["../../src/navigation/state.ts"],"sourcesContent":["import type { RowData } from \"@tanstack/table-core\";\nimport { functionalUpdate, stateSlicesEqual } from \"@tanstack/table-core\";\nimport type { Updater } from \"@tanstack/table-core\";\nimport type { TreeState } from \"../collections/types.js\";\nimport type { ChangeDetails } from \"../controllable.js\";\nimport type { Kernel } from \"../kernel.js\";\nimport type { TreeAction, TreeChangeReason, TreeSnapshot } from \"../tree/types.js\";\nimport { reconcile } from \"./reconcile.js\";\nimport type { NavigationOptions, NavigationSnapshot, NavigationTable } from \"./types.js\";\n\nexport function changeDetails({\n reason = \"programmatic\",\n event,\n}: TreeAction = {}): ChangeDetails<TreeChangeReason> {\n let canceled = false;\n return {\n reason,\n event,\n cancel() {\n canceled = true;\n },\n get canceled() {\n return canceled;\n },\n };\n}\n\nexport const keyState = (keys: ReadonlySet<string>) =>\n Object.fromEntries(Array.from(keys, (key) => [key, true as const]));\nconst selectedKeys = (state: Record<string, boolean>) =>\n new Set(Object.keys(state).filter((key) => state[key]));\nconst controls = [\"expanded\", \"sorting\", \"highlighted\", \"rowSelection\"] as const;\n\n/** Table atoms are the authority. The kernel store publishes one adapter snapshot per transaction. */\nexport function createNavigationModel<T extends RowData>(\n kernel: Kernel,\n table: NavigationTable<T>,\n initial: NavigationOptions<T>,\n) {\n let options = initial;\n let disposed = false;\n let publishing = false;\n let staged: Partial<NavigationSnapshot> | undefined;\n let action: TreeAction = {};\n const queued: (() => void)[] = [];\n const subscriptions = new Set<() => void>();\n\n function read(): NavigationSnapshot {\n return table.store.state;\n }\n\n let validatedRows: ReturnType<typeof table.getCoreRowModel>[\"flatRows\"] | undefined;\n\n function rows() {\n const core = table.getCoreRowModel();\n if (validatedRows !== core.flatRows) {\n const ids = new Set<string>();\n for (const row of core.flatRows) {\n if (ids.has(row.id)) throw new Error(`Duplicate row id \"${row.id}\".`);\n ids.add(row.id);\n }\n validatedRows = core.flatRows;\n }\n return table.getRowModel().rows;\n }\n\n let projected:\n | {\n source: ReturnType<typeof rows>;\n expanded: NavigationSnapshot[\"expanded\"];\n disabled: NavigationOptions<T>[\"isDisabled\"];\n text: NavigationOptions<T>[\"getTextValue\"];\n rows: TreeSnapshot<T>[\"rows\"];\n }\n | undefined;\n\n function project(): TreeSnapshot<T> {\n const state = read();\n const visible = rows();\n if (\n projected?.source !== visible ||\n projected.expanded !== state.expanded ||\n projected.disabled !== options.isDisabled ||\n projected.text !== options.getTextValue\n ) {\n const siblings = new Map<string | undefined, typeof visible>();\n for (const row of visible) {\n const group = siblings.get(row.parentId) ?? [];\n if (!siblings.has(row.parentId)) siblings.set(row.parentId, group);\n group.push(row);\n }\n const positions = new Map<string, number>();\n for (const group of siblings.values())\n group.forEach((row, index) => positions.set(row.id, index + 1));\n projected = {\n source: visible,\n expanded: state.expanded,\n disabled: options.isDisabled,\n text: options.getTextValue,\n rows: visible.map((row, index) => ({\n key: row.id,\n item: row.original,\n index,\n depth: row.depth,\n parentKey: row.parentId ?? null,\n hasChildren: row.getCanExpand(),\n expanded: row.getCanExpand() && row.getIsExpanded(),\n posInSet: positions.get(row.id)!,\n setSize: siblings.get(row.parentId)!.length,\n disabled: options.isDisabled?.(row.original) ?? false,\n textValue: options.getTextValue?.(row.original) ?? row.id,\n })),\n };\n }\n return {\n highlighted: state.highlighted,\n anchor: state.anchor,\n selected: selectedKeys(state.rowSelection),\n expanded:\n state.expanded === true\n ? new Set(\n table\n .getCoreRowModel()\n .flatRows.filter((row) => row.getCanExpand())\n .map((row) => row.id),\n )\n : selectedKeys(state.expanded),\n rows: projected.rows,\n };\n }\n\n const store = kernel.ports.store.create({ snapshot: read(), tree: project() });\n\n function write(patch: Partial<NavigationSnapshot>): void {\n // Every key is a registered state slice; the dynamic loop preserves its matching atom/value pair.\n const atoms = table.baseAtoms as unknown as Record<\n string,\n { get(): unknown; set(value: unknown): void }\n >;\n for (const [key, value] of Object.entries(patch)) {\n if (!stateSlicesEqual(atoms[key]!.get(), value)) atoms[key]!.set(value);\n }\n }\n\n function configure(next: NavigationOptions<T>): void {\n table.setOptions((previous) => ({\n ...previous,\n data: next.data,\n columns: next.columns ?? previous.columns,\n getRowId:\n next.getRowId ??\n ((_item, index, parent) => (parent ? `${parent.id}.${index}` : `${index}`)),\n getSubRows: (item, index) => next.getSubRows?.(item, index),\n enableRowSelection: (row) => !(next.isDisabled?.(row.original) ?? false),\n enableMultiRowSelection: next.selectionMode === \"multiple\",\n }));\n }\n\n function publish(\n patch: Partial<NavigationSnapshot>,\n why: TreeAction,\n update?: Partial<NavigationOptions<T>>,\n ): void {\n if (disposed) return;\n if (publishing) {\n queued.push(() => publish(patch, why, update));\n return;\n }\n publishing = true;\n const previous = read();\n const previousOptions = options;\n const previousTree = store.get().tree;\n try {\n table._reactivity.batch(() => {\n try {\n if (update !== undefined) {\n const nextOptions = { ...options, ...update };\n configure(nextOptions);\n options = nextOptions;\n rows(); // Validate new IDs before any callback or publication.\n }\n const baseline = { ...previous };\n if (update !== undefined) {\n for (const key of controls) {\n const value = options[key]?.value;\n if (value !== undefined) Object.assign(baseline, { [key]: value });\n }\n }\n const requested = { ...baseline, ...patch };\n const effective = { ...requested };\n for (const key of controls) {\n if (options[key]?.value !== undefined)\n Object.assign(effective, { [key]: baseline[key] });\n }\n write(effective);\n const visible = project();\n const reconciled = reconcile(previousTree.rows, visible.rows, {\n highlighted: requested.highlighted,\n anchor: requested.anchor,\n expanded: visible.expanded,\n selected: selectedKeys(requested.rowSelection),\n });\n requested.highlighted = reconciled.highlighted;\n requested.anchor = reconciled.anchor;\n requested.rowSelection = keyState(reconciled.selected);\n const details = changeDetails(why);\n for (const key of controls) {\n if (details.canceled || disposed) break;\n if (stateSlicesEqual(baseline[key], requested[key])) continue;\n // The branch keeps each Controllable callback paired with its state type.\n switch (key) {\n case \"expanded\":\n options.expanded?.onChange?.(requested.expanded, details);\n break;\n case \"sorting\":\n options.sorting?.onChange?.(requested.sorting, details);\n break;\n case \"highlighted\":\n options.highlighted?.onChange?.(requested.highlighted, details);\n break;\n case \"rowSelection\":\n options.rowSelection?.onChange?.(requested.rowSelection, details);\n break;\n }\n }\n const accepted = details.canceled || disposed ? baseline : requested;\n const final = { ...accepted };\n for (const key of controls) {\n if (options[key]?.value !== undefined) Object.assign(final, { [key]: baseline[key] });\n }\n write(final);\n if (!disposed) store.set({ snapshot: read(), tree: project() });\n } catch (cause) {\n options = previousOptions;\n if (update !== undefined) configure(previousOptions);\n write(previous);\n throw cause;\n }\n });\n } finally {\n publishing = false;\n while (!publishing && queued.length > 0 && !disposed) queued.shift()!();\n }\n }\n\n const subscription = table.store.subscribe(() => {\n if (!publishing && !disposed) publish({}, { reason: \"programmatic\" });\n });\n\n function request<K extends keyof NavigationSnapshot>(\n key: K,\n updater: Updater<NavigationSnapshot[K]>,\n ): void {\n if (disposed) return;\n if (publishing) {\n queued.push(() => request(key, updater));\n return;\n }\n const current = staged?.[key] ?? read()[key];\n const patch = { [key]: functionalUpdate(updater, current) };\n if (staged) Object.assign(staged, patch);\n else publish(patch, action);\n }\n\n function subscribe(listener: (tree: TreeSnapshot<T>) => void): () => void {\n if (disposed) return () => {};\n const unsubscribe = store.subscribe((value) => {\n if (!disposed) listener(value.tree);\n });\n const release = () => {\n if (subscriptions.delete(release)) unsubscribe();\n };\n subscriptions.add(release);\n return release;\n }\n\n return {\n table,\n get: () => store.get().tree,\n options: () => options,\n active: () => !disposed,\n request,\n subscribe,\n state: {\n get: () => store.get().snapshot,\n subscribe(listener: (state: NavigationSnapshot) => void) {\n return subscribe(() => listener(store.get().snapshot));\n },\n },\n run(why: TreeAction, run: () => void) {\n if (disposed) return;\n const previousAction = action;\n action = why;\n staged = {};\n try {\n run();\n const patch = staged;\n staged = undefined;\n publish(patch, why);\n } finally {\n staged = undefined;\n action = previousAction;\n }\n },\n change(patch: Partial<TreeState>, why: TreeAction = {}) {\n const next: Partial<NavigationSnapshot> = {};\n if (patch.highlighted !== undefined) next.highlighted = patch.highlighted;\n if (patch.anchor !== undefined) next.anchor = patch.anchor;\n if (patch.expanded !== undefined) next.expanded = keyState(patch.expanded);\n if (patch.selected !== undefined) next.rowSelection = keyState(patch.selected);\n if (staged) Object.assign(staged, next);\n else publish(next, why);\n },\n update: (next: Partial<NavigationOptions<T>>) => publish({}, { reason: \"items-changed\" }, next),\n dispose() {\n if (disposed) return;\n disposed = true;\n queued.length = 0;\n subscription.unsubscribe();\n for (const release of subscriptions) release();\n subscriptions.clear();\n },\n };\n}\n\nexport type NavigationModel<T extends RowData> = ReturnType<typeof createNavigationModel<T>>;\n"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { ChangeDetails, Controllable } from "../controllable.js";
2
+ import { CommandScopeOptions, KeyLayer } from "../commands/types.js";
3
+ import { Kernel } from "../kernel.js";
4
+ import { Store } from "../ports/store.js";
5
+ import { TreeAction, TreeChangeReason, TreeCommands, TreeFocusStrategy, TreeLabels, TreeMode, TreeRootProps, TreeRowProps, TreeToggleProps } from "../tree/types.js";
6
+ import { navigationFeatures } from "./index.js";
7
+ import { ColumnDef, ExpandedState, RowData, RowSelectionState, SortingState, Table, TableFeatures, TableState_ColumnFiltering, TableState_ColumnOrdering, TableState_ColumnSizing, TableState_ColumnVisibility, TableState_GlobalFiltering, Updater } from "@tanstack/table-core";
8
+ export interface NavigationState {
9
+ highlighted: string | null;
10
+ anchor: string | null;
11
+ chosenColumn: string | null;
12
+ }
13
+ export interface NavigationSnapshot extends NavigationState, TableState_ColumnFiltering, TableState_GlobalFiltering, TableState_ColumnOrdering, TableState_ColumnSizing, TableState_ColumnVisibility {
14
+ expanded: ExpandedState;
15
+ rowSelection: RowSelectionState;
16
+ sorting: SortingState;
17
+ }
18
+ export interface NavigationOptions<T extends RowData> {
19
+ data: readonly T[];
20
+ getRowId?(item: NoInfer<T>, index: number, parent?: NavigationRow<T>): string;
21
+ getSubRows?(item: NoInfer<T>, index: number): readonly NoInfer<T>[] | undefined;
22
+ columns?: readonly ColumnDef<NavigationFeatures, NoInfer<T>>[];
23
+ isDisabled?(item: NoInfer<T>): boolean;
24
+ getTextValue?(item: NoInfer<T>): string;
25
+ id?: string;
26
+ mode?: TreeMode;
27
+ focusStrategy?: TreeFocusStrategy;
28
+ selectionMode?: "single" | "multiple";
29
+ highlighted?: Controllable<string | null, TreeChangeReason>;
30
+ expanded?: Controllable<ExpandedState, TreeChangeReason>;
31
+ rowSelection?: Controllable<RowSelectionState, TreeChangeReason>;
32
+ sorting?: Controllable<SortingState, TreeChangeReason>;
33
+ loop?: boolean;
34
+ pageSize?: number;
35
+ typeaheadMs?: number;
36
+ labels?: TreeLabels;
37
+ scope?: CommandScopeOptions;
38
+ /** Optional per-instance page-wide keys. Without it the root must have focus. */
39
+ keys?: KeyLayer | undefined;
40
+ onOpen?: ((item: NoInfer<T>, details: ChangeDetails<TreeChangeReason>) => void) | undefined;
41
+ }
42
+ export interface NavigationCommands extends TreeCommands {
43
+ sort(column?: string, action?: TreeAction): void;
44
+ chooseColumn(direction?: 1 | -1, action?: TreeAction): void;
45
+ }
46
+ export type NavigationFeatures = typeof navigationFeatures;
47
+ export type NavigationRow<T extends RowData> = ReturnType<Table<NavigationFeatures, T>["getRow"]>;
48
+ export type NavigationTable<T extends RowData> = Table<NavigationFeatures, T>;
49
+ export interface NavigableTable<T extends RowData> {
50
+ readonly table: NavigationTable<T>;
51
+ readonly state: Pick<Store<NavigationSnapshot>, "get" | "subscribe">;
52
+ readonly commands: NavigationCommands;
53
+ readonly scope: string;
54
+ setHighlighted(updater: Updater<string | null>): void;
55
+ getRootProps(): TreeRootProps;
56
+ getRowProps(id: string): TreeRowProps;
57
+ getToggleProps(id: string): TreeToggleProps;
58
+ update(options: Partial<NavigationOptions<T>>): void;
59
+ dispose(): void;
60
+ }
61
+ export interface NavigationFeatureOptions<T extends RowData> {
62
+ navigation: {
63
+ kernel: Kernel;
64
+ options: NavigationOptions<T>;
65
+ };
66
+ onHighlightedChange?: (updater: Updater<string | null>) => void;
67
+ }
68
+ export interface NavigationTableAPI<T extends RowData> {
69
+ getNavigation(): NavigableTable<T>;
70
+ setHighlighted(updater: Updater<string | null>): void;
71
+ }
72
+ declare module "@tanstack/table-core" {
73
+ interface Plugins {
74
+ navigationFeature: import("@tanstack/table-core").TableFeature;
75
+ }
76
+ interface TableState_FeatureMap {
77
+ navigationFeature: NavigationState;
78
+ }
79
+ interface TableOptions_FeatureMap<TFeatures extends TableFeatures, TData extends RowData> {
80
+ navigationFeature: NavigationFeatureOptions<TData>;
81
+ }
82
+ interface Table_FeatureMap<TFeatures extends TableFeatures, TData extends RowData> {
83
+ navigationFeature: NavigationTableAPI<TData>;
84
+ }
85
+ }
86
+ //# sourceMappingURL=types.d.ts.map
@@ -23,8 +23,8 @@ export interface KeyboardBindingOptions {
23
23
  readonly scope?: string;
24
24
  /** Defaults to document. Element targets require focus within the element. */
25
25
  readonly target?: KeyboardTarget;
26
- /** Defaults to "auto": skip typing keys in controls, but allow Mod shortcuts. */
27
- readonly ignoreInputs?: boolean | "auto";
26
+ /** Defaults to "auto". "editable" skips text fields while allowing keys owned by widgets. */
27
+ readonly ignoreInputs?: boolean | "auto" | "editable";
28
28
  readonly enabled?: boolean;
29
29
  /** Maximum milliseconds between sequence steps. Defaults to 1,000. */
30
30
  readonly timeout?: number;
@@ -51,6 +51,7 @@ export interface KeyboardPort {
51
51
  readonly platform: KeyboardPlatform;
52
52
  /** Validate saved strings or arrays. Malformed input returns a reason, never throws. */
53
53
  parse(input: unknown): KeyboardParseResult;
54
+ /** A label: `⇧J`, `⌘K` on macOS; `Shift+J`, `Ctrl+K` elsewhere; `g r` for a sequence. */
54
55
  format(hotkey: KeyboardHotkey): string;
55
56
  /** Descendants beat ancestors; otherwise the latest activation wins. Release is idempotent. */
56
57
  activateScope(scope: string, options?: KeyboardScopeOptions): () => void;
@@ -0,0 +1,9 @@
1
+ // src/ports/keyboard.ts
2
+ /** Shared by editable-only dispatch and its command legend projection. */
3
+ function isKeyboardEditable(element) {
4
+ while (element?.shadowRoot?.activeElement) element = element.shadowRoot.activeElement;
5
+ return element?.matches("input, textarea, select, [role=\"textbox\"], [role=\"searchbox\"]") === true || element !== null && element !== void 0 && "isContentEditable" in element && element.isContentEditable === true;
6
+ }
7
+ export { isKeyboardEditable };
8
+
9
+ //# sourceMappingURL=keyboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.js","names":[],"sources":["../../src/ports/keyboard.ts"],"sourcesContent":["import type { Hotkey, HotkeyChord, HotkeyPlatform } from \"@tanstack/hotkeys\";\nimport type { Store } from \"./store.js\";\n\n/** TanStack's checked chord strings and readonly arrays of chord strings. */\nexport type KeyboardHotkey = Hotkey;\nexport type KeyboardPlatform = HotkeyPlatform;\nexport type KeyboardTarget = HTMLElement | Document | Window;\nexport type KeyboardHandler = (event: KeyboardEvent) => void;\n\n/** Shared by editable-only dispatch and its command legend projection. */\nexport function isKeyboardEditable(element: Element | null | undefined): boolean {\n while (element?.shadowRoot?.activeElement) element = element.shadowRoot.activeElement;\n return (\n element?.matches('input, textarea, select, [role=\"textbox\"], [role=\"searchbox\"]') === true ||\n (element !== null &&\n element !== undefined &&\n \"isContentEditable\" in element &&\n element.isContentEditable === true)\n );\n}\n\nexport type KeyboardParseResult =\n | { readonly ok: true; readonly hotkey: KeyboardHotkey }\n | { readonly ok: false; readonly reason: string };\n\nexport interface KeyboardScopeOptions {\n readonly parent?: string;\n readonly exclusive?: boolean | { readonly allow: readonly string[] };\n}\n\nexport interface KeyboardBindingOptions {\n /** Defaults to the always-active \"global\" scope. */\n readonly scope?: string;\n /** Defaults to document. Element targets require focus within the element. */\n readonly target?: KeyboardTarget;\n /** Defaults to \"auto\". \"editable\" skips text fields while allowing keys owned by widgets. */\n readonly ignoreInputs?: boolean | \"auto\" | \"editable\";\n readonly enabled?: boolean;\n /** Maximum milliseconds between sequence steps. Defaults to 1,000. */\n readonly timeout?: number;\n}\n\nexport interface KeyboardBinding {\n /** Merge options and optionally replace the handler. A new target resets progress. */\n update(options: KeyboardBindingOptions & { readonly handler?: KeyboardHandler }): void;\n /** Idempotent. Updates after disposal do nothing. */\n dispose(): void;\n}\n\nexport interface PendingKeyboardSequence {\n readonly id: string;\n readonly hotkey: readonly KeyboardHotkeyChord[];\n readonly matchedStepCount: number;\n readonly scope: string;\n readonly target: KeyboardTarget;\n}\n\nexport type KeyboardHotkeyChord = HotkeyChord;\n/** The read side of Store; consumers cannot write sequence progress. */\nexport type KeyboardPendingStore = Pick<\n Store<readonly PendingKeyboardSequence[]>,\n \"get\" | \"subscribe\"\n>;\n\nexport interface KeyboardPort {\n readonly platform: KeyboardPlatform;\n /** Validate saved strings or arrays. Malformed input returns a reason, never throws. */\n parse(input: unknown): KeyboardParseResult;\n /** A label: `⇧J`, `⌘K` on macOS; `Shift+J`, `Ctrl+K` elsewhere; `g r` for a sequence. */\n format(hotkey: KeyboardHotkey): string;\n /** Descendants beat ancestors; otherwise the latest activation wins. Release is idempotent. */\n activateScope(scope: string, options?: KeyboardScopeOptions): () => void;\n bind(\n hotkey: KeyboardHotkey,\n handler: KeyboardHandler,\n options?: KeyboardBindingOptions,\n ): KeyboardBinding;\n readonly pending: KeyboardPendingStore;\n /** Releases all bindings, scopes, subscriptions, timers and DOM listeners once. */\n dispose(): void;\n}\n"],"mappings":";;AAUA,SAAgB,mBAAmB,SAA8C;CAC/E,OAAO,SAAS,YAAY,eAAe,UAAU,QAAQ,WAAW;CACxE,OACE,SAAS,QAAQ,mEAA+D,MAAM,QACrF,YAAY,QACX,YAAY,KAAA,KACZ,uBAAuB,WACvB,QAAQ,sBAAsB;AAEpC"}
@@ -1,27 +1,13 @@
1
- import { Key, TreeRow, TreeSource, TreeState } from "../collections/types.js";
2
- import { ChangeDetails, Controllable } from "../controllable.js";
3
- import { CommandScopeOptions } from "../commands/types.js";
4
- import { Store } from "../ports/store.js";
1
+ import { Key } from "../collections/types.js";
5
2
  export type TreeChangeReason = "keyboard" | "pointer" | "typeahead" | "programmatic" | "items-changed";
6
3
  export type TreeMode = "tree" | "listbox" | "treegrid" | "grid";
7
4
  export type TreeFocusStrategy = "active-descendant" | "roving";
8
- export interface TreeOptions<T> extends TreeSource<T> {
9
- /** Supply a stable id for server rendering. Otherwise generated per instance. */
10
- id?: string;
11
- mode?: TreeMode;
12
- focusStrategy?: TreeFocusStrategy;
13
- selectionMode?: "single" | "multiple";
14
- highlighted?: Controllable<Key | null, TreeChangeReason>;
15
- selected?: Controllable<ReadonlySet<Key>, TreeChangeReason>;
16
- expanded?: Controllable<ReadonlySet<Key>, TreeChangeReason>;
17
- loop?: boolean;
18
- /** Number of enabled rows per page. Defaults to 10. */
19
- pageSize?: number;
20
- /** Milliseconds between typed characters. Defaults to 500. */
21
- typeaheadMs?: number;
22
- /** Scope options, including the mounted root target for keyboard isolation. */
23
- scope?: CommandScopeOptions;
24
- onOpen?(item: NoInfer<T>, details: ChangeDetails<TreeChangeReason>): void;
5
+ /** Accessible names of a row's expand/collapse toggle, for localization. */
6
+ export interface TreeLabels {
7
+ /** Names a collapsed row's toggle. Defaults to "Expand". */
8
+ readonly expand?: string;
9
+ /** Names an expanded row's toggle. Defaults to "Collapse". */
10
+ readonly collapse?: string;
25
11
  }
26
12
  export interface TreeAction {
27
13
  /** Defaults to the highlighted row. */
@@ -43,9 +29,6 @@ export interface TreeCommands {
43
29
  extendSelection(action?: TreeAction): void;
44
30
  open(action?: TreeAction): void;
45
31
  }
46
- export interface TreeSnapshot<T> extends Readonly<TreeState> {
47
- readonly rows: readonly TreeRow<T>[];
48
- }
49
32
  export interface TreePartProps {
50
33
  /** Grid and treegrid modes use the `table-*` slots. */
51
34
  "data-slot": "tree-root" | "tree-row" | "tree-toggle" | "table-root" | "table-row" | "table-toggle";
@@ -92,16 +75,4 @@ export interface TreeToggleProps extends TreePartProps {
92
75
  "aria-expanded": boolean | undefined;
93
76
  onClick(event: MouseEvent): void;
94
77
  }
95
- export interface Tree<T> {
96
- readonly rows: readonly TreeRow<T>[];
97
- readonly state: Pick<Store<TreeSnapshot<T>>, "get" | "subscribe">;
98
- readonly commands: TreeCommands;
99
- readonly scope: string;
100
- /** Merge live data, control values and presentation options. Defaults apply only at creation. */
101
- update(options: Partial<TreeOptions<T>>): void;
102
- getRootProps(): TreeRootProps;
103
- getRowProps(key: Key): TreeRowProps;
104
- getToggleProps(key: Key): TreeToggleProps;
105
- dispose(): void;
106
- }
107
78
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@densedash/core",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic densedash kernel, ports and plugin contracts.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@tanstack/hotkeys": "0.24.0",
36
- "@tanstack/store": "0.11.2"
36
+ "@tanstack/store": "0.11.2",
37
+ "@tanstack/table-core": "9.3.1"
37
38
  },
38
39
  "scripts": {
39
40
  "build": "tsdown",
@@ -1,8 +0,0 @@
1
- import { Key, TreeRow, TreeSource } from "./types.js";
2
- /**
3
- * Visible preorder rows. Index/depth are zero-based; sibling positions are one-based.
4
- * Validate the entire source, including collapsed branches, in O(total items).
5
- * Duplicate keys and ancestor cycles throw in every environment, naming the key.
6
- */
7
- export declare function flatten<T>(source: TreeSource<T>, expanded: ReadonlySet<Key>): TreeRow<T>[];
8
- //# sourceMappingURL=flatten.d.ts.map
@@ -1,59 +0,0 @@
1
- // src/collections/flatten.ts
2
- /**
3
- * Visible preorder rows. Index/depth are zero-based; sibling positions are one-based.
4
- * Validate the entire source, including collapsed branches, in O(total items).
5
- * Duplicate keys and ancestor cycles throw in every environment, naming the key.
6
- */
7
- function flatten(source, expanded) {
8
- const rows = [];
9
- const seen = /* @__PURE__ */ new Set();
10
- const ancestors = /* @__PURE__ */ new Set();
11
- const stack = [{
12
- items: source.roots,
13
- offset: 0,
14
- parentKey: null,
15
- visible: true
16
- }];
17
- while (stack.length > 0) {
18
- const frame = stack[stack.length - 1];
19
- if (frame.offset === frame.items.length) {
20
- if (frame.parentKey !== null) ancestors.delete(frame.parentKey);
21
- stack.pop();
22
- continue;
23
- }
24
- const item = frame.items[frame.offset++];
25
- const key = source.getKey(item);
26
- if (ancestors.has(key)) throw new Error(`Collection cycle at key "${key}".`);
27
- if (seen.has(key)) throw new Error(`Duplicate collection key "${key}".`);
28
- seen.add(key);
29
- const children = source.getChildren?.(item);
30
- const hasChildren = children !== void 0 && children.length > 0;
31
- const isExpanded = hasChildren && expanded.has(key);
32
- if (frame.visible) rows.push({
33
- key,
34
- item,
35
- index: rows.length,
36
- depth: stack.length - 1,
37
- parentKey: frame.parentKey,
38
- hasChildren,
39
- expanded: isExpanded,
40
- posInSet: frame.offset,
41
- setSize: frame.items.length,
42
- disabled: source.isDisabled?.(item) ?? false,
43
- textValue: source.getTextValue?.(item) ?? key
44
- });
45
- if (children !== void 0 && hasChildren) {
46
- ancestors.add(key);
47
- stack.push({
48
- items: children,
49
- offset: 0,
50
- parentKey: key,
51
- visible: frame.visible && isExpanded
52
- });
53
- }
54
- }
55
- return rows;
56
- }
57
- export { flatten };
58
-
59
- //# sourceMappingURL=flatten.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"flatten.js","names":[],"sources":["../../src/collections/flatten.ts"],"sourcesContent":["import type { Key, TreeRow, TreeSource } from \"./types.js\";\n\ninterface Frame<T> {\n items: readonly T[];\n offset: number;\n parentKey: Key | null;\n visible: boolean;\n}\n\n/**\n * Visible preorder rows. Index/depth are zero-based; sibling positions are one-based.\n * Validate the entire source, including collapsed branches, in O(total items).\n * Duplicate keys and ancestor cycles throw in every environment, naming the key.\n */\nexport function flatten<T>(source: TreeSource<T>, expanded: ReadonlySet<Key>): TreeRow<T>[] {\n const rows: TreeRow<T>[] = [];\n const seen = new Set<Key>();\n const ancestors = new Set<Key>();\n const stack: Frame<T>[] = [{ items: source.roots, offset: 0, parentKey: null, visible: true }];\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1]!;\n if (frame.offset === frame.items.length) {\n if (frame.parentKey !== null) ancestors.delete(frame.parentKey);\n stack.pop();\n continue;\n }\n\n const item = frame.items[frame.offset++]!;\n const key = source.getKey(item);\n if (ancestors.has(key)) throw new Error(`Collection cycle at key \"${key}\".`);\n if (seen.has(key)) throw new Error(`Duplicate collection key \"${key}\".`);\n seen.add(key);\n\n const children = source.getChildren?.(item);\n const hasChildren = children !== undefined && children.length > 0;\n const isExpanded = hasChildren && expanded.has(key);\n if (frame.visible) {\n rows.push({\n key,\n item,\n index: rows.length,\n depth: stack.length - 1,\n parentKey: frame.parentKey,\n hasChildren,\n expanded: isExpanded,\n posInSet: frame.offset,\n setSize: frame.items.length,\n disabled: source.isDisabled?.(item) ?? false,\n textValue: source.getTextValue?.(item) ?? key,\n });\n }\n\n if (children !== undefined && hasChildren) {\n ancestors.add(key);\n stack.push({\n items: children,\n offset: 0,\n parentKey: key,\n visible: frame.visible && isExpanded,\n });\n }\n }\n\n return rows;\n}\n"],"mappings":";;;;;;AAcA,SAAgB,QAAW,QAAuB,UAA0C;CAC1F,MAAM,OAAqB,CAAC;CAC5B,MAAM,uBAAO,IAAI,IAAS;CAC1B,MAAM,4BAAY,IAAI,IAAS;CAC/B,MAAM,QAAoB,CAAC;EAAE,OAAO,OAAO;EAAO,QAAQ;EAAG,WAAW;EAAM,SAAS;CAAK,CAAC;CAE7F,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,QAAQ,MAAM,MAAM,SAAS;EACnC,IAAI,MAAM,WAAW,MAAM,MAAM,QAAQ;GACvC,IAAI,MAAM,cAAc,MAAM,UAAU,OAAO,MAAM,SAAS;GAC9D,MAAM,IAAI;GACV;EACF;EAEA,MAAM,OAAO,MAAM,MAAM,MAAM;EAC/B,MAAM,MAAM,OAAO,OAAO,IAAI;EAC9B,IAAI,UAAU,IAAI,GAAG,GAAG,MAAM,IAAI,MAAM,4BAA4B,IAAI,GAAG;EAC3E,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,IAAI,MAAM,6BAA6B,IAAI,GAAG;EACvE,KAAK,IAAI,GAAG;EAEZ,MAAM,WAAW,OAAO,cAAc,IAAI;EAC1C,MAAM,cAAc,aAAa,KAAA,KAAa,SAAS,SAAS;EAChE,MAAM,aAAa,eAAe,SAAS,IAAI,GAAG;EAClD,IAAI,MAAM,SACR,KAAK,KAAK;GACR;GACA;GACA,OAAO,KAAK;GACZ,OAAO,MAAM,SAAS;GACtB,WAAW,MAAM;GACjB;GACA,UAAU;GACV,UAAU,MAAM;GAChB,SAAS,MAAM,MAAM;GACrB,UAAU,OAAO,aAAa,IAAI,KAAK;GACvC,WAAW,OAAO,eAAe,IAAI,KAAK;EAC5C,CAAC;EAGH,IAAI,aAAa,KAAA,KAAa,aAAa;GACzC,UAAU,IAAI,GAAG;GACjB,MAAM,KAAK;IACT,OAAO;IACP,QAAQ;IACR,WAAW;IACX,SAAS,MAAM,WAAW;GAC5B,CAAC;EACH;CACF;CAEA,OAAO;AACT"}
@@ -1,7 +0,0 @@
1
- import { Key, Move, MoveOptions, TreeRow } from "./types.js";
2
- /**
3
- * Return a key; never mutate rows or state. No target leaves the current enabled
4
- * key in place (or null). Counts and pages count enabled rows. Empty rows return null.
5
- */
6
- export declare function move<T>(rows: readonly TreeRow<T>[], from: Key | null, movement: Move, { loop }?: MoveOptions): Key | null;
7
- //# sourceMappingURL=move.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"move.js","names":[],"sources":["../../src/collections/move.ts"],"sourcesContent":["import type { Key, Move, MoveOptions, TreeRow } from \"./types.js\";\n\n/** Move by enabled rows without iterating count times, even for huge counts. */\nfunction step<T>(\n rows: readonly TreeRow<T>[],\n fromIndex: number,\n count: number,\n direction: 1 | -1,\n loop: boolean,\n): Key | null {\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new RangeError(\"Collection move count/size must be a non-negative safe integer.\");\n }\n\n const keys: Key[] = [];\n let before = 0;\n let current = -1;\n for (let index = 0; index < rows.length; index++) {\n const row = rows[index]!;\n if (row.disabled) continue;\n if (index < fromIndex) before++;\n if (index === fromIndex) current = keys.length;\n keys.push(row.key);\n }\n if (keys.length === 0) return null;\n if (count === 0) return current < 0 ? null : keys[current]!;\n\n let start = current;\n if (current < 0) {\n start =\n fromIndex < 0 ? (direction === 1 ? -1 : keys.length) : direction === 1 ? before - 1 : before;\n }\n // Reduce before addition so even MAX_SAFE_INTEGER stays arithmetically exact.\n const distance = loop ? count % keys.length : Math.min(count, keys.length);\n const target = start + direction * distance;\n const bounded = loop\n ? ((target % keys.length) + keys.length) % keys.length\n : Math.max(0, Math.min(keys.length - 1, target));\n return keys[bounded]!;\n}\n\n/**\n * Return a key; never mutate rows or state. No target leaves the current enabled\n * key in place (or null). Counts and pages count enabled rows. Empty rows return null.\n */\nexport function move<T>(\n rows: readonly TreeRow<T>[],\n from: Key | null,\n movement: Move,\n { loop = false }: MoveOptions = {},\n): Key | null {\n const fromIndex = from === null ? -1 : rows.findIndex((row) => row.key === from);\n const current = rows[fromIndex];\n const unchanged = current && !current.disabled ? current.key : null;\n\n switch (movement.type) {\n case \"next\":\n case \"prev\":\n return step(rows, fromIndex, movement.count ?? 1, movement.type === \"next\" ? 1 : -1, loop);\n case \"page\":\n return step(rows, fromIndex, movement.size, movement.direction, loop);\n case \"first\":\n return rows.find((row) => !row.disabled)?.key ?? null;\n case \"last\":\n for (let index = rows.length - 1; index >= 0; index--) {\n const row = rows[index]!;\n if (!row.disabled) return row.key;\n }\n return null;\n case \"parent\": {\n let parentKey = current?.parentKey;\n for (let index = fromIndex - 1; index >= 0 && parentKey != null; index--) {\n const row = rows[index]!;\n if (row.key !== parentKey) continue;\n if (!row.disabled) return row.key;\n parentKey = row.parentKey;\n }\n return unchanged;\n }\n case \"firstChild\":\n if (!current) return null;\n for (let index = fromIndex + 1; index < rows.length; index++) {\n const row = rows[index]!;\n if (row.depth <= current.depth) break;\n if (row.parentKey === current.key && !row.disabled) return row.key;\n }\n return unchanged;\n case \"typeahead\": {\n const text = movement.text.toLowerCase();\n if (text.length === 0) return unchanged;\n const end = loop ? rows.length + fromIndex + 1 : rows.length;\n for (let index = fromIndex + 1; index < end; index++) {\n const row = rows[index % rows.length]!;\n if (!row.disabled && row.textValue.toLowerCase().startsWith(text)) return row.key;\n }\n return unchanged;\n }\n }\n}\n"],"mappings":";;AAGA,SAAS,KACP,MACA,WACA,OACA,WACA,MACY;CACZ,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,GAC1C,MAAM,IAAI,WAAW,iEAAiE;CAGxF,MAAM,OAAc,CAAC;CACrB,IAAI,SAAS;CACb,IAAI,UAAU;CACd,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;EAChD,MAAM,MAAM,KAAK;EACjB,IAAI,IAAI,UAAU;EAClB,IAAI,QAAQ,WAAW;EACvB,IAAI,UAAU,WAAW,UAAU,KAAK;EACxC,KAAK,KAAK,IAAI,GAAG;CACnB;CACA,IAAI,KAAK,WAAW,GAAG,OAAO;CAC9B,IAAI,UAAU,GAAG,OAAO,UAAU,IAAI,OAAO,KAAK;CAElD,IAAI,QAAQ;CACZ,IAAI,UAAU,GACZ,QACE,YAAY,IAAK,cAAc,IAAI,KAAK,KAAK,SAAU,cAAc,IAAI,SAAS,IAAI;CAG1F,MAAM,WAAW,OAAO,QAAQ,KAAK,SAAS,KAAK,IAAI,OAAO,KAAK,MAAM;CACzE,MAAM,SAAS,QAAQ,YAAY;CAInC,OAAO,KAHS,QACV,SAAS,KAAK,SAAU,KAAK,UAAU,KAAK,SAC9C,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,MAAM,CAAC;AAEnD;;;;;AAMA,SAAgB,KACd,MACA,MACA,UACA,EAAE,OAAO,UAAuB,CAAC,GACrB;CACZ,MAAM,YAAY,SAAS,OAAO,KAAK,KAAK,WAAW,QAAQ,IAAI,QAAQ,IAAI;CAC/E,MAAM,UAAU,KAAK;CACrB,MAAM,YAAY,WAAW,CAAC,QAAQ,WAAW,QAAQ,MAAM;CAE/D,QAAQ,SAAS,MAAjB;EACE,KAAK;EACL,KAAK,QACH,OAAO,KAAK,MAAM,WAAW,SAAS,SAAS,GAAG,SAAS,SAAS,SAAS,IAAI,IAAI,IAAI;EAC3F,KAAK,QACH,OAAO,KAAK,MAAM,WAAW,SAAS,MAAM,SAAS,WAAW,IAAI;EACtE,KAAK,SACH,OAAO,KAAK,MAAM,QAAQ,CAAC,IAAI,QAAQ,CAAC,EAAE,OAAO;EACnD,KAAK;GACH,KAAK,IAAI,QAAQ,KAAK,SAAS,GAAG,SAAS,GAAG,SAAS;IACrD,MAAM,MAAM,KAAK;IACjB,IAAI,CAAC,IAAI,UAAU,OAAO,IAAI;GAChC;GACA,OAAO;EACT,KAAK,UAAU;GACb,IAAI,YAAY,SAAS;GACzB,KAAK,IAAI,QAAQ,YAAY,GAAG,SAAS,KAAK,aAAa,MAAM,SAAS;IACxE,MAAM,MAAM,KAAK;IACjB,IAAI,IAAI,QAAQ,WAAW;IAC3B,IAAI,CAAC,IAAI,UAAU,OAAO,IAAI;IAC9B,YAAY,IAAI;GAClB;GACA,OAAO;EACT;EACA,KAAK;GACH,IAAI,CAAC,SAAS,OAAO;GACrB,KAAK,IAAI,QAAQ,YAAY,GAAG,QAAQ,KAAK,QAAQ,SAAS;IAC5D,MAAM,MAAM,KAAK;IACjB,IAAI,IAAI,SAAS,QAAQ,OAAO;IAChC,IAAI,IAAI,cAAc,QAAQ,OAAO,CAAC,IAAI,UAAU,OAAO,IAAI;GACjE;GACA,OAAO;EACT,KAAK,aAAa;GAChB,MAAM,OAAO,SAAS,KAAK,YAAY;GACvC,IAAI,KAAK,WAAW,GAAG,OAAO;GAC9B,MAAM,MAAM,OAAO,KAAK,SAAS,YAAY,IAAI,KAAK;GACtD,KAAK,IAAI,QAAQ,YAAY,GAAG,QAAQ,KAAK,SAAS;IACpD,MAAM,MAAM,KAAK,QAAQ,KAAK;IAC9B,IAAI,CAAC,IAAI,YAAY,IAAI,UAAU,YAAY,CAAC,CAAC,WAAW,IAAI,GAAG,OAAO,IAAI;GAChF;GACA,OAAO;EACT;CACF;AACF"}
@@ -1,9 +0,0 @@
1
- import { TreeRow, TreeState } from "./types.js";
2
- /**
3
- * Reconcile against supplied visible rows in O(prev + next + selected).
4
- * Keep a live enabled cursor by key. Otherwise prefer the nearest enabled survivor
5
- * in the old order (following wins ties), then the first enabled new row.
6
- * Prune invisible selection/anchor; retain expansion for hidden descendants.
7
- */
8
- export declare function reconcile<T>(prev: readonly TreeRow<T>[], next: readonly TreeRow<T>[], state: TreeState): TreeState;
9
- //# sourceMappingURL=reconcile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reconcile.js","names":[],"sources":["../../src/collections/reconcile.ts"],"sourcesContent":["import type { Key, TreeRow, TreeState } from \"./types.js\";\n\n/**\n * Reconcile against supplied visible rows in O(prev + next + selected).\n * Keep a live enabled cursor by key. Otherwise prefer the nearest enabled survivor\n * in the old order (following wins ties), then the first enabled new row.\n * Prune invisible selection/anchor; retain expansion for hidden descendants.\n */\nexport function reconcile<T>(\n prev: readonly TreeRow<T>[],\n next: readonly TreeRow<T>[],\n state: TreeState,\n): TreeState {\n const existing = new Set<Key>();\n const enabled = new Set<Key>();\n for (const row of next) {\n existing.add(row.key);\n if (!row.disabled) enabled.add(row.key);\n }\n\n let highlighted = state.highlighted;\n if (highlighted !== null && !enabled.has(highlighted)) {\n const previousIndex = prev.findIndex((row) => row.key === highlighted);\n highlighted = null;\n if (previousIndex >= 0) {\n for (let distance = 1; distance < prev.length; distance++) {\n const following = prev[previousIndex + distance];\n const preceding = prev[previousIndex - distance];\n if (following && enabled.has(following.key)) {\n highlighted = following.key;\n break;\n }\n if (preceding && enabled.has(preceding.key)) {\n highlighted = preceding.key;\n break;\n }\n }\n }\n highlighted ??= next.find((row) => !row.disabled)?.key ?? null;\n }\n\n const selected = new Set<Key>();\n for (const key of state.selected) {\n if (existing.has(key)) selected.add(key);\n }\n\n return {\n highlighted,\n selected,\n anchor: state.anchor !== null && existing.has(state.anchor) ? state.anchor : null,\n expanded: state.expanded,\n };\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,UACd,MACA,MACA,OACW;CACX,MAAM,2BAAW,IAAI,IAAS;CAC9B,MAAM,0BAAU,IAAI,IAAS;CAC7B,KAAK,MAAM,OAAO,MAAM;EACtB,SAAS,IAAI,IAAI,GAAG;EACpB,IAAI,CAAC,IAAI,UAAU,QAAQ,IAAI,IAAI,GAAG;CACxC;CAEA,IAAI,cAAc,MAAM;CACxB,IAAI,gBAAgB,QAAQ,CAAC,QAAQ,IAAI,WAAW,GAAG;EACrD,MAAM,gBAAgB,KAAK,WAAW,QAAQ,IAAI,QAAQ,WAAW;EACrE,cAAc;EACd,IAAI,iBAAiB,GACnB,KAAK,IAAI,WAAW,GAAG,WAAW,KAAK,QAAQ,YAAY;GACzD,MAAM,YAAY,KAAK,gBAAgB;GACvC,MAAM,YAAY,KAAK,gBAAgB;GACvC,IAAI,aAAa,QAAQ,IAAI,UAAU,GAAG,GAAG;IAC3C,cAAc,UAAU;IACxB;GACF;GACA,IAAI,aAAa,QAAQ,IAAI,UAAU,GAAG,GAAG;IAC3C,cAAc,UAAU;IACxB;GACF;EACF;EAEF,gBAAgB,KAAK,MAAM,QAAQ,CAAC,IAAI,QAAQ,CAAC,EAAE,OAAO;CAC5D;CAEA,MAAM,2BAAW,IAAI,IAAS;CAC9B,KAAK,MAAM,OAAO,MAAM,UACtB,IAAI,SAAS,IAAI,GAAG,GAAG,SAAS,IAAI,GAAG;CAGzC,OAAO;EACL;EACA;EACA,QAAQ,MAAM,WAAW,QAAQ,SAAS,IAAI,MAAM,MAAM,IAAI,MAAM,SAAS;EAC7E,UAAU,MAAM;CAClB;AACF"}
@@ -1,12 +0,0 @@
1
- import { Key, TreeRow } from "./types.js";
2
- /** Toggle a visible enabled key, preserving the caller's selected set. */
3
- export declare function toggleSelection<T>(rows: readonly TreeRow<T>[], selected: ReadonlySet<Key>, key: Key): ReadonlySet<Key>;
4
- /**
5
- * Replace selection with an inclusive visible range, excluding disabled rows.
6
- * A missing anchor selects only the target; a missing target yields an empty set.
7
- */
8
- export declare function selectRange<T>(rows: readonly TreeRow<T>[], anchor: Key | null, to: Key): ReadonlySet<Key>;
9
- /** Select every visible enabled row. */
10
- export declare function selectAll<T>(rows: readonly TreeRow<T>[]): ReadonlySet<Key>;
11
- export declare function clearSelection(): ReadonlySet<Key>;
12
- //# sourceMappingURL=selection.d.ts.map