@human-kit/svelte-components 1.0.0-alpha.4 → 1.0.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/calendar/index.d.ts +3 -3
- package/dist/calendar/index.js +3 -3
- package/dist/checkbox/README.md +53 -0
- package/dist/checkbox/TODO.md +16 -0
- package/dist/checkbox/index.d.ts +6 -0
- package/dist/checkbox/index.js +6 -0
- package/dist/checkbox/index.parts.d.ts +2 -0
- package/dist/checkbox/index.parts.js +2 -0
- package/dist/checkbox/indicator/README.md +23 -0
- package/dist/checkbox/indicator/checkbox-indicator.svelte +43 -0
- package/dist/checkbox/indicator/checkbox-indicator.svelte.d.ts +10 -0
- package/dist/checkbox/root/README.md +47 -0
- package/dist/checkbox/root/checkbox-label-test.svelte +10 -0
- package/dist/checkbox/root/checkbox-label-test.svelte.d.ts +18 -0
- package/dist/checkbox/root/checkbox-root.svelte +361 -0
- package/dist/checkbox/root/checkbox-root.svelte.d.ts +23 -0
- package/dist/checkbox/root/checkbox-test.svelte +59 -0
- package/dist/checkbox/root/checkbox-test.svelte.d.ts +18 -0
- package/dist/checkbox/root/context.d.ts +21 -0
- package/dist/checkbox/root/context.js +15 -0
- package/dist/clock/index.d.ts +5 -5
- package/dist/clock/index.js +5 -5
- package/dist/combobox/index.d.ts +3 -3
- package/dist/combobox/index.js +3 -3
- package/dist/combobox/input/combobox-input.svelte +17 -2
- package/dist/combobox/list/combobox-listbox.svelte.d.ts +1 -1
- package/dist/combobox/popover/combobox-popover.svelte +17 -1
- package/dist/combobox/root/combobox.svelte +5 -0
- package/dist/combobox/root/context.d.ts +2 -0
- package/dist/datepicker/index.d.ts +3 -3
- package/dist/datepicker/index.js +3 -3
- package/dist/dialog/index.d.ts +3 -3
- package/dist/dialog/index.js +2 -2
- package/dist/index.d.ts +29 -25
- package/dist/index.js +29 -25
- package/dist/listbox/index.d.ts +3 -3
- package/dist/listbox/index.js +3 -3
- package/dist/popover/index.d.ts +3 -3
- package/dist/popover/index.js +3 -3
- package/dist/table/IMPLEMENTATION_NOTES.md +8 -0
- package/dist/table/PLAN-HIDDEN-COLUMNS.md +152 -0
- package/dist/table/PLAN.md +924 -0
- package/dist/table/README.md +116 -0
- package/dist/table/SELECTION_CHECKBOX_PLAN.md +234 -0
- package/dist/table/TODO.md +100 -0
- package/dist/table/body/README.md +24 -0
- package/dist/table/body/table-body.svelte +25 -0
- package/dist/table/body/table-body.svelte.d.ts +9 -0
- package/dist/table/cell/README.md +25 -0
- package/dist/table/cell/table-cell.svelte +247 -0
- package/dist/table/cell/table-cell.svelte.d.ts +9 -0
- package/dist/table/checkbox/README.md +38 -0
- package/dist/table/checkbox/table-checkbox-test.svelte +121 -0
- package/dist/table/checkbox/table-checkbox-test.svelte.d.ts +16 -0
- package/dist/table/checkbox/table-checkbox.svelte +274 -0
- package/dist/table/checkbox/table-checkbox.svelte.d.ts +13 -0
- package/dist/table/checkbox-indicator/README.md +29 -0
- package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte +22 -0
- package/dist/table/checkbox-indicator/table-checkbox-indicator.svelte.d.ts +10 -0
- package/dist/table/column/README.md +32 -0
- package/dist/table/column/table-column.svelte +108 -0
- package/dist/table/column/table-column.svelte.d.ts +18 -0
- package/dist/table/column-header-cell/README.md +28 -0
- package/dist/table/column-header-cell/table-column-header-cell.svelte +281 -0
- package/dist/table/column-header-cell/table-column-header-cell.svelte.d.ts +9 -0
- package/dist/table/column-resizer/README.md +32 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte +51 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte +83 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-test.svelte +75 -0
- package/dist/table/column-resizer/table-column-resizer-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer.svelte +616 -0
- package/dist/table/column-resizer/table-column-resizer.svelte.d.ts +11 -0
- package/dist/table/empty-state/README.md +25 -0
- package/dist/table/empty-state/table-empty-state.svelte +38 -0
- package/dist/table/empty-state/table-empty-state.svelte.d.ts +8 -0
- package/dist/table/footer/README.md +24 -0
- package/dist/table/footer/table-footer.svelte +19 -0
- package/dist/table/footer/table-footer.svelte.d.ts +9 -0
- package/dist/table/header/README.md +24 -0
- package/dist/table/header/table-header.svelte +19 -0
- package/dist/table/header/table-header.svelte.d.ts +9 -0
- package/dist/table/index.d.ts +16 -0
- package/dist/table/index.js +16 -0
- package/dist/table/index.parts.d.ts +12 -0
- package/dist/table/index.parts.js +12 -0
- package/dist/table/root/README.md +56 -0
- package/dist/table/root/context.d.ts +198 -0
- package/dist/table/root/context.js +1426 -0
- package/dist/table/root/table-reorder-test.svelte +64 -0
- package/dist/table/root/table-reorder-test.svelte.d.ts +3 -0
- package/dist/table/root/table-root.svelte +410 -0
- package/dist/table/root/table-root.svelte.d.ts +29 -0
- package/dist/table/root/table-test.svelte +165 -0
- package/dist/table/root/table-test.svelte.d.ts +25 -0
- package/dist/table/row/README.md +27 -0
- package/dist/table/row/table-row.svelte +321 -0
- package/dist/table/row/table-row.svelte.d.ts +13 -0
- package/dist/timepicker/index.d.ts +5 -5
- package/dist/timepicker/index.js +5 -5
- package/package.json +11 -1
|
@@ -0,0 +1,1426 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { writable } from 'svelte/store';
|
|
3
|
+
const TABLE_KEY = Symbol('table');
|
|
4
|
+
const TABLE_SECTION_KEY = Symbol('table-section');
|
|
5
|
+
const TABLE_ROW_KEY = Symbol('table-row');
|
|
6
|
+
const TABLE_COLUMN_KEY = Symbol('table-column');
|
|
7
|
+
const TABLE_CELL_KEY = Symbol('table-cell');
|
|
8
|
+
export function createTableContext(options = {}) {
|
|
9
|
+
let selectionMode = options.selectionMode ?? 'none';
|
|
10
|
+
let selectionBehavior = options.selectionBehavior ?? 'toggle';
|
|
11
|
+
let sortDescriptor = options.initialSortDescriptor;
|
|
12
|
+
let focusedCellKey = null;
|
|
13
|
+
let focusedRowTarget = null;
|
|
14
|
+
let focusVisible = false;
|
|
15
|
+
let selectedKeys = new Set(options.initialSelectedKeys ?? []);
|
|
16
|
+
let selectionAnchorKey = selectedKeys.values().next().value ?? null;
|
|
17
|
+
const disabledKeys = new Set(options.disabledKeys ?? []);
|
|
18
|
+
const hiddenColumnIds = new Set(options.initialHiddenColumns ?? []);
|
|
19
|
+
let resizingColumnId = null;
|
|
20
|
+
let suppressNextHeaderClick = false;
|
|
21
|
+
const columns = new Map();
|
|
22
|
+
const columnIds = new Map();
|
|
23
|
+
const columnOrder = [];
|
|
24
|
+
const columnsWithResizers = new Set();
|
|
25
|
+
const columnWidths = new Map(options.initialColumnWidths ?? []);
|
|
26
|
+
const rows = new Map();
|
|
27
|
+
const headerRowOrder = [];
|
|
28
|
+
const bodyRowOrder = [];
|
|
29
|
+
const cells = new Map();
|
|
30
|
+
const cellOrder = [];
|
|
31
|
+
let orderedRowTokensCache = {
|
|
32
|
+
header: null,
|
|
33
|
+
body: null
|
|
34
|
+
};
|
|
35
|
+
let orderedColumnTokensCache = null;
|
|
36
|
+
let visibleOrderedColumnTokensCache = null;
|
|
37
|
+
let columnWidthsCache = null;
|
|
38
|
+
let navigableCellsCache = null;
|
|
39
|
+
let rowsWithCellsCache = null;
|
|
40
|
+
const layoutVersion = writable(0);
|
|
41
|
+
const selectionVersion = writable(0);
|
|
42
|
+
const focusVersion = writable(0);
|
|
43
|
+
const sortVersion = writable(0);
|
|
44
|
+
const widthVersion = writable(0);
|
|
45
|
+
const resizeVersion = writable(0);
|
|
46
|
+
const instanceCounters = new Map();
|
|
47
|
+
function createInstanceToken(prefix) {
|
|
48
|
+
const nextCount = (instanceCounters.get(prefix) ?? 0) + 1;
|
|
49
|
+
instanceCounters.set(prefix, nextCount);
|
|
50
|
+
return `table-${prefix}-${nextCount}`;
|
|
51
|
+
}
|
|
52
|
+
function invalidateLayoutCaches() {
|
|
53
|
+
orderedRowTokensCache = { header: null, body: null };
|
|
54
|
+
orderedColumnTokensCache = null;
|
|
55
|
+
visibleOrderedColumnTokensCache = null;
|
|
56
|
+
columnWidthsCache = null;
|
|
57
|
+
navigableCellsCache = null;
|
|
58
|
+
rowsWithCellsCache = null;
|
|
59
|
+
}
|
|
60
|
+
let layoutNotifyScheduled = false;
|
|
61
|
+
let widthNotifyScheduled = false;
|
|
62
|
+
function notifyLayout() {
|
|
63
|
+
invalidateLayoutCaches();
|
|
64
|
+
if (!layoutNotifyScheduled) {
|
|
65
|
+
layoutNotifyScheduled = true;
|
|
66
|
+
queueMicrotask(() => {
|
|
67
|
+
layoutNotifyScheduled = false;
|
|
68
|
+
layoutVersion.update((value) => value + 1);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function notifySelection() {
|
|
73
|
+
selectionVersion.update((value) => value + 1);
|
|
74
|
+
}
|
|
75
|
+
function notifyFocus() {
|
|
76
|
+
focusVersion.update((value) => value + 1);
|
|
77
|
+
}
|
|
78
|
+
function notifySort() {
|
|
79
|
+
invalidateLayoutCaches();
|
|
80
|
+
sortVersion.update((value) => value + 1);
|
|
81
|
+
}
|
|
82
|
+
function notifyWidth() {
|
|
83
|
+
columnWidthsCache = null;
|
|
84
|
+
if (!widthNotifyScheduled) {
|
|
85
|
+
widthNotifyScheduled = true;
|
|
86
|
+
queueMicrotask(() => {
|
|
87
|
+
widthNotifyScheduled = false;
|
|
88
|
+
widthVersion.update((value) => value + 1);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function notifyResize() {
|
|
93
|
+
resizeVersion.update((value) => value + 1);
|
|
94
|
+
}
|
|
95
|
+
function normalizeColumnWidth(width) {
|
|
96
|
+
if (typeof width === 'number') {
|
|
97
|
+
return Number.isFinite(width) ? width : undefined;
|
|
98
|
+
}
|
|
99
|
+
if (typeof width === 'string') {
|
|
100
|
+
const match = width.trim().match(/^(\d+(?:\.\d+)?)px$/i);
|
|
101
|
+
if (!match)
|
|
102
|
+
return undefined;
|
|
103
|
+
const next = Number(match[1]);
|
|
104
|
+
return Number.isFinite(next) ? next : undefined;
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
function getColumnRegistrationById(columnId) {
|
|
109
|
+
const token = columnIds.get(columnId);
|
|
110
|
+
return token ? columns.get(token) : undefined;
|
|
111
|
+
}
|
|
112
|
+
function isColumnHidden(columnId) {
|
|
113
|
+
return hiddenColumnIds.has(columnId);
|
|
114
|
+
}
|
|
115
|
+
function getColumnMinWidth(columnId) {
|
|
116
|
+
return getColumnRegistrationById(columnId)?.minWidth;
|
|
117
|
+
}
|
|
118
|
+
function getColumnMaxWidth(columnId) {
|
|
119
|
+
return getColumnRegistrationById(columnId)?.maxWidth;
|
|
120
|
+
}
|
|
121
|
+
function clampColumnWidth(columnId, width) {
|
|
122
|
+
const registration = getColumnRegistrationById(columnId);
|
|
123
|
+
const minWidth = registration?.minWidth ?? 75;
|
|
124
|
+
const maxWidth = registration?.maxWidth;
|
|
125
|
+
let next = Math.round(width);
|
|
126
|
+
if (Number.isNaN(next) || !Number.isFinite(next)) {
|
|
127
|
+
next = minWidth;
|
|
128
|
+
}
|
|
129
|
+
next = Math.max(minWidth, next);
|
|
130
|
+
if (maxWidth !== undefined) {
|
|
131
|
+
next = Math.min(maxWidth, next);
|
|
132
|
+
}
|
|
133
|
+
return next;
|
|
134
|
+
}
|
|
135
|
+
function hasResizableColumns() {
|
|
136
|
+
for (const column of columns.values()) {
|
|
137
|
+
if (isColumnHidden(column.id))
|
|
138
|
+
continue;
|
|
139
|
+
if (column.allowsResizing || columnsWithResizers.has(column.token))
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
function registerColumnResizer(columnToken) {
|
|
145
|
+
if (columnsWithResizers.has(columnToken))
|
|
146
|
+
return;
|
|
147
|
+
columnsWithResizers.add(columnToken);
|
|
148
|
+
notifyLayout();
|
|
149
|
+
}
|
|
150
|
+
function unregisterColumnResizer(columnToken) {
|
|
151
|
+
if (!columnsWithResizers.delete(columnToken))
|
|
152
|
+
return;
|
|
153
|
+
notifyLayout();
|
|
154
|
+
}
|
|
155
|
+
function sameColumnRegistration(left, right) {
|
|
156
|
+
return (left.token === right.token &&
|
|
157
|
+
left.id === right.id &&
|
|
158
|
+
left.allowsSorting === right.allowsSorting &&
|
|
159
|
+
left.allowsResizing === right.allowsResizing &&
|
|
160
|
+
left.isRowHeader === right.isRowHeader &&
|
|
161
|
+
left.textValue === right.textValue &&
|
|
162
|
+
left.width === right.width &&
|
|
163
|
+
left.defaultWidth === right.defaultWidth &&
|
|
164
|
+
left.minWidth === right.minWidth &&
|
|
165
|
+
left.maxWidth === right.maxWidth);
|
|
166
|
+
}
|
|
167
|
+
function sameRowRegistration(left, right) {
|
|
168
|
+
return (left.token === right.token &&
|
|
169
|
+
left.section === right.section &&
|
|
170
|
+
left.id === right.id &&
|
|
171
|
+
left.disabled === right.disabled &&
|
|
172
|
+
left.element === right.element);
|
|
173
|
+
}
|
|
174
|
+
function sameCellRegistration(left, right) {
|
|
175
|
+
return (left.key === right.key &&
|
|
176
|
+
left.rowToken === right.rowToken &&
|
|
177
|
+
left.section === right.section &&
|
|
178
|
+
left.columnIndex === right.columnIndex &&
|
|
179
|
+
left.columnToken === right.columnToken &&
|
|
180
|
+
left.element === right.element &&
|
|
181
|
+
left.focusDelegate === right.focusDelegate);
|
|
182
|
+
}
|
|
183
|
+
function registerColumn(column) {
|
|
184
|
+
const existing = columns.get(column.token);
|
|
185
|
+
const alreadyOrdered = columnOrder.includes(column.token);
|
|
186
|
+
if (existing && sameColumnRegistration(existing, column) && alreadyOrdered)
|
|
187
|
+
return;
|
|
188
|
+
if (existing && existing.id !== column.id && columnIds.get(existing.id) === column.token) {
|
|
189
|
+
columnIds.delete(existing.id);
|
|
190
|
+
}
|
|
191
|
+
columns.set(column.token, column);
|
|
192
|
+
columnIds.set(column.id, column.token);
|
|
193
|
+
if (!alreadyOrdered) {
|
|
194
|
+
columnOrder.push(column.token);
|
|
195
|
+
}
|
|
196
|
+
notifyLayout();
|
|
197
|
+
}
|
|
198
|
+
function unregisterColumn(token) {
|
|
199
|
+
const column = columns.get(token);
|
|
200
|
+
if (column && columnIds.get(column.id) === token) {
|
|
201
|
+
columnIds.delete(column.id);
|
|
202
|
+
}
|
|
203
|
+
columns.delete(token);
|
|
204
|
+
columnsWithResizers.delete(token);
|
|
205
|
+
const index = columnOrder.indexOf(token);
|
|
206
|
+
if (index >= 0) {
|
|
207
|
+
columnOrder.splice(index, 1);
|
|
208
|
+
}
|
|
209
|
+
notifyLayout();
|
|
210
|
+
}
|
|
211
|
+
function getOrderedColumnTokens() {
|
|
212
|
+
if (orderedColumnTokensCache)
|
|
213
|
+
return orderedColumnTokensCache;
|
|
214
|
+
// Pre-build a lookup from columnToken → header cell element to avoid
|
|
215
|
+
// O(columns × cells) scanning inside the comparator.
|
|
216
|
+
const headerElementByToken = new Map();
|
|
217
|
+
for (const cell of cells.values()) {
|
|
218
|
+
if (cell.section === 'header' && cell.columnToken && cell.element) {
|
|
219
|
+
headerElementByToken.set(cell.columnToken, cell.element);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
orderedColumnTokensCache = [...columnOrder].sort((leftToken, rightToken) => {
|
|
223
|
+
const leftCell = headerElementByToken.get(leftToken);
|
|
224
|
+
const rightCell = headerElementByToken.get(rightToken);
|
|
225
|
+
if (!leftCell || !rightCell) {
|
|
226
|
+
return columnOrder.indexOf(leftToken) - columnOrder.indexOf(rightToken);
|
|
227
|
+
}
|
|
228
|
+
if (leftCell === rightCell)
|
|
229
|
+
return 0;
|
|
230
|
+
const position = leftCell.compareDocumentPosition(rightCell);
|
|
231
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
232
|
+
return -1;
|
|
233
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING)
|
|
234
|
+
return 1;
|
|
235
|
+
return columnOrder.indexOf(leftToken) - columnOrder.indexOf(rightToken);
|
|
236
|
+
});
|
|
237
|
+
return orderedColumnTokensCache;
|
|
238
|
+
}
|
|
239
|
+
function getColumnCount() {
|
|
240
|
+
return getOrderedColumnTokens().length;
|
|
241
|
+
}
|
|
242
|
+
function getVisibleOrderedColumnTokens() {
|
|
243
|
+
if (visibleOrderedColumnTokensCache)
|
|
244
|
+
return visibleOrderedColumnTokensCache;
|
|
245
|
+
visibleOrderedColumnTokensCache = getOrderedColumnTokens().filter((token) => {
|
|
246
|
+
const column = columns.get(token);
|
|
247
|
+
return column ? !isColumnHidden(column.id) : false;
|
|
248
|
+
});
|
|
249
|
+
return visibleOrderedColumnTokensCache;
|
|
250
|
+
}
|
|
251
|
+
function getVisibleColumnCount() {
|
|
252
|
+
return getVisibleOrderedColumnTokens().length;
|
|
253
|
+
}
|
|
254
|
+
function getColumnAt(index) {
|
|
255
|
+
const token = getOrderedColumnTokens()[index];
|
|
256
|
+
return token ? columns.get(token) : undefined;
|
|
257
|
+
}
|
|
258
|
+
function getColumnIndexByToken(token) {
|
|
259
|
+
return getOrderedColumnTokens().indexOf(token);
|
|
260
|
+
}
|
|
261
|
+
function getVisibleColumnIndexByToken(token) {
|
|
262
|
+
return getVisibleOrderedColumnTokens().indexOf(token);
|
|
263
|
+
}
|
|
264
|
+
function getColumnTextValue(columnId) {
|
|
265
|
+
return getColumnRegistrationById(columnId)?.textValue;
|
|
266
|
+
}
|
|
267
|
+
function getColumnWidth(columnId) {
|
|
268
|
+
const managedWidth = columnWidths.get(columnId);
|
|
269
|
+
if (managedWidth !== undefined) {
|
|
270
|
+
return clampColumnWidth(columnId, managedWidth);
|
|
271
|
+
}
|
|
272
|
+
const registration = getColumnRegistrationById(columnId);
|
|
273
|
+
if (!registration)
|
|
274
|
+
return undefined;
|
|
275
|
+
const nextWidth = normalizeColumnWidth(registration.width) ?? normalizeColumnWidth(registration.defaultWidth);
|
|
276
|
+
return nextWidth !== undefined ? clampColumnWidth(columnId, nextWidth) : undefined;
|
|
277
|
+
}
|
|
278
|
+
function isColumnResizable(columnId) {
|
|
279
|
+
const column = getColumnRegistrationById(columnId);
|
|
280
|
+
if (!column)
|
|
281
|
+
return false;
|
|
282
|
+
if (isColumnHidden(columnId))
|
|
283
|
+
return false;
|
|
284
|
+
return column.allowsResizing || columnsWithResizers.has(column.token);
|
|
285
|
+
}
|
|
286
|
+
function getColumnWidths() {
|
|
287
|
+
if (columnWidthsCache)
|
|
288
|
+
return columnWidthsCache;
|
|
289
|
+
const widths = new Map();
|
|
290
|
+
for (const token of getOrderedColumnTokens()) {
|
|
291
|
+
const column = columns.get(token);
|
|
292
|
+
if (!column)
|
|
293
|
+
continue;
|
|
294
|
+
const width = getColumnWidth(column.id);
|
|
295
|
+
if (width !== undefined) {
|
|
296
|
+
widths.set(column.id, width);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
columnWidthsCache = widths;
|
|
300
|
+
return widths;
|
|
301
|
+
}
|
|
302
|
+
function getVisibleColumnWidths() {
|
|
303
|
+
const widths = new Map();
|
|
304
|
+
for (const [columnId, width] of getColumnWidths()) {
|
|
305
|
+
if (isColumnHidden(columnId))
|
|
306
|
+
continue;
|
|
307
|
+
widths.set(columnId, width);
|
|
308
|
+
}
|
|
309
|
+
return widths;
|
|
310
|
+
}
|
|
311
|
+
function getMeasuredHeaderWidth(columnToken) {
|
|
312
|
+
const headerCell = Array.from(cells.values()).find((cell) => cell.section === 'header' && cell.columnToken === columnToken && cell.element);
|
|
313
|
+
const width = headerCell?.element?.getBoundingClientRect().width;
|
|
314
|
+
if (width === undefined || width <= 0 || !Number.isFinite(width)) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
return Math.round(width);
|
|
318
|
+
}
|
|
319
|
+
function freezeColumnWidthsFromLayout() {
|
|
320
|
+
const next = new Map();
|
|
321
|
+
let changed = false;
|
|
322
|
+
for (const token of columnOrder) {
|
|
323
|
+
const column = columns.get(token);
|
|
324
|
+
if (!column)
|
|
325
|
+
continue;
|
|
326
|
+
const measuredWidth = getMeasuredHeaderWidth(token);
|
|
327
|
+
const resolvedWidth = measuredWidth ?? getColumnWidth(column.id);
|
|
328
|
+
if (resolvedWidth === undefined)
|
|
329
|
+
continue;
|
|
330
|
+
const clampedWidth = clampColumnWidth(column.id, resolvedWidth);
|
|
331
|
+
next.set(column.id, clampedWidth);
|
|
332
|
+
if (columnWidths.get(column.id) !== clampedWidth) {
|
|
333
|
+
changed = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (!changed || next.size === 0)
|
|
337
|
+
return;
|
|
338
|
+
columnWidths.clear();
|
|
339
|
+
for (const [columnId, width] of next) {
|
|
340
|
+
columnWidths.set(columnId, width);
|
|
341
|
+
}
|
|
342
|
+
columnWidthsCache = null;
|
|
343
|
+
options.onColumnWidthsChange?.(getColumnWidths());
|
|
344
|
+
notifyWidth();
|
|
345
|
+
}
|
|
346
|
+
function setColumnWidths(widths) {
|
|
347
|
+
const next = new Map();
|
|
348
|
+
for (const token of columnOrder) {
|
|
349
|
+
const column = columns.get(token);
|
|
350
|
+
if (!column)
|
|
351
|
+
continue;
|
|
352
|
+
const incomingWidth = widths ? new Map(widths).get(column.id) : undefined;
|
|
353
|
+
if (incomingWidth !== undefined) {
|
|
354
|
+
next.set(column.id, clampColumnWidth(column.id, incomingWidth));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
columnWidths.clear();
|
|
358
|
+
for (const [columnId, width] of next) {
|
|
359
|
+
columnWidths.set(columnId, width);
|
|
360
|
+
}
|
|
361
|
+
notifyWidth();
|
|
362
|
+
}
|
|
363
|
+
function setColumnWidth(columnId, width) {
|
|
364
|
+
if (!isColumnResizable(columnId))
|
|
365
|
+
return;
|
|
366
|
+
const nextWidth = clampColumnWidth(columnId, width);
|
|
367
|
+
if (columnWidths.get(columnId) === nextWidth)
|
|
368
|
+
return;
|
|
369
|
+
columnWidths.set(columnId, nextWidth);
|
|
370
|
+
columnWidthsCache = null;
|
|
371
|
+
options.onColumnWidthsChange?.(getColumnWidths());
|
|
372
|
+
notifyWidth();
|
|
373
|
+
}
|
|
374
|
+
function getCellColumn(cell) {
|
|
375
|
+
if (cell.section === 'header' && cell.columnToken) {
|
|
376
|
+
return columns.get(cell.columnToken);
|
|
377
|
+
}
|
|
378
|
+
return cell.columnIndex !== undefined ? getColumnAt(cell.columnIndex) : undefined;
|
|
379
|
+
}
|
|
380
|
+
function isCellColumnHidden(cell) {
|
|
381
|
+
const column = getCellColumn(cell);
|
|
382
|
+
return column ? isColumnHidden(column.id) : false;
|
|
383
|
+
}
|
|
384
|
+
function getNearestVisibleCellKey(targetCell) {
|
|
385
|
+
const targetPhysicalIndex = targetCell.section === 'header'
|
|
386
|
+
? targetCell.columnToken
|
|
387
|
+
? getColumnIndexByToken(targetCell.columnToken)
|
|
388
|
+
: -1
|
|
389
|
+
: (targetCell.columnIndex ?? -1);
|
|
390
|
+
const siblingCells = Array.from(cells.values())
|
|
391
|
+
.filter((candidate) => {
|
|
392
|
+
if (candidate.key === targetCell.key)
|
|
393
|
+
return false;
|
|
394
|
+
if (candidate.rowToken !== targetCell.rowToken)
|
|
395
|
+
return false;
|
|
396
|
+
if (candidate.section !== targetCell.section)
|
|
397
|
+
return false;
|
|
398
|
+
if (!candidate.element)
|
|
399
|
+
return false;
|
|
400
|
+
if (isCellColumnHidden(candidate))
|
|
401
|
+
return false;
|
|
402
|
+
if (candidate.section === 'body' &&
|
|
403
|
+
isRowDisabled(rows.get(candidate.rowToken)?.id, rows.get(candidate.rowToken)?.disabled)) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
})
|
|
408
|
+
.map((candidate) => {
|
|
409
|
+
const physicalIndex = candidate.section === 'header'
|
|
410
|
+
? candidate.columnToken
|
|
411
|
+
? getColumnIndexByToken(candidate.columnToken)
|
|
412
|
+
: -1
|
|
413
|
+
: (candidate.columnIndex ?? -1);
|
|
414
|
+
const candidateColumn = getCellColumn(candidate);
|
|
415
|
+
return { candidate, physicalIndex, candidateColumn };
|
|
416
|
+
})
|
|
417
|
+
.filter((entry) => entry.physicalIndex >= 0 && entry.candidateColumn);
|
|
418
|
+
if (siblingCells.length === 0)
|
|
419
|
+
return null;
|
|
420
|
+
siblingCells.sort((left, right) => {
|
|
421
|
+
const leftDistance = Math.abs(left.physicalIndex - targetPhysicalIndex);
|
|
422
|
+
const rightDistance = Math.abs(right.physicalIndex - targetPhysicalIndex);
|
|
423
|
+
if (leftDistance !== rightDistance)
|
|
424
|
+
return leftDistance - rightDistance;
|
|
425
|
+
return left.physicalIndex - right.physicalIndex;
|
|
426
|
+
});
|
|
427
|
+
return siblingCells[0]?.candidate.key ?? null;
|
|
428
|
+
}
|
|
429
|
+
function reconcileFocusAfterHiddenColumnsChange() {
|
|
430
|
+
if (resizingColumnId && isColumnHidden(resizingColumnId)) {
|
|
431
|
+
endColumnResize();
|
|
432
|
+
}
|
|
433
|
+
if (!focusedCellKey)
|
|
434
|
+
return;
|
|
435
|
+
const focusedCell = cells.get(focusedCellKey);
|
|
436
|
+
if (!focusedCell || !isCellColumnHidden(focusedCell))
|
|
437
|
+
return;
|
|
438
|
+
const replacementKey = getNearestVisibleCellKey(focusedCell);
|
|
439
|
+
if (replacementKey) {
|
|
440
|
+
focusCellByKey(replacementKey);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
focusedCellKey = null;
|
|
444
|
+
focusedRowTarget = null;
|
|
445
|
+
notifyFocus();
|
|
446
|
+
}
|
|
447
|
+
function setHiddenColumns(columnIds) {
|
|
448
|
+
const next = new Set(columnIds ?? []);
|
|
449
|
+
let changed = next.size !== hiddenColumnIds.size;
|
|
450
|
+
if (!changed) {
|
|
451
|
+
for (const columnId of hiddenColumnIds) {
|
|
452
|
+
if (!next.has(columnId)) {
|
|
453
|
+
changed = true;
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (!changed)
|
|
459
|
+
return;
|
|
460
|
+
hiddenColumnIds.clear();
|
|
461
|
+
for (const columnId of next) {
|
|
462
|
+
hiddenColumnIds.add(columnId);
|
|
463
|
+
}
|
|
464
|
+
reconcileFocusAfterHiddenColumnsChange();
|
|
465
|
+
notifyLayout();
|
|
466
|
+
notifyWidth();
|
|
467
|
+
}
|
|
468
|
+
function measureIntrinsicElementWidth(cell) {
|
|
469
|
+
const target = cell.querySelector('[data-table-header-content]') ??
|
|
470
|
+
cell.firstElementChild ??
|
|
471
|
+
cell;
|
|
472
|
+
const clone = target.cloneNode(true);
|
|
473
|
+
for (const separator of clone.querySelectorAll('[role="separator"]')) {
|
|
474
|
+
separator.remove();
|
|
475
|
+
}
|
|
476
|
+
// Copy the cell's computed font so the clone inherits correct text metrics
|
|
477
|
+
// (the clone is appended to document.body which may have different font styles)
|
|
478
|
+
const cellFont = getComputedStyle(cell);
|
|
479
|
+
clone.style.font = cellFont.font;
|
|
480
|
+
clone.style.letterSpacing = cellFont.letterSpacing;
|
|
481
|
+
clone.style.wordSpacing = cellFont.wordSpacing;
|
|
482
|
+
clone.style.position = 'absolute';
|
|
483
|
+
clone.style.visibility = 'hidden';
|
|
484
|
+
clone.style.pointerEvents = 'none';
|
|
485
|
+
clone.style.left = '-99999px';
|
|
486
|
+
clone.style.top = '0';
|
|
487
|
+
clone.style.width = 'max-content';
|
|
488
|
+
clone.style.maxWidth = 'none';
|
|
489
|
+
clone.style.minWidth = '0';
|
|
490
|
+
clone.style.overflow = 'visible';
|
|
491
|
+
clone.style.whiteSpace = 'nowrap';
|
|
492
|
+
document.body.appendChild(clone);
|
|
493
|
+
const width = Math.ceil(clone.getBoundingClientRect().width);
|
|
494
|
+
clone.remove();
|
|
495
|
+
return width;
|
|
496
|
+
}
|
|
497
|
+
function measureColumnContentWidth(columnId) {
|
|
498
|
+
const registration = getColumnRegistrationById(columnId);
|
|
499
|
+
if (!registration)
|
|
500
|
+
return undefined;
|
|
501
|
+
const columnIndex = getColumnIndexByToken(registration.token);
|
|
502
|
+
if (columnIndex < 0)
|
|
503
|
+
return undefined;
|
|
504
|
+
const measuredWidths = [];
|
|
505
|
+
for (const cell of cells.values()) {
|
|
506
|
+
const matchesHeader = cell.section === 'header' && cell.columnToken === registration.token && cell.element;
|
|
507
|
+
const matchesBody = cell.section === 'body' && cell.columnIndex === columnIndex && cell.element;
|
|
508
|
+
if (!matchesHeader && !matchesBody)
|
|
509
|
+
continue;
|
|
510
|
+
const element = cell.element;
|
|
511
|
+
if (!element)
|
|
512
|
+
continue;
|
|
513
|
+
const computedStyle = getComputedStyle(element);
|
|
514
|
+
const paddingX = parseFloat(computedStyle.paddingLeft || '0') +
|
|
515
|
+
parseFloat(computedStyle.paddingRight || '0');
|
|
516
|
+
const borderX = parseFloat(computedStyle.borderLeftWidth || '0') +
|
|
517
|
+
parseFloat(computedStyle.borderRightWidth || '0');
|
|
518
|
+
const contentWidth = measureIntrinsicElementWidth(element);
|
|
519
|
+
if (contentWidth <= 0)
|
|
520
|
+
continue;
|
|
521
|
+
measuredWidths.push(Math.ceil(contentWidth + paddingX + borderX));
|
|
522
|
+
}
|
|
523
|
+
if (measuredWidths.length === 0)
|
|
524
|
+
return undefined;
|
|
525
|
+
return clampColumnWidth(columnId, Math.max(...measuredWidths));
|
|
526
|
+
}
|
|
527
|
+
function startColumnResize(columnId) {
|
|
528
|
+
if (!isColumnResizable(columnId) || resizingColumnId === columnId)
|
|
529
|
+
return;
|
|
530
|
+
if (getVisibleColumnWidths().size < getVisibleColumnCount()) {
|
|
531
|
+
freezeColumnWidthsFromLayout();
|
|
532
|
+
}
|
|
533
|
+
resizingColumnId = columnId;
|
|
534
|
+
options.onColumnResizeStart?.(columnId);
|
|
535
|
+
notifyResize();
|
|
536
|
+
}
|
|
537
|
+
function endColumnResize() {
|
|
538
|
+
if (!resizingColumnId)
|
|
539
|
+
return;
|
|
540
|
+
resizingColumnId = null;
|
|
541
|
+
options.onColumnResizeEnd?.(getColumnWidths());
|
|
542
|
+
notifyResize();
|
|
543
|
+
}
|
|
544
|
+
function suppressHeaderClickOnce() {
|
|
545
|
+
suppressNextHeaderClick = true;
|
|
546
|
+
}
|
|
547
|
+
function consumeHeaderClickSuppression() {
|
|
548
|
+
const shouldSuppress = suppressNextHeaderClick;
|
|
549
|
+
suppressNextHeaderClick = false;
|
|
550
|
+
return shouldSuppress;
|
|
551
|
+
}
|
|
552
|
+
function registerRow(row) {
|
|
553
|
+
const existing = rows.get(row.token);
|
|
554
|
+
const targetOrder = row.section === 'header' ? headerRowOrder : row.section === 'body' ? bodyRowOrder : null;
|
|
555
|
+
const alreadyOrdered = targetOrder ? targetOrder.includes(row.token) : false;
|
|
556
|
+
const wasInHeader = headerRowOrder.includes(row.token);
|
|
557
|
+
const wasInBody = bodyRowOrder.includes(row.token);
|
|
558
|
+
if (existing &&
|
|
559
|
+
sameRowRegistration(existing, row) &&
|
|
560
|
+
(targetOrder ? alreadyOrdered : !wasInHeader && !wasInBody)) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (wasInHeader) {
|
|
564
|
+
headerRowOrder.splice(headerRowOrder.indexOf(row.token), 1);
|
|
565
|
+
}
|
|
566
|
+
if (wasInBody) {
|
|
567
|
+
bodyRowOrder.splice(bodyRowOrder.indexOf(row.token), 1);
|
|
568
|
+
}
|
|
569
|
+
rows.set(row.token, row);
|
|
570
|
+
if (targetOrder && !targetOrder.includes(row.token)) {
|
|
571
|
+
targetOrder.push(row.token);
|
|
572
|
+
}
|
|
573
|
+
notifyLayout();
|
|
574
|
+
}
|
|
575
|
+
function unregisterRow(token) {
|
|
576
|
+
const row = rows.get(token);
|
|
577
|
+
rows.delete(token);
|
|
578
|
+
if (focusedRowTarget?.rowToken === token) {
|
|
579
|
+
focusedRowTarget = null;
|
|
580
|
+
notifyFocus();
|
|
581
|
+
}
|
|
582
|
+
for (const order of [headerRowOrder, bodyRowOrder]) {
|
|
583
|
+
const index = order.indexOf(token);
|
|
584
|
+
if (index >= 0) {
|
|
585
|
+
order.splice(index, 1);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
for (const [key, cell] of cells.entries()) {
|
|
589
|
+
if (cell.rowToken === token) {
|
|
590
|
+
cells.delete(key);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (row && focusedCellKey) {
|
|
594
|
+
const focusedCell = cells.get(focusedCellKey);
|
|
595
|
+
if (!focusedCell || focusedCell.rowToken === token) {
|
|
596
|
+
focusedCellKey = null;
|
|
597
|
+
notifyFocus();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
notifyLayout();
|
|
601
|
+
}
|
|
602
|
+
function getBodyRowCount() {
|
|
603
|
+
return getOrderedRowTokens('body').length;
|
|
604
|
+
}
|
|
605
|
+
function getHeaderRowCount() {
|
|
606
|
+
return getOrderedRowTokens('header').length;
|
|
607
|
+
}
|
|
608
|
+
function compareRowsByDocumentOrder(leftToken, rightToken, fallback) {
|
|
609
|
+
const leftIndex = fallback.indexOf(leftToken);
|
|
610
|
+
const rightIndex = fallback.indexOf(rightToken);
|
|
611
|
+
const leftRow = rows.get(leftToken)?.element;
|
|
612
|
+
const rightRow = rows.get(rightToken)?.element;
|
|
613
|
+
if (!leftRow || !rightRow || leftRow === rightRow) {
|
|
614
|
+
return leftIndex - rightIndex;
|
|
615
|
+
}
|
|
616
|
+
const position = leftRow.compareDocumentPosition(rightRow);
|
|
617
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
618
|
+
return -1;
|
|
619
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING)
|
|
620
|
+
return 1;
|
|
621
|
+
return leftIndex - rightIndex;
|
|
622
|
+
}
|
|
623
|
+
function getOrderedRowTokens(section) {
|
|
624
|
+
const cached = orderedRowTokensCache[section];
|
|
625
|
+
if (cached)
|
|
626
|
+
return cached;
|
|
627
|
+
const fallback = section === 'header' ? headerRowOrder : bodyRowOrder;
|
|
628
|
+
const sorted = [...fallback].sort((leftToken, rightToken) => compareRowsByDocumentOrder(leftToken, rightToken, fallback));
|
|
629
|
+
orderedRowTokensCache[section] = sorted;
|
|
630
|
+
return sorted;
|
|
631
|
+
}
|
|
632
|
+
function getOrderedSelectableRowIds() {
|
|
633
|
+
const rowIds = [];
|
|
634
|
+
for (const token of getOrderedRowTokens('body')) {
|
|
635
|
+
const row = rows.get(token);
|
|
636
|
+
if (!row?.id)
|
|
637
|
+
continue;
|
|
638
|
+
if (disabledKeys.has(row.id) || row.disabled)
|
|
639
|
+
continue;
|
|
640
|
+
rowIds.push(row.id);
|
|
641
|
+
}
|
|
642
|
+
return rowIds;
|
|
643
|
+
}
|
|
644
|
+
function isRowDisabled(id, localDisabled = false) {
|
|
645
|
+
if (localDisabled)
|
|
646
|
+
return true;
|
|
647
|
+
if (id === undefined)
|
|
648
|
+
return false;
|
|
649
|
+
return disabledKeys.has(id);
|
|
650
|
+
}
|
|
651
|
+
function isRowSelected(id) {
|
|
652
|
+
if (id === undefined)
|
|
653
|
+
return false;
|
|
654
|
+
return selectedKeys.has(id);
|
|
655
|
+
}
|
|
656
|
+
function getSelectionCheckboxState() {
|
|
657
|
+
const orderedIds = getOrderedSelectableRowIds();
|
|
658
|
+
if (orderedIds.length === 0) {
|
|
659
|
+
return selectedKeys.size > 0 ? 'some' : 'none';
|
|
660
|
+
}
|
|
661
|
+
if (selectedKeys.size === 0) {
|
|
662
|
+
return 'none';
|
|
663
|
+
}
|
|
664
|
+
let selectedCount = 0;
|
|
665
|
+
for (const id of orderedIds) {
|
|
666
|
+
if (selectedKeys.has(id)) {
|
|
667
|
+
selectedCount += 1;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (selectedCount === 0)
|
|
671
|
+
return 'none';
|
|
672
|
+
if (selectedCount === orderedIds.length)
|
|
673
|
+
return 'all';
|
|
674
|
+
return 'some';
|
|
675
|
+
}
|
|
676
|
+
function hasSelectableRows() {
|
|
677
|
+
return getOrderedSelectableRowIds().length > 0 || selectedKeys.size > 0;
|
|
678
|
+
}
|
|
679
|
+
function isRowFocused(token) {
|
|
680
|
+
if (focusedRowTarget?.rowToken === token)
|
|
681
|
+
return true;
|
|
682
|
+
if (!focusedCellKey)
|
|
683
|
+
return false;
|
|
684
|
+
return cells.get(focusedCellKey)?.rowToken === token;
|
|
685
|
+
}
|
|
686
|
+
function isRowFocusTarget(token) {
|
|
687
|
+
return focusedRowTarget?.rowToken === token;
|
|
688
|
+
}
|
|
689
|
+
function getRowFocusEdge(token) {
|
|
690
|
+
return focusedRowTarget?.rowToken === token ? focusedRowTarget.edge : null;
|
|
691
|
+
}
|
|
692
|
+
function registerCell(cell) {
|
|
693
|
+
const existing = cells.get(cell.key);
|
|
694
|
+
const alreadyOrdered = cellOrder.includes(cell.key);
|
|
695
|
+
if (existing && sameCellRegistration(existing, cell) && alreadyOrdered)
|
|
696
|
+
return;
|
|
697
|
+
cells.set(cell.key, cell);
|
|
698
|
+
if (!alreadyOrdered) {
|
|
699
|
+
cellOrder.push(cell.key);
|
|
700
|
+
}
|
|
701
|
+
notifyLayout();
|
|
702
|
+
}
|
|
703
|
+
function unregisterCell(key) {
|
|
704
|
+
cells.delete(key);
|
|
705
|
+
const index = cellOrder.indexOf(key);
|
|
706
|
+
if (index >= 0) {
|
|
707
|
+
cellOrder.splice(index, 1);
|
|
708
|
+
}
|
|
709
|
+
if (focusedCellKey === key) {
|
|
710
|
+
focusedCellKey = null;
|
|
711
|
+
notifyFocus();
|
|
712
|
+
}
|
|
713
|
+
notifyLayout();
|
|
714
|
+
}
|
|
715
|
+
function isCellFocused(key) {
|
|
716
|
+
return focusedCellKey === key;
|
|
717
|
+
}
|
|
718
|
+
function getDefaultFocusKey() {
|
|
719
|
+
for (const rowToken of getOrderedRowTokens('header')) {
|
|
720
|
+
const headerCells = Array.from(cells.values())
|
|
721
|
+
.filter((cell) => cell.section === 'header' && cell.rowToken === rowToken)
|
|
722
|
+
.sort((left, right) => getColumnIndex(left) - getColumnIndex(right));
|
|
723
|
+
const firstHeaderCell = headerCells[0]?.key;
|
|
724
|
+
if (firstHeaderCell)
|
|
725
|
+
return firstHeaderCell;
|
|
726
|
+
}
|
|
727
|
+
for (const rowToken of getOrderedRowTokens('body')) {
|
|
728
|
+
const row = rows.get(rowToken);
|
|
729
|
+
if (isRowDisabled(row?.id, row?.disabled))
|
|
730
|
+
continue;
|
|
731
|
+
const bodyCells = Array.from(cells.values())
|
|
732
|
+
.filter((cell) => cell.section === 'body' && cell.rowToken === rowToken)
|
|
733
|
+
.sort((left, right) => (left.columnIndex ?? -1) - (right.columnIndex ?? -1));
|
|
734
|
+
const firstBodyCell = bodyCells[0]?.key;
|
|
735
|
+
if (firstBodyCell)
|
|
736
|
+
return firstBodyCell;
|
|
737
|
+
}
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
function isCellTabStop(key) {
|
|
741
|
+
if (focusedCellKey) {
|
|
742
|
+
return focusedCellKey === key;
|
|
743
|
+
}
|
|
744
|
+
if (focusedRowTarget) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
return getDefaultFocusKey() === key;
|
|
748
|
+
}
|
|
749
|
+
function isRowTabStop(token) {
|
|
750
|
+
return focusedRowTarget?.rowToken === token;
|
|
751
|
+
}
|
|
752
|
+
function getGlobalRowIndex(rowToken) {
|
|
753
|
+
const orderedHeaderRows = getOrderedRowTokens('header');
|
|
754
|
+
const orderedBodyRows = getOrderedRowTokens('body');
|
|
755
|
+
const headerIndex = orderedHeaderRows.indexOf(rowToken);
|
|
756
|
+
if (headerIndex >= 0)
|
|
757
|
+
return headerIndex;
|
|
758
|
+
const bodyIndex = orderedBodyRows.indexOf(rowToken);
|
|
759
|
+
if (bodyIndex >= 0)
|
|
760
|
+
return orderedHeaderRows.length + bodyIndex;
|
|
761
|
+
return -1;
|
|
762
|
+
}
|
|
763
|
+
function getColumnIndex(cell) {
|
|
764
|
+
if (cell.section === 'header' && cell.columnToken) {
|
|
765
|
+
return getVisibleColumnIndexByToken(cell.columnToken);
|
|
766
|
+
}
|
|
767
|
+
const column = cell.columnIndex !== undefined ? getColumnAt(cell.columnIndex) : undefined;
|
|
768
|
+
if (!column || isColumnHidden(column.id))
|
|
769
|
+
return -1;
|
|
770
|
+
return getVisibleColumnIndexByToken(column.token);
|
|
771
|
+
}
|
|
772
|
+
function getCellCoord(cell) {
|
|
773
|
+
const row = getGlobalRowIndex(cell.rowToken);
|
|
774
|
+
const col = getColumnIndex(cell);
|
|
775
|
+
if (row < 0 || col < 0)
|
|
776
|
+
return null;
|
|
777
|
+
return { row, col };
|
|
778
|
+
}
|
|
779
|
+
function getNavigableCells() {
|
|
780
|
+
if (navigableCellsCache)
|
|
781
|
+
return navigableCellsCache;
|
|
782
|
+
navigableCellsCache = Array.from(cells.values())
|
|
783
|
+
.map((cell) => ({ cell, coord: getCellCoord(cell) }))
|
|
784
|
+
.filter((entry) => Boolean(entry.coord &&
|
|
785
|
+
!isCellColumnHidden(entry.cell) &&
|
|
786
|
+
entry.cell.element &&
|
|
787
|
+
(entry.cell.section !== 'body' ||
|
|
788
|
+
!isRowDisabled(rows.get(entry.cell.rowToken)?.id, rows.get(entry.cell.rowToken)?.disabled))))
|
|
789
|
+
.sort((left, right) => left.coord.row === right.coord.row
|
|
790
|
+
? left.coord.col - right.coord.col
|
|
791
|
+
: left.coord.row - right.coord.row);
|
|
792
|
+
return navigableCellsCache;
|
|
793
|
+
}
|
|
794
|
+
function getRowsWithCells() {
|
|
795
|
+
if (rowsWithCellsCache)
|
|
796
|
+
return rowsWithCellsCache;
|
|
797
|
+
const rowsByIndex = new Map();
|
|
798
|
+
for (const { cell, coord } of getNavigableCells()) {
|
|
799
|
+
const rowCells = rowsByIndex.get(coord.row) ?? [];
|
|
800
|
+
rowCells.push({ col: coord.col, key: cell.key, element: cell.element });
|
|
801
|
+
rowsByIndex.set(coord.row, rowCells);
|
|
802
|
+
}
|
|
803
|
+
for (const rowCells of rowsByIndex.values()) {
|
|
804
|
+
rowCells.sort((a, b) => a.col - b.col);
|
|
805
|
+
}
|
|
806
|
+
rowsWithCellsCache = rowsByIndex;
|
|
807
|
+
return rowsWithCellsCache;
|
|
808
|
+
}
|
|
809
|
+
function getClosestCellKey(rowIndex, preferredCol) {
|
|
810
|
+
const rowCells = getRowsWithCells().get(rowIndex);
|
|
811
|
+
if (!rowCells || rowCells.length === 0)
|
|
812
|
+
return null;
|
|
813
|
+
const exact = rowCells.find((rowCell) => rowCell.col === preferredCol);
|
|
814
|
+
if (exact)
|
|
815
|
+
return exact.key;
|
|
816
|
+
const before = [...rowCells].reverse().find((rowCell) => rowCell.col <= preferredCol);
|
|
817
|
+
if (before)
|
|
818
|
+
return before.key;
|
|
819
|
+
return rowCells[0]?.key ?? null;
|
|
820
|
+
}
|
|
821
|
+
function focusCellByKey(key) {
|
|
822
|
+
if (!key)
|
|
823
|
+
return;
|
|
824
|
+
const cell = cells.get(key);
|
|
825
|
+
if (!cell?.element)
|
|
826
|
+
return;
|
|
827
|
+
if (cell.section === 'body' &&
|
|
828
|
+
isRowDisabled(rows.get(cell.rowToken)?.id, rows.get(cell.rowToken)?.disabled)) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
focusedRowTarget = null;
|
|
832
|
+
focusedCellKey = key;
|
|
833
|
+
notifyFocus();
|
|
834
|
+
const focusTarget = cell.focusDelegate?.() ?? cell.element;
|
|
835
|
+
focusTarget.focus();
|
|
836
|
+
focusTarget.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });
|
|
837
|
+
}
|
|
838
|
+
function focusRowByToken(token, edge) {
|
|
839
|
+
const row = rows.get(token);
|
|
840
|
+
if (!row?.element || row.section !== 'body')
|
|
841
|
+
return;
|
|
842
|
+
if (isRowDisabled(row.id, row.disabled))
|
|
843
|
+
return;
|
|
844
|
+
focusedCellKey = null;
|
|
845
|
+
focusedRowTarget = { rowToken: token, edge };
|
|
846
|
+
notifyFocus();
|
|
847
|
+
row.element.focus();
|
|
848
|
+
row.element.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });
|
|
849
|
+
}
|
|
850
|
+
function setFocusedCell(key) {
|
|
851
|
+
if (focusedCellKey === key && focusedRowTarget === null)
|
|
852
|
+
return;
|
|
853
|
+
focusedCellKey = key;
|
|
854
|
+
focusedRowTarget = null;
|
|
855
|
+
notifyFocus();
|
|
856
|
+
}
|
|
857
|
+
function setFocusedRow(token, edge = 'start') {
|
|
858
|
+
if (token === null) {
|
|
859
|
+
if (focusedCellKey === null && focusedRowTarget === null)
|
|
860
|
+
return;
|
|
861
|
+
focusedCellKey = null;
|
|
862
|
+
focusedRowTarget = null;
|
|
863
|
+
notifyFocus();
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (focusedCellKey === null &&
|
|
867
|
+
focusedRowTarget?.rowToken === token &&
|
|
868
|
+
focusedRowTarget.edge === edge) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
focusedCellKey = null;
|
|
872
|
+
focusedRowTarget = { rowToken: token, edge };
|
|
873
|
+
notifyFocus();
|
|
874
|
+
}
|
|
875
|
+
function setFocusVisible(visible) {
|
|
876
|
+
if (focusVisible === visible)
|
|
877
|
+
return;
|
|
878
|
+
focusVisible = visible;
|
|
879
|
+
notifyFocus();
|
|
880
|
+
}
|
|
881
|
+
function getFocusedCoord() {
|
|
882
|
+
if (!focusedCellKey)
|
|
883
|
+
return null;
|
|
884
|
+
const cell = cells.get(focusedCellKey);
|
|
885
|
+
return cell ? getCellCoord(cell) : null;
|
|
886
|
+
}
|
|
887
|
+
function getFocusedRowId() {
|
|
888
|
+
const rowToken = focusedRowTarget?.rowToken ?? cells.get(focusedCellKey ?? '')?.rowToken;
|
|
889
|
+
if (!rowToken)
|
|
890
|
+
return null;
|
|
891
|
+
const row = rows.get(rowToken);
|
|
892
|
+
return row?.section === 'body' ? (row.id ?? null) : null;
|
|
893
|
+
}
|
|
894
|
+
function getRowTokenByGlobalIndex(index) {
|
|
895
|
+
const headerRows = getOrderedRowTokens('header');
|
|
896
|
+
if (index < headerRows.length) {
|
|
897
|
+
return headerRows[index] ?? null;
|
|
898
|
+
}
|
|
899
|
+
return getOrderedRowTokens('body')[index - headerRows.length] ?? null;
|
|
900
|
+
}
|
|
901
|
+
function getFocusableBodyRowToken(direction) {
|
|
902
|
+
const orderedBodyTokens = getOrderedRowTokens('body');
|
|
903
|
+
const tokens = direction === 'start' ? orderedBodyTokens : [...orderedBodyTokens].reverse();
|
|
904
|
+
for (const token of tokens) {
|
|
905
|
+
const row = rows.get(token);
|
|
906
|
+
if (!row || isRowDisabled(row.id, row.disabled))
|
|
907
|
+
continue;
|
|
908
|
+
return token;
|
|
909
|
+
}
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
function hasSameSelection(left, right) {
|
|
913
|
+
if (left.size !== right.size)
|
|
914
|
+
return false;
|
|
915
|
+
for (const key of left) {
|
|
916
|
+
if (!right.has(key))
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
return true;
|
|
920
|
+
}
|
|
921
|
+
function setSelectedKeys(next, anchor) {
|
|
922
|
+
selectedKeys =
|
|
923
|
+
selectionMode === 'none'
|
|
924
|
+
? new Set()
|
|
925
|
+
: selectionMode === 'single' && next.size > 1
|
|
926
|
+
? new Set([next.values().next().value])
|
|
927
|
+
: next;
|
|
928
|
+
const fallbackAnchor = selectedKeys.values().next().value ?? null;
|
|
929
|
+
if (anchor === undefined) {
|
|
930
|
+
selectionAnchorKey = fallbackAnchor;
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
selectionAnchorKey = anchor === null || selectedKeys.has(anchor) ? anchor : fallbackAnchor;
|
|
934
|
+
}
|
|
935
|
+
function replaceSelectionWithRow(id) {
|
|
936
|
+
if (id === undefined || disabledKeys.has(id))
|
|
937
|
+
return;
|
|
938
|
+
setSelectedKeys(new Set([id]), id);
|
|
939
|
+
emitSelectionChange();
|
|
940
|
+
}
|
|
941
|
+
function toggleSelectionForRow(id) {
|
|
942
|
+
if (id === undefined || disabledKeys.has(id))
|
|
943
|
+
return;
|
|
944
|
+
if (selectionMode === 'single') {
|
|
945
|
+
const wasSelected = selectedKeys.has(id);
|
|
946
|
+
setSelectedKeys(selectionBehavior === 'toggle' && wasSelected ? new Set() : new Set([id]), selectionBehavior === 'toggle' && wasSelected ? null : id);
|
|
947
|
+
emitSelectionChange();
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const next = new Set(selectedKeys);
|
|
951
|
+
if (next.has(id)) {
|
|
952
|
+
next.delete(id);
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
next.add(id);
|
|
956
|
+
}
|
|
957
|
+
setSelectedKeys(next, id);
|
|
958
|
+
emitSelectionChange();
|
|
959
|
+
}
|
|
960
|
+
function extendSelectionToRow(id, anchorOverride) {
|
|
961
|
+
if (id === undefined || disabledKeys.has(id))
|
|
962
|
+
return;
|
|
963
|
+
if (selectionMode !== 'multiple') {
|
|
964
|
+
replaceSelectionWithRow(id);
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
const orderedIds = getOrderedSelectableRowIds();
|
|
968
|
+
const targetIndex = orderedIds.indexOf(id);
|
|
969
|
+
if (targetIndex < 0)
|
|
970
|
+
return;
|
|
971
|
+
const anchor = anchorOverride ??
|
|
972
|
+
(selectionAnchorKey && orderedIds.includes(selectionAnchorKey) ? selectionAnchorKey : null) ??
|
|
973
|
+
getFocusedRowId() ??
|
|
974
|
+
id;
|
|
975
|
+
const anchorIndex = orderedIds.indexOf(anchor);
|
|
976
|
+
if (anchorIndex < 0) {
|
|
977
|
+
replaceSelectionWithRow(id);
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const start = Math.min(anchorIndex, targetIndex);
|
|
981
|
+
const end = Math.max(anchorIndex, targetIndex);
|
|
982
|
+
setSelectedKeys(new Set(orderedIds.slice(start, end + 1)), anchor);
|
|
983
|
+
emitSelectionChange();
|
|
984
|
+
}
|
|
985
|
+
function pressRow(id, interaction = {}) {
|
|
986
|
+
if (selectionMode === 'none' || id === undefined || disabledKeys.has(id))
|
|
987
|
+
return;
|
|
988
|
+
if (selectionBehavior === 'replace' && selectionMode === 'multiple') {
|
|
989
|
+
if (interaction.shiftKey) {
|
|
990
|
+
extendSelectionToRow(id);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (interaction.ctrlKey || interaction.metaKey) {
|
|
994
|
+
toggleSelectionForRow(id);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
replaceSelectionWithRow(id);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
toggleSelectionForRow(id);
|
|
1001
|
+
}
|
|
1002
|
+
function moveFocus(direction, interaction = {}) {
|
|
1003
|
+
const rowMap = getRowsWithCells();
|
|
1004
|
+
const currentCoord = getFocusedCoord();
|
|
1005
|
+
const rowIndexes = Array.from(rowMap.keys()).sort((a, b) => a - b);
|
|
1006
|
+
if (rowIndexes.length === 0)
|
|
1007
|
+
return;
|
|
1008
|
+
const previousFocusedRowId = getFocusedRowId();
|
|
1009
|
+
function maybeSyncSelectionAfterFocus() {
|
|
1010
|
+
if (direction !== 'up' && direction !== 'down')
|
|
1011
|
+
return;
|
|
1012
|
+
if (selectionMode === 'none')
|
|
1013
|
+
return;
|
|
1014
|
+
if (interaction.ctrlKey || interaction.metaKey || interaction.altKey)
|
|
1015
|
+
return;
|
|
1016
|
+
const targetRowId = getFocusedRowId();
|
|
1017
|
+
if (targetRowId === null)
|
|
1018
|
+
return;
|
|
1019
|
+
if (interaction.shiftKey) {
|
|
1020
|
+
extendSelectionToRow(targetRowId, selectionAnchorKey ?? previousFocusedRowId);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
if (selectionBehavior !== 'replace')
|
|
1024
|
+
return;
|
|
1025
|
+
replaceSelectionWithRow(targetRowId);
|
|
1026
|
+
}
|
|
1027
|
+
if (!currentCoord) {
|
|
1028
|
+
if (focusedRowTarget) {
|
|
1029
|
+
const currentRowIndex = getGlobalRowIndex(focusedRowTarget.rowToken);
|
|
1030
|
+
if (currentRowIndex < 0)
|
|
1031
|
+
return;
|
|
1032
|
+
if (direction === 'left' || direction === 'right') {
|
|
1033
|
+
const rowCells = rowMap.get(currentRowIndex) ?? [];
|
|
1034
|
+
if (focusedRowTarget.edge === 'start') {
|
|
1035
|
+
if (direction === 'right') {
|
|
1036
|
+
focusCellByKey(getClosestCellKey(currentRowIndex, -1));
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
focusCellByKey(rowCells[rowCells.length - 1]?.key ?? null);
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (direction === 'left') {
|
|
1043
|
+
focusCellByKey(rowCells[rowCells.length - 1]?.key ?? null);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
focusCellByKey(getClosestCellKey(currentRowIndex, -1));
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const rowPosition = rowIndexes.indexOf(currentRowIndex);
|
|
1050
|
+
const targetRowIndex = direction === 'up' ? rowIndexes[rowPosition - 1] : rowIndexes[rowPosition + 1];
|
|
1051
|
+
if (targetRowIndex === undefined)
|
|
1052
|
+
return;
|
|
1053
|
+
const targetRowToken = getRowTokenByGlobalIndex(targetRowIndex);
|
|
1054
|
+
const targetRow = targetRowToken ? rows.get(targetRowToken) : null;
|
|
1055
|
+
if (targetRow?.section === 'body' && !isRowDisabled(targetRow.id, targetRow.disabled)) {
|
|
1056
|
+
focusRowByToken(targetRowToken, focusedRowTarget.edge);
|
|
1057
|
+
}
|
|
1058
|
+
else if (focusedRowTarget.edge === 'start') {
|
|
1059
|
+
focusCellByKey(getClosestCellKey(targetRowIndex, -1));
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
const targetRowCells = rowMap.get(targetRowIndex) ?? [];
|
|
1063
|
+
focusCellByKey(targetRowCells[targetRowCells.length - 1]?.key ?? null);
|
|
1064
|
+
}
|
|
1065
|
+
maybeSyncSelectionAfterFocus();
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
const firstKey = getClosestCellKey(rowIndexes[0], 0);
|
|
1069
|
+
focusCellByKey(firstKey);
|
|
1070
|
+
maybeSyncSelectionAfterFocus();
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
if (direction === 'left' || direction === 'right') {
|
|
1074
|
+
const rowCells = rowMap.get(currentCoord.row) ?? [];
|
|
1075
|
+
const currentIndex = rowCells.findIndex((rowCell) => rowCell.key === focusedCellKey);
|
|
1076
|
+
if (currentIndex < 0)
|
|
1077
|
+
return;
|
|
1078
|
+
const nextIndex = direction === 'left' ? currentIndex - 1 : currentIndex + 1;
|
|
1079
|
+
const nextKey = rowCells[nextIndex]?.key;
|
|
1080
|
+
if (nextKey) {
|
|
1081
|
+
focusCellByKey(nextKey);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
const currentCell = cells.get(focusedCellKey ?? '');
|
|
1085
|
+
if (currentCell?.section === 'body') {
|
|
1086
|
+
focusRowByToken(currentCell.rowToken, direction === 'left' ? 'start' : 'end');
|
|
1087
|
+
}
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const rowPosition = rowIndexes.indexOf(currentCoord.row);
|
|
1091
|
+
const targetRowIndex = direction === 'up' ? rowIndexes[rowPosition - 1] : rowIndexes[rowPosition + 1];
|
|
1092
|
+
if (targetRowIndex === undefined)
|
|
1093
|
+
return;
|
|
1094
|
+
const targetKey = getClosestCellKey(targetRowIndex, currentCoord.col);
|
|
1095
|
+
focusCellByKey(targetKey);
|
|
1096
|
+
maybeSyncSelectionAfterFocus();
|
|
1097
|
+
}
|
|
1098
|
+
function moveToRowStart() {
|
|
1099
|
+
if (focusedRowTarget) {
|
|
1100
|
+
const rowIndex = getGlobalRowIndex(focusedRowTarget.rowToken);
|
|
1101
|
+
if (rowIndex < 0)
|
|
1102
|
+
return;
|
|
1103
|
+
focusCellByKey(getClosestCellKey(rowIndex, -1));
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
const currentCoord = getFocusedCoord();
|
|
1107
|
+
if (!currentCoord) {
|
|
1108
|
+
moveToGridStart();
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
focusCellByKey(getClosestCellKey(currentCoord.row, -1));
|
|
1112
|
+
}
|
|
1113
|
+
function moveToRowEnd() {
|
|
1114
|
+
const rowMap = getRowsWithCells();
|
|
1115
|
+
if (focusedRowTarget) {
|
|
1116
|
+
const rowIndex = getGlobalRowIndex(focusedRowTarget.rowToken);
|
|
1117
|
+
if (rowIndex < 0)
|
|
1118
|
+
return;
|
|
1119
|
+
const rowCells = rowMap.get(rowIndex) ?? [];
|
|
1120
|
+
focusCellByKey(rowCells[rowCells.length - 1]?.key ?? null);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const currentCoord = getFocusedCoord();
|
|
1124
|
+
if (!currentCoord) {
|
|
1125
|
+
moveToGridEnd();
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
const rowCells = rowMap.get(currentCoord.row) ?? [];
|
|
1129
|
+
focusCellByKey(rowCells[rowCells.length - 1]?.key ?? null);
|
|
1130
|
+
}
|
|
1131
|
+
function moveToGridStart() {
|
|
1132
|
+
const rowIndexes = Array.from(getRowsWithCells().keys()).sort((a, b) => a - b);
|
|
1133
|
+
if (rowIndexes.length === 0)
|
|
1134
|
+
return;
|
|
1135
|
+
focusCellByKey(getClosestCellKey(rowIndexes[0], -1));
|
|
1136
|
+
}
|
|
1137
|
+
function moveToBodyRowStart() {
|
|
1138
|
+
const targetToken = getFocusableBodyRowToken('start');
|
|
1139
|
+
if (!targetToken)
|
|
1140
|
+
return;
|
|
1141
|
+
focusRowByToken(targetToken, focusedRowTarget?.edge ?? 'start');
|
|
1142
|
+
}
|
|
1143
|
+
function moveToBodyRowEnd() {
|
|
1144
|
+
const targetToken = getFocusableBodyRowToken('end');
|
|
1145
|
+
if (!targetToken)
|
|
1146
|
+
return;
|
|
1147
|
+
focusRowByToken(targetToken, focusedRowTarget?.edge ?? 'end');
|
|
1148
|
+
}
|
|
1149
|
+
function moveToGridEnd() {
|
|
1150
|
+
const rowMap = getRowsWithCells();
|
|
1151
|
+
const bodyGlobalRows = getOrderedRowTokens('body')
|
|
1152
|
+
.map((token) => getGlobalRowIndex(token))
|
|
1153
|
+
.filter((index) => index >= 0)
|
|
1154
|
+
.sort((a, b) => a - b);
|
|
1155
|
+
const targetRow = bodyGlobalRows.at(-1) ??
|
|
1156
|
+
Array.from(rowMap.keys())
|
|
1157
|
+
.sort((a, b) => a - b)
|
|
1158
|
+
.at(-1);
|
|
1159
|
+
if (targetRow === undefined)
|
|
1160
|
+
return;
|
|
1161
|
+
const rowCells = rowMap.get(targetRow) ?? [];
|
|
1162
|
+
focusCellByKey(rowCells[rowCells.length - 1]?.key ?? null);
|
|
1163
|
+
}
|
|
1164
|
+
function emitSelectionChange() {
|
|
1165
|
+
options.onSelectionChange?.(new Set(selectedKeys));
|
|
1166
|
+
notifySelection();
|
|
1167
|
+
}
|
|
1168
|
+
function toggleRowSelection(id) {
|
|
1169
|
+
if (selectionMode === 'none' || id === undefined || disabledKeys.has(id))
|
|
1170
|
+
return;
|
|
1171
|
+
if (selectionMode === 'single') {
|
|
1172
|
+
const wasSelected = selectedKeys.has(id);
|
|
1173
|
+
setSelectedKeys(wasSelected ? new Set() : new Set([id]), wasSelected ? null : id);
|
|
1174
|
+
emitSelectionChange();
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
const next = new Set(selectedKeys);
|
|
1178
|
+
if (next.has(id)) {
|
|
1179
|
+
next.delete(id);
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
next.add(id);
|
|
1183
|
+
}
|
|
1184
|
+
setSelectedKeys(next, id);
|
|
1185
|
+
emitSelectionChange();
|
|
1186
|
+
}
|
|
1187
|
+
function selectAllRows() {
|
|
1188
|
+
if (selectionMode !== 'multiple')
|
|
1189
|
+
return;
|
|
1190
|
+
const next = new Set();
|
|
1191
|
+
for (const token of getOrderedRowTokens('body')) {
|
|
1192
|
+
const row = rows.get(token);
|
|
1193
|
+
if (!row?.id || disabledKeys.has(row.id) || row.disabled)
|
|
1194
|
+
continue;
|
|
1195
|
+
next.add(row.id);
|
|
1196
|
+
}
|
|
1197
|
+
setSelectedKeys(next, next.values().next().value ?? null);
|
|
1198
|
+
emitSelectionChange();
|
|
1199
|
+
}
|
|
1200
|
+
function deselectAllRows() {
|
|
1201
|
+
if (selectedKeys.size === 0)
|
|
1202
|
+
return;
|
|
1203
|
+
setSelectedKeys(new Set(), null);
|
|
1204
|
+
emitSelectionChange();
|
|
1205
|
+
}
|
|
1206
|
+
function setSelection(keys) {
|
|
1207
|
+
const next = new Set(keys);
|
|
1208
|
+
const preservedAnchor = selectionAnchorKey !== null && next.has(selectionAnchorKey) ? selectionAnchorKey : undefined;
|
|
1209
|
+
setSelectedKeys(next, preservedAnchor);
|
|
1210
|
+
notifySelection();
|
|
1211
|
+
}
|
|
1212
|
+
function setSelectionMode(mode) {
|
|
1213
|
+
const previousSelectedKeys = new Set(selectedKeys);
|
|
1214
|
+
const previousAnchor = selectionAnchorKey;
|
|
1215
|
+
selectionMode = mode;
|
|
1216
|
+
setSelectedKeys(new Set(selectedKeys), selectionAnchorKey);
|
|
1217
|
+
if (!hasSameSelection(previousSelectedKeys, selectedKeys) ||
|
|
1218
|
+
previousAnchor !== selectionAnchorKey) {
|
|
1219
|
+
emitSelectionChange();
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
notifySelection();
|
|
1223
|
+
}
|
|
1224
|
+
function setSelectionBehavior(behavior) {
|
|
1225
|
+
selectionBehavior = behavior;
|
|
1226
|
+
notifySelection();
|
|
1227
|
+
}
|
|
1228
|
+
function setDisabledKeys(keys) {
|
|
1229
|
+
disabledKeys.clear();
|
|
1230
|
+
if (keys) {
|
|
1231
|
+
for (const key of keys) {
|
|
1232
|
+
disabledKeys.add(key);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
notifySelection();
|
|
1236
|
+
}
|
|
1237
|
+
function setSortDescriptor(descriptor) {
|
|
1238
|
+
sortDescriptor = descriptor;
|
|
1239
|
+
options.onSortChange?.(descriptor);
|
|
1240
|
+
notifySort();
|
|
1241
|
+
}
|
|
1242
|
+
function isColumnSortable(columnId) {
|
|
1243
|
+
return Array.from(columns.values()).some((column) => column.id === columnId && column.allowsSorting);
|
|
1244
|
+
}
|
|
1245
|
+
function toggleSort(columnId) {
|
|
1246
|
+
if (!isColumnSortable(columnId))
|
|
1247
|
+
return;
|
|
1248
|
+
if (!sortDescriptor || sortDescriptor.column !== columnId) {
|
|
1249
|
+
setSortDescriptor({ column: columnId, direction: 'ascending' });
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
setSortDescriptor({
|
|
1253
|
+
column: columnId,
|
|
1254
|
+
direction: sortDescriptor.direction === 'ascending' ? 'descending' : 'ascending'
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
function getSortDirection(columnId) {
|
|
1258
|
+
if (sortDescriptor?.column !== columnId)
|
|
1259
|
+
return undefined;
|
|
1260
|
+
return sortDescriptor.direction;
|
|
1261
|
+
}
|
|
1262
|
+
return {
|
|
1263
|
+
layoutVersion,
|
|
1264
|
+
selectionVersion,
|
|
1265
|
+
focusVersion,
|
|
1266
|
+
sortVersion,
|
|
1267
|
+
widthVersion,
|
|
1268
|
+
resizeVersion,
|
|
1269
|
+
createInstanceToken,
|
|
1270
|
+
get selectionMode() {
|
|
1271
|
+
return selectionMode;
|
|
1272
|
+
},
|
|
1273
|
+
get selectionBehavior() {
|
|
1274
|
+
return selectionBehavior;
|
|
1275
|
+
},
|
|
1276
|
+
disabledKeys,
|
|
1277
|
+
get focusedCellKey() {
|
|
1278
|
+
return focusedCellKey;
|
|
1279
|
+
},
|
|
1280
|
+
get focusVisible() {
|
|
1281
|
+
return focusVisible;
|
|
1282
|
+
},
|
|
1283
|
+
get sortDescriptor() {
|
|
1284
|
+
return sortDescriptor;
|
|
1285
|
+
},
|
|
1286
|
+
get resizingColumnId() {
|
|
1287
|
+
return resizingColumnId;
|
|
1288
|
+
},
|
|
1289
|
+
registerColumn,
|
|
1290
|
+
unregisterColumn,
|
|
1291
|
+
registerColumnResizer,
|
|
1292
|
+
unregisterColumnResizer,
|
|
1293
|
+
getColumnCount,
|
|
1294
|
+
getVisibleColumnCount,
|
|
1295
|
+
getColumnAt,
|
|
1296
|
+
getColumnIndexByToken,
|
|
1297
|
+
getVisibleColumnIndexByToken,
|
|
1298
|
+
getColumnTextValue,
|
|
1299
|
+
getColumnWidth,
|
|
1300
|
+
getColumnMinWidth,
|
|
1301
|
+
getColumnMaxWidth,
|
|
1302
|
+
isColumnHidden,
|
|
1303
|
+
isColumnResizable,
|
|
1304
|
+
getColumnWidths,
|
|
1305
|
+
getVisibleColumnWidths,
|
|
1306
|
+
setColumnWidths,
|
|
1307
|
+
setColumnWidth,
|
|
1308
|
+
setHiddenColumns,
|
|
1309
|
+
measureColumnContentWidth,
|
|
1310
|
+
startColumnResize,
|
|
1311
|
+
endColumnResize,
|
|
1312
|
+
suppressHeaderClickOnce,
|
|
1313
|
+
consumeHeaderClickSuppression,
|
|
1314
|
+
hasResizableColumns,
|
|
1315
|
+
registerRow,
|
|
1316
|
+
unregisterRow,
|
|
1317
|
+
getHeaderRowCount,
|
|
1318
|
+
getBodyRowCount,
|
|
1319
|
+
isRowSelected,
|
|
1320
|
+
isRowFocused,
|
|
1321
|
+
isRowFocusTarget,
|
|
1322
|
+
getRowFocusEdge,
|
|
1323
|
+
isRowDisabled,
|
|
1324
|
+
hasSelectableRows,
|
|
1325
|
+
getSelectionCheckboxState,
|
|
1326
|
+
registerCell,
|
|
1327
|
+
unregisterCell,
|
|
1328
|
+
isCellFocused,
|
|
1329
|
+
isCellTabStop,
|
|
1330
|
+
isRowTabStop,
|
|
1331
|
+
focusCellByKey,
|
|
1332
|
+
focusRowByToken,
|
|
1333
|
+
pressRow,
|
|
1334
|
+
setFocusedCell,
|
|
1335
|
+
setFocusedRow,
|
|
1336
|
+
setFocusVisible,
|
|
1337
|
+
moveFocus,
|
|
1338
|
+
moveToRowStart,
|
|
1339
|
+
moveToRowEnd,
|
|
1340
|
+
moveToBodyRowStart,
|
|
1341
|
+
moveToBodyRowEnd,
|
|
1342
|
+
moveToGridStart,
|
|
1343
|
+
moveToGridEnd,
|
|
1344
|
+
toggleRowSelection,
|
|
1345
|
+
selectAllRows,
|
|
1346
|
+
deselectAllRows,
|
|
1347
|
+
setSelection,
|
|
1348
|
+
setSelectionMode,
|
|
1349
|
+
setSelectionBehavior,
|
|
1350
|
+
setDisabledKeys,
|
|
1351
|
+
setSortDescriptor,
|
|
1352
|
+
toggleSort,
|
|
1353
|
+
isColumnSortable,
|
|
1354
|
+
getSortDirection
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
export function setTableContext(context) {
|
|
1358
|
+
setContext(TABLE_KEY, context);
|
|
1359
|
+
return context;
|
|
1360
|
+
}
|
|
1361
|
+
export function getTableContext() {
|
|
1362
|
+
return getContext(TABLE_KEY);
|
|
1363
|
+
}
|
|
1364
|
+
export function useTableContext() {
|
|
1365
|
+
const context = getTableContext();
|
|
1366
|
+
if (!context) {
|
|
1367
|
+
throw new Error('Table parts must be used inside `Table.Root`.');
|
|
1368
|
+
}
|
|
1369
|
+
return context;
|
|
1370
|
+
}
|
|
1371
|
+
export function setTableSectionContext(context) {
|
|
1372
|
+
setContext(TABLE_SECTION_KEY, context);
|
|
1373
|
+
return context;
|
|
1374
|
+
}
|
|
1375
|
+
export function getTableSectionContext() {
|
|
1376
|
+
return getContext(TABLE_SECTION_KEY);
|
|
1377
|
+
}
|
|
1378
|
+
export function useTableSectionContext() {
|
|
1379
|
+
const context = getTableSectionContext();
|
|
1380
|
+
if (!context) {
|
|
1381
|
+
throw new Error('Table section parts must be used inside `Table.Root`.');
|
|
1382
|
+
}
|
|
1383
|
+
return context;
|
|
1384
|
+
}
|
|
1385
|
+
export function setTableRowContext(context) {
|
|
1386
|
+
setContext(TABLE_ROW_KEY, context);
|
|
1387
|
+
return context;
|
|
1388
|
+
}
|
|
1389
|
+
export function getTableRowContext() {
|
|
1390
|
+
return getContext(TABLE_ROW_KEY);
|
|
1391
|
+
}
|
|
1392
|
+
export function useTableRowContext() {
|
|
1393
|
+
const context = getTableRowContext();
|
|
1394
|
+
if (!context) {
|
|
1395
|
+
throw new Error('Table cells must be used inside `Table.Row`.');
|
|
1396
|
+
}
|
|
1397
|
+
return context;
|
|
1398
|
+
}
|
|
1399
|
+
export function setTableCellContext(context) {
|
|
1400
|
+
setContext(TABLE_CELL_KEY, context);
|
|
1401
|
+
return context;
|
|
1402
|
+
}
|
|
1403
|
+
export function getTableCellContext() {
|
|
1404
|
+
return getContext(TABLE_CELL_KEY);
|
|
1405
|
+
}
|
|
1406
|
+
export function useTableCellContext() {
|
|
1407
|
+
const context = getTableCellContext();
|
|
1408
|
+
if (!context) {
|
|
1409
|
+
throw new Error('Table interactive cell parts must be used inside `Table.Cell` or `Table.ColumnHeaderCell`.');
|
|
1410
|
+
}
|
|
1411
|
+
return context;
|
|
1412
|
+
}
|
|
1413
|
+
export function setTableColumnContext(context) {
|
|
1414
|
+
setContext(TABLE_COLUMN_KEY, context);
|
|
1415
|
+
return context;
|
|
1416
|
+
}
|
|
1417
|
+
export function getTableColumnContext() {
|
|
1418
|
+
return getContext(TABLE_COLUMN_KEY);
|
|
1419
|
+
}
|
|
1420
|
+
export function useTableColumnContext() {
|
|
1421
|
+
const context = getTableColumnContext();
|
|
1422
|
+
if (!context) {
|
|
1423
|
+
throw new Error('`Table.ColumnHeaderCell` must be used inside `Table.Column`.');
|
|
1424
|
+
}
|
|
1425
|
+
return context;
|
|
1426
|
+
}
|