@gridkitjs/react 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/index.d.ts +100 -5
- package/dist/index.js +869 -103
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,38 +1,487 @@
|
|
|
1
1
|
// src/DataGrid/DataGrid.tsx
|
|
2
|
-
import { useMemo, useRef, useState as
|
|
2
|
+
import { useMemo as useMemo2, useRef as useRef4, useState as useState5 } from "react";
|
|
3
3
|
import {
|
|
4
4
|
applyColumnOrder,
|
|
5
5
|
defineColumnsFromRows,
|
|
6
6
|
fitColumnsToWidth,
|
|
7
7
|
moveColumnBefore,
|
|
8
8
|
resolveColumnWidths,
|
|
9
|
+
resolveRowId,
|
|
10
|
+
resolveShownRows,
|
|
9
11
|
totalColumnWidth
|
|
10
12
|
} from "@gridkitjs/core";
|
|
11
13
|
|
|
12
14
|
// src/DataGrid/components/GridHeader.tsx
|
|
13
15
|
import { createPortal } from "react-dom";
|
|
14
16
|
import { KEYBOARD_STEP } from "@gridkitjs/core";
|
|
17
|
+
|
|
18
|
+
// src/DataGrid/useGridNavigation.ts
|
|
19
|
+
import {
|
|
20
|
+
useEffect,
|
|
21
|
+
useRef,
|
|
22
|
+
useState
|
|
23
|
+
} from "react";
|
|
24
|
+
var HEADER_ROW = -1;
|
|
25
|
+
var FALLBACK_PAGE = 10;
|
|
26
|
+
function clampFocus(focus, rowCount, columnCount) {
|
|
27
|
+
return {
|
|
28
|
+
rowIndex: Math.min(Math.max(focus.rowIndex, HEADER_ROW), rowCount - 1),
|
|
29
|
+
columnIndex: Math.min(
|
|
30
|
+
Math.max(focus.columnIndex, 0),
|
|
31
|
+
Math.max(columnCount - 1, 0)
|
|
32
|
+
)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function cellAt(table, focus) {
|
|
36
|
+
const row = focus.rowIndex === HEADER_ROW ? table.tHead?.rows[0] ?? null : table.tBodies[0]?.rows[focus.rowIndex] ?? null;
|
|
37
|
+
return row?.cells[focus.columnIndex] ?? null;
|
|
38
|
+
}
|
|
39
|
+
function useGridNavigation({
|
|
40
|
+
tableRef,
|
|
41
|
+
rowCount,
|
|
42
|
+
columnCount
|
|
43
|
+
}) {
|
|
44
|
+
const [stored, setStored] = useState({
|
|
45
|
+
rowIndex: HEADER_ROW,
|
|
46
|
+
columnIndex: 0
|
|
47
|
+
});
|
|
48
|
+
const focus = clampFocus(stored, rowCount, columnCount);
|
|
49
|
+
const pending = useRef(false);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!pending.current) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
pending.current = false;
|
|
55
|
+
const table = tableRef.current;
|
|
56
|
+
if (table === null) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
cellAt(table, { rowIndex: focus.rowIndex, columnIndex: focus.columnIndex })?.focus();
|
|
60
|
+
}, [tableRef, focus.rowIndex, focus.columnIndex]);
|
|
61
|
+
function pageSize() {
|
|
62
|
+
const table = tableRef.current;
|
|
63
|
+
const firstRow = table?.tBodies[0]?.rows[0];
|
|
64
|
+
const viewport = table?.parentElement;
|
|
65
|
+
if (firstRow === void 0 || viewport === null || viewport === void 0 || firstRow.offsetHeight === 0) {
|
|
66
|
+
return FALLBACK_PAGE;
|
|
67
|
+
}
|
|
68
|
+
return Math.max(
|
|
69
|
+
1,
|
|
70
|
+
Math.floor(viewport.clientHeight / firstRow.offsetHeight)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function focusCell(rowIndex, columnIndex) {
|
|
74
|
+
const next = clampFocus({ rowIndex, columnIndex }, rowCount, columnCount);
|
|
75
|
+
if (next.rowIndex === focus.rowIndex && next.columnIndex === focus.columnIndex) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
pending.current = true;
|
|
79
|
+
setStored(next);
|
|
80
|
+
}
|
|
81
|
+
function tabIndexFor(rowIndex, columnIndex) {
|
|
82
|
+
return rowIndex === focus.rowIndex && columnIndex === focus.columnIndex ? 0 : -1;
|
|
83
|
+
}
|
|
84
|
+
function onKeyDown(event) {
|
|
85
|
+
if (event.altKey) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { rowIndex, columnIndex } = focus;
|
|
89
|
+
let next;
|
|
90
|
+
switch (event.key) {
|
|
91
|
+
case "ArrowLeft":
|
|
92
|
+
if (event.ctrlKey) return;
|
|
93
|
+
next = { rowIndex, columnIndex: columnIndex - 1 };
|
|
94
|
+
break;
|
|
95
|
+
case "ArrowRight":
|
|
96
|
+
if (event.ctrlKey) return;
|
|
97
|
+
next = { rowIndex, columnIndex: columnIndex + 1 };
|
|
98
|
+
break;
|
|
99
|
+
case "ArrowUp":
|
|
100
|
+
next = { rowIndex: rowIndex - 1, columnIndex };
|
|
101
|
+
break;
|
|
102
|
+
case "ArrowDown":
|
|
103
|
+
next = { rowIndex: rowIndex + 1, columnIndex };
|
|
104
|
+
break;
|
|
105
|
+
// Ctrl takes Home and End to the grid's ends rather than the row's, which
|
|
106
|
+
// is the one place Ctrl is navigation rather than reorder.
|
|
107
|
+
case "Home":
|
|
108
|
+
next = event.ctrlKey ? { rowIndex: HEADER_ROW, columnIndex: 0 } : { rowIndex, columnIndex: 0 };
|
|
109
|
+
break;
|
|
110
|
+
case "End":
|
|
111
|
+
next = event.ctrlKey ? { rowIndex: rowCount - 1, columnIndex: columnCount - 1 } : { rowIndex, columnIndex: columnCount - 1 };
|
|
112
|
+
break;
|
|
113
|
+
case "PageUp":
|
|
114
|
+
next = { rowIndex: rowIndex - pageSize(), columnIndex };
|
|
115
|
+
break;
|
|
116
|
+
case "PageDown":
|
|
117
|
+
next = { rowIndex: rowIndex + pageSize(), columnIndex };
|
|
118
|
+
break;
|
|
119
|
+
default:
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
focusCell(next.rowIndex, next.columnIndex);
|
|
124
|
+
}
|
|
125
|
+
return { focus, tabIndexFor, focusCell, onKeyDown };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/DataGrid/useGridSelection.ts
|
|
129
|
+
import { useMemo, useRef as useRef2 } from "react";
|
|
130
|
+
import {
|
|
131
|
+
accessDotted,
|
|
132
|
+
clearSelection,
|
|
133
|
+
diffSelection,
|
|
134
|
+
selectAll,
|
|
135
|
+
selectCell,
|
|
136
|
+
selectOnly,
|
|
137
|
+
selectRange,
|
|
138
|
+
toggleCellSelection,
|
|
139
|
+
toggleSelection
|
|
140
|
+
} from "@gridkitjs/core";
|
|
141
|
+
function intentOf(event) {
|
|
142
|
+
if (event.shiftKey) {
|
|
143
|
+
return "range";
|
|
144
|
+
}
|
|
145
|
+
return event.ctrlKey || event.metaKey ? "toggle" : "replace";
|
|
146
|
+
}
|
|
147
|
+
function applyIntent(selection, orderedIds, anchorId, id, intent, mode) {
|
|
148
|
+
switch (intent) {
|
|
149
|
+
case "toggle":
|
|
150
|
+
return toggleSelection(selection, id, mode);
|
|
151
|
+
// A range with no anchor yet spans from the id to itself, which is the
|
|
152
|
+
// plain click a first Shift-click may as well be.
|
|
153
|
+
case "range":
|
|
154
|
+
return selectRange(selection, orderedIds, anchorId ?? id, id, mode);
|
|
155
|
+
default:
|
|
156
|
+
return selectOnly(selection, id, mode);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function countMessage(count, noun) {
|
|
160
|
+
if (count === 0) {
|
|
161
|
+
return `No ${noun}s selected`;
|
|
162
|
+
}
|
|
163
|
+
return count === 1 ? `1 ${noun} selected` : `${String(count)} ${noun}s selected`;
|
|
164
|
+
}
|
|
165
|
+
function useGridSelection({
|
|
166
|
+
rows,
|
|
167
|
+
columns,
|
|
168
|
+
selectable,
|
|
169
|
+
rowSelection,
|
|
170
|
+
setRowSelection,
|
|
171
|
+
columnSelection,
|
|
172
|
+
setColumnSelection,
|
|
173
|
+
cellSelection,
|
|
174
|
+
setCellSelection,
|
|
175
|
+
callbacks,
|
|
176
|
+
announce
|
|
177
|
+
}) {
|
|
178
|
+
const rowMode = selectable?.rows ?? false;
|
|
179
|
+
const columnMode = selectable?.columns ?? false;
|
|
180
|
+
const cellMode = selectable?.cells ?? false;
|
|
181
|
+
const rowsById = useMemo(
|
|
182
|
+
() => new Map(rows.map((entry) => [entry.rowId, entry])),
|
|
183
|
+
[rows]
|
|
184
|
+
);
|
|
185
|
+
const rowIds = useMemo(() => rows.map((entry) => entry.rowId), [rows]);
|
|
186
|
+
const columnIds = useMemo(() => columns.map((entry) => entry.id), [columns]);
|
|
187
|
+
const selectedRowIds = useMemo(() => new Set(rowSelection), [rowSelection]);
|
|
188
|
+
const selectedColumnIds = useMemo(
|
|
189
|
+
() => new Set(columnSelection),
|
|
190
|
+
[columnSelection]
|
|
191
|
+
);
|
|
192
|
+
const rowAnchor = useRef2(null);
|
|
193
|
+
const columnAnchor = useRef2(null);
|
|
194
|
+
function resolveRows(ids) {
|
|
195
|
+
return ids.map((id) => rowsById.get(id)).filter((row) => row !== void 0);
|
|
196
|
+
}
|
|
197
|
+
function resolveColumns(ids) {
|
|
198
|
+
return ids.map((id) => {
|
|
199
|
+
const columnIndex = columns.findIndex((entry) => entry.id === id);
|
|
200
|
+
const column = columns[columnIndex];
|
|
201
|
+
return column === void 0 ? void 0 : { columnId: id, column, columnIndex };
|
|
202
|
+
}).filter((column) => column !== void 0);
|
|
203
|
+
}
|
|
204
|
+
function resolveCell(ref) {
|
|
205
|
+
if (ref === null) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const entry = rowsById.get(ref.rowId);
|
|
209
|
+
const columnIndex = columns.findIndex(
|
|
210
|
+
(candidate) => candidate.id === ref.columnId
|
|
211
|
+
);
|
|
212
|
+
const column = columns[columnIndex];
|
|
213
|
+
if (entry === void 0 || column === void 0) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
rowId: ref.rowId,
|
|
218
|
+
columnId: ref.columnId,
|
|
219
|
+
row: entry.row,
|
|
220
|
+
column,
|
|
221
|
+
rowIndex: entry.rowIndex,
|
|
222
|
+
columnIndex,
|
|
223
|
+
value: accessDotted(entry.row, column.column.field)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function announceCount(changed, total, noun) {
|
|
227
|
+
if (changed > 1) {
|
|
228
|
+
announce(countMessage(total, noun));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function commitRows(next) {
|
|
232
|
+
if (next === rowSelection) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
setRowSelection(next);
|
|
236
|
+
const { added, removed } = diffSelection(rowSelection, next);
|
|
237
|
+
const addedRows = resolveRows(added);
|
|
238
|
+
const removedRows = resolveRows(removed);
|
|
239
|
+
for (const row of addedRows) {
|
|
240
|
+
callbacks.onRowSelect?.({ row, selection: next });
|
|
241
|
+
}
|
|
242
|
+
if (addedRows.length > 0) {
|
|
243
|
+
callbacks.onRowsSelect?.({ rows: addedRows, selection: next });
|
|
244
|
+
}
|
|
245
|
+
for (const row of removedRows) {
|
|
246
|
+
callbacks.onRowDeselect?.({ row, selection: next });
|
|
247
|
+
}
|
|
248
|
+
if (removedRows.length > 0) {
|
|
249
|
+
callbacks.onRowsDeselect?.({ rows: removedRows, selection: next });
|
|
250
|
+
}
|
|
251
|
+
callbacks.onRowSelectionChange?.({
|
|
252
|
+
added: addedRows,
|
|
253
|
+
removed: removedRows,
|
|
254
|
+
selected: resolveRows(next),
|
|
255
|
+
selection: next
|
|
256
|
+
});
|
|
257
|
+
announceCount(added.length + removed.length, next.length, "row");
|
|
258
|
+
}
|
|
259
|
+
function commitColumns(next) {
|
|
260
|
+
if (next === columnSelection) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
setColumnSelection(next);
|
|
264
|
+
const { added, removed } = diffSelection(columnSelection, next);
|
|
265
|
+
const addedColumns = resolveColumns(added);
|
|
266
|
+
const removedColumns = resolveColumns(removed);
|
|
267
|
+
for (const column of addedColumns) {
|
|
268
|
+
callbacks.onColumnSelect?.({ column, selection: next });
|
|
269
|
+
}
|
|
270
|
+
if (addedColumns.length > 0) {
|
|
271
|
+
callbacks.onColumnsSelect?.({ columns: addedColumns, selection: next });
|
|
272
|
+
}
|
|
273
|
+
for (const column of removedColumns) {
|
|
274
|
+
callbacks.onColumnDeselect?.({ column, selection: next });
|
|
275
|
+
}
|
|
276
|
+
if (removedColumns.length > 0) {
|
|
277
|
+
callbacks.onColumnsDeselect?.({
|
|
278
|
+
columns: removedColumns,
|
|
279
|
+
selection: next
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
callbacks.onColumnSelectionChange?.({
|
|
283
|
+
added: addedColumns,
|
|
284
|
+
removed: removedColumns,
|
|
285
|
+
selected: resolveColumns(next),
|
|
286
|
+
selection: next
|
|
287
|
+
});
|
|
288
|
+
announceCount(added.length + removed.length, next.length, "column");
|
|
289
|
+
}
|
|
290
|
+
function commitCell(next) {
|
|
291
|
+
if (next === cellSelection) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
setCellSelection(next);
|
|
295
|
+
const selected = resolveCell(next);
|
|
296
|
+
const deselected = resolveCell(cellSelection);
|
|
297
|
+
if (deselected !== null) {
|
|
298
|
+
callbacks.onCellDeselect?.({ cell: deselected, selection: next });
|
|
299
|
+
}
|
|
300
|
+
if (selected !== null) {
|
|
301
|
+
callbacks.onCellSelect?.({ cell: selected, selection: next });
|
|
302
|
+
}
|
|
303
|
+
callbacks.onCellSelectionChange?.({
|
|
304
|
+
selected,
|
|
305
|
+
deselected,
|
|
306
|
+
selection: next
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function selectRow(rowId, intent) {
|
|
310
|
+
const next = applyIntent(
|
|
311
|
+
rowSelection,
|
|
312
|
+
rowIds,
|
|
313
|
+
rowAnchor.current,
|
|
314
|
+
rowId,
|
|
315
|
+
intent,
|
|
316
|
+
rowMode
|
|
317
|
+
);
|
|
318
|
+
if (intent !== "range") {
|
|
319
|
+
rowAnchor.current = rowId;
|
|
320
|
+
}
|
|
321
|
+
commitRows(next);
|
|
322
|
+
}
|
|
323
|
+
function selectColumn(columnId, intent) {
|
|
324
|
+
const next = applyIntent(
|
|
325
|
+
columnSelection,
|
|
326
|
+
columnIds,
|
|
327
|
+
columnAnchor.current,
|
|
328
|
+
columnId,
|
|
329
|
+
intent,
|
|
330
|
+
columnMode
|
|
331
|
+
);
|
|
332
|
+
if (intent !== "range") {
|
|
333
|
+
columnAnchor.current = columnId;
|
|
334
|
+
}
|
|
335
|
+
commitColumns(next);
|
|
336
|
+
}
|
|
337
|
+
function selectCellAt(cell, intent) {
|
|
338
|
+
commitCell(
|
|
339
|
+
intent === "toggle" ? toggleCellSelection(cellSelection, cell, cellMode) : selectCell(cellSelection, cell, cellMode)
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
function selectAllRows() {
|
|
343
|
+
commitRows(selectAll(rowSelection, rowIds, rowMode));
|
|
344
|
+
}
|
|
345
|
+
function clear() {
|
|
346
|
+
commitRows(clearSelection(rowSelection));
|
|
347
|
+
commitColumns(clearSelection(columnSelection));
|
|
348
|
+
commitCell(null);
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
rowMode,
|
|
352
|
+
columnMode,
|
|
353
|
+
cellMode,
|
|
354
|
+
selectedRowIds,
|
|
355
|
+
selectedColumnIds,
|
|
356
|
+
selectedCell: cellSelection,
|
|
357
|
+
selectRow,
|
|
358
|
+
selectColumn,
|
|
359
|
+
selectCell: selectCellAt,
|
|
360
|
+
selectAllRows,
|
|
361
|
+
clear
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/DataGrid/components/GridHeader.tsx
|
|
15
366
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
367
|
+
function ChevronsUpDownIcon() {
|
|
368
|
+
return /* @__PURE__ */ jsxs(
|
|
369
|
+
"svg",
|
|
370
|
+
{
|
|
371
|
+
viewBox: "0 0 24 24",
|
|
372
|
+
fill: "none",
|
|
373
|
+
stroke: "currentColor",
|
|
374
|
+
strokeWidth: 2,
|
|
375
|
+
strokeLinecap: "round",
|
|
376
|
+
strokeLinejoin: "round",
|
|
377
|
+
"aria-hidden": "true",
|
|
378
|
+
children: [
|
|
379
|
+
/* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }),
|
|
380
|
+
/* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
function ChevronUpIcon() {
|
|
386
|
+
return /* @__PURE__ */ jsx(
|
|
387
|
+
"svg",
|
|
388
|
+
{
|
|
389
|
+
viewBox: "0 0 24 24",
|
|
390
|
+
fill: "none",
|
|
391
|
+
stroke: "currentColor",
|
|
392
|
+
strokeWidth: 2,
|
|
393
|
+
strokeLinecap: "round",
|
|
394
|
+
strokeLinejoin: "round",
|
|
395
|
+
"aria-hidden": "true",
|
|
396
|
+
children: /* @__PURE__ */ jsx("path", { d: "m18 15-6-6-6 6" })
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
}
|
|
16
400
|
function GridHeader({
|
|
17
401
|
columns,
|
|
18
402
|
resize,
|
|
19
|
-
drag
|
|
403
|
+
drag,
|
|
404
|
+
sort,
|
|
405
|
+
sortableColumns,
|
|
406
|
+
nav,
|
|
407
|
+
selection
|
|
20
408
|
}) {
|
|
21
409
|
const { beforeId } = drag.dropTarget ?? {};
|
|
22
410
|
const draggedEntry = columns.find((entry) => entry.id === drag.draggedColumnId) ?? null;
|
|
23
411
|
return /* @__PURE__ */ jsxs("thead", { children: [
|
|
24
|
-
/* @__PURE__ */ jsx("tr", { className: "grid-header", children: columns.map((entry, index) => {
|
|
412
|
+
/* @__PURE__ */ jsx("tr", { className: "grid-header", role: "row", "aria-rowindex": 1, children: columns.map((entry, index) => {
|
|
25
413
|
const { column } = entry;
|
|
26
414
|
const resizing = resize.activeColumnId === entry.id;
|
|
27
415
|
const dragging = drag.draggedColumnId === entry.id;
|
|
28
416
|
const dropBefore = beforeId !== void 0 && beforeId === entry.id;
|
|
29
417
|
const dropAfter = beforeId === null && index === columns.length - 1;
|
|
418
|
+
const selected = selection.selectedColumnIds.has(entry.id);
|
|
419
|
+
const sortable = column.sortable ?? sortableColumns;
|
|
420
|
+
const sortDirection = sort.directionFor(entry.id);
|
|
421
|
+
const sortPriority = sort.priorityFor(entry.id);
|
|
422
|
+
const shortcuts = [
|
|
423
|
+
entry.reorderable ? "Control+ArrowLeft Control+ArrowRight" : "",
|
|
424
|
+
entry.resizable ? "Alt+ArrowLeft Alt+ArrowRight" : "",
|
|
425
|
+
sortable ? "Alt+ArrowUp" : ""
|
|
426
|
+
].filter(Boolean).join(" ");
|
|
30
427
|
return /* @__PURE__ */ jsxs(
|
|
31
428
|
"th",
|
|
32
429
|
{
|
|
430
|
+
scope: "col",
|
|
33
431
|
"data-gridkit-column": entry.id,
|
|
432
|
+
"aria-colindex": index + 1,
|
|
433
|
+
tabIndex: nav.tabIndexFor(HEADER_ROW, index),
|
|
434
|
+
...selection.columnMode !== false && {
|
|
435
|
+
"aria-selected": selected
|
|
436
|
+
},
|
|
437
|
+
onFocus: () => {
|
|
438
|
+
nav.focusCell(HEADER_ROW, index);
|
|
439
|
+
},
|
|
440
|
+
onClick: (event) => {
|
|
441
|
+
if (drag.justDragged()) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (event.target instanceof Element && (event.target.closest(".header-resize-handle") !== null || event.target.closest(".header-sort-toggle") !== null)) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
selection.selectColumn(entry.id, intentOf(event));
|
|
448
|
+
},
|
|
449
|
+
onKeyDown: (event) => {
|
|
450
|
+
const horizontal = event.key === "ArrowLeft" || event.key === "ArrowRight";
|
|
451
|
+
const direction = event.key === "ArrowLeft" ? -1 : 1;
|
|
452
|
+
if (horizontal && event.ctrlKey && entry.reorderable) {
|
|
453
|
+
event.preventDefault();
|
|
454
|
+
drag.moveByKeyboard(entry, direction);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
if (horizontal && event.altKey && entry.resizable) {
|
|
458
|
+
event.preventDefault();
|
|
459
|
+
resize.nudge(entry, direction * KEYBOARD_STEP);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (event.key === "ArrowUp" && event.altKey && sortable) {
|
|
463
|
+
event.preventDefault();
|
|
464
|
+
sort.toggle(entry, { shiftKey: event.shiftKey });
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (event.key === " " || event.key === "Enter") {
|
|
468
|
+
event.preventDefault();
|
|
469
|
+
selection.selectColumn(
|
|
470
|
+
entry.id,
|
|
471
|
+
intentOf({
|
|
472
|
+
ctrlKey: event.key === " " || event.ctrlKey,
|
|
473
|
+
metaKey: event.metaKey,
|
|
474
|
+
shiftKey: event.shiftKey
|
|
475
|
+
})
|
|
476
|
+
);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
nav.onKeyDown(event);
|
|
480
|
+
},
|
|
34
481
|
className: [
|
|
35
482
|
"header-cell",
|
|
483
|
+
entry.reorderable ? "is-reorderable" : "",
|
|
484
|
+
selected ? "is-selected" : "",
|
|
36
485
|
resizing ? "is-resizing" : "",
|
|
37
486
|
dragging ? "is-dragging" : "",
|
|
38
487
|
dropBefore ? "is-drop-before" : "",
|
|
@@ -40,46 +489,61 @@ function GridHeader({
|
|
|
40
489
|
column.wrap?.header ? "is-wrapped" : "",
|
|
41
490
|
column.headerClassName ?? ""
|
|
42
491
|
].filter(Boolean).join(" "),
|
|
492
|
+
...shortcuts !== "" && { "aria-keyshortcuts": shortcuts },
|
|
493
|
+
...sortable && sortDirection !== null && {
|
|
494
|
+
"aria-sort": sortDirection === "asc" ? "ascending" : "descending"
|
|
495
|
+
},
|
|
43
496
|
...entry.reorderable && {
|
|
44
|
-
tabIndex: 0,
|
|
45
497
|
onPointerDown: (event) => {
|
|
46
498
|
drag.startDrag(entry, event);
|
|
47
|
-
},
|
|
48
|
-
onKeyDown: (event) => {
|
|
49
|
-
if (!event.ctrlKey) return;
|
|
50
|
-
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
|
|
51
|
-
return;
|
|
52
|
-
event.preventDefault();
|
|
53
|
-
drag.moveByKeyboard(
|
|
54
|
-
entry,
|
|
55
|
-
event.key === "ArrowLeft" ? -1 : 1
|
|
56
|
-
);
|
|
57
499
|
}
|
|
58
500
|
},
|
|
59
501
|
children: [
|
|
60
502
|
entry.label,
|
|
61
|
-
|
|
503
|
+
sortable && /*
|
|
504
|
+
* A pointer affordance only, and hidden from assistive
|
|
505
|
+
* technology like the resize handle: the keyboard equivalent
|
|
506
|
+
* lives on the header itself, under Alt+ArrowUp (plain) /
|
|
507
|
+
* Alt+Shift+ArrowUp (stacking).
|
|
508
|
+
*/
|
|
509
|
+
/* @__PURE__ */ jsxs(
|
|
510
|
+
"span",
|
|
511
|
+
{
|
|
512
|
+
className: [
|
|
513
|
+
"header-sort-toggle",
|
|
514
|
+
sortDirection !== null ? `is-sorted-${sortDirection}` : ""
|
|
515
|
+
].filter(Boolean).join(" "),
|
|
516
|
+
"aria-hidden": "true",
|
|
517
|
+
tabIndex: -1,
|
|
518
|
+
onPointerDown: (event) => {
|
|
519
|
+
event.stopPropagation();
|
|
520
|
+
},
|
|
521
|
+
onClick: (event) => {
|
|
522
|
+
sort.toggle(entry, { shiftKey: event.shiftKey });
|
|
523
|
+
},
|
|
524
|
+
children: [
|
|
525
|
+
sortDirection === null ? /* @__PURE__ */ jsx(ChevronsUpDownIcon, {}) : /* @__PURE__ */ jsx(ChevronUpIcon, {}),
|
|
526
|
+
sortPriority !== null && sortPriority > 1 && /* @__PURE__ */ jsx("span", { className: "header-sort-priority", children: sortPriority })
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
),
|
|
530
|
+
entry.resizable && /*
|
|
531
|
+
* A pointer affordance only, and hidden from assistive
|
|
532
|
+
* technology accordingly: a focusable handle per column would
|
|
533
|
+
* put N tab stops inside a widget that is meant to have one.
|
|
534
|
+
* The keyboard resizes from the header itself, under Alt.
|
|
535
|
+
*/
|
|
536
|
+
/* @__PURE__ */ jsx(
|
|
62
537
|
"span",
|
|
63
538
|
{
|
|
64
539
|
className: "header-resize-handle",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"aria-label": `Resize ${column.field}`,
|
|
68
|
-
tabIndex: 0,
|
|
540
|
+
"aria-hidden": "true",
|
|
541
|
+
tabIndex: -1,
|
|
69
542
|
onPointerDown: (event) => {
|
|
70
543
|
resize.startResize(entry, event);
|
|
71
544
|
},
|
|
72
545
|
onDoubleClick: () => {
|
|
73
546
|
resize.sizeToContent(entry);
|
|
74
|
-
},
|
|
75
|
-
onKeyDown: (event) => {
|
|
76
|
-
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
|
|
77
|
-
return;
|
|
78
|
-
event.preventDefault();
|
|
79
|
-
resize.nudge(
|
|
80
|
-
entry,
|
|
81
|
-
event.key === "ArrowLeft" ? -KEYBOARD_STEP : KEYBOARD_STEP
|
|
82
|
-
);
|
|
83
547
|
}
|
|
84
548
|
}
|
|
85
549
|
)
|
|
@@ -106,37 +570,154 @@ function GridHeader({
|
|
|
106
570
|
] });
|
|
107
571
|
}
|
|
108
572
|
|
|
109
|
-
// src/DataGrid/components/
|
|
110
|
-
import { accessDotted } from "@gridkitjs/core";
|
|
111
|
-
import "react";
|
|
573
|
+
// src/DataGrid/components/GridRow.tsx
|
|
574
|
+
import { accessDotted as accessDotted2 } from "@gridkitjs/core";
|
|
575
|
+
import { memo } from "react";
|
|
112
576
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
577
|
+
function GridRowComponent({
|
|
578
|
+
columns,
|
|
579
|
+
rowId,
|
|
580
|
+
row,
|
|
581
|
+
rowIndex,
|
|
582
|
+
activeColumnId,
|
|
583
|
+
selectedColumnIds,
|
|
584
|
+
selected,
|
|
585
|
+
selectedColumnId,
|
|
586
|
+
focusedColumnIndex,
|
|
587
|
+
rowsSelectable,
|
|
588
|
+
cellsSelectable
|
|
589
|
+
}) {
|
|
590
|
+
return /* @__PURE__ */ jsx2(
|
|
591
|
+
"tr",
|
|
592
|
+
{
|
|
593
|
+
role: "row",
|
|
594
|
+
"aria-rowindex": rowIndex + 2,
|
|
595
|
+
...rowsSelectable && { "aria-selected": selected },
|
|
596
|
+
className: ["grid-row", selected ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
597
|
+
children: columns.map(({ id, column, alignment }, columnIndex) => {
|
|
598
|
+
const value = accessDotted2(row, column.field);
|
|
599
|
+
const cellSelected = id === selectedColumnId;
|
|
600
|
+
return /* @__PURE__ */ jsx2(
|
|
601
|
+
"td",
|
|
602
|
+
{
|
|
603
|
+
role: "gridcell",
|
|
604
|
+
"data-gridkit-column": id,
|
|
605
|
+
"aria-colindex": columnIndex + 1,
|
|
606
|
+
tabIndex: columnIndex === focusedColumnIndex ? 0 : -1,
|
|
607
|
+
...cellsSelectable && { "aria-selected": cellSelected },
|
|
608
|
+
className: [
|
|
609
|
+
"grid-cell",
|
|
610
|
+
id === activeColumnId ? "is-resizing" : "",
|
|
611
|
+
cellSelected || selectedColumnIds.has(id) ? "is-selected" : "",
|
|
612
|
+
column.wrap?.cells ? "is-wrapped" : "",
|
|
613
|
+
column.cellClassName ?? ""
|
|
614
|
+
].filter(Boolean).join(" "),
|
|
615
|
+
style: { textAlign: alignment },
|
|
616
|
+
children: column.cellTemplate ? column.cellTemplate({ value, row, rowIndex, rowId, selected }) : value
|
|
617
|
+
},
|
|
618
|
+
id
|
|
619
|
+
);
|
|
620
|
+
})
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
var GridRow = memo(GridRowComponent);
|
|
625
|
+
var GridRow_default = GridRow;
|
|
626
|
+
|
|
627
|
+
// src/DataGrid/components/GridBody.tsx
|
|
628
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
629
|
+
function cellFrom(target) {
|
|
630
|
+
if (!(target instanceof Element)) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
const cell = target.closest("td[data-gridkit-column]");
|
|
634
|
+
const row = cell?.parentElement;
|
|
635
|
+
const columnId = cell?.getAttribute("data-gridkit-column");
|
|
636
|
+
if (!(cell instanceof HTMLTableCellElement) || !(row instanceof HTMLTableRowElement) || columnId === null || columnId === void 0) {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
rowIndex: row.sectionRowIndex,
|
|
641
|
+
columnIndex: cell.cellIndex,
|
|
642
|
+
columnId
|
|
643
|
+
};
|
|
644
|
+
}
|
|
113
645
|
function GridBody({
|
|
114
646
|
columns,
|
|
115
|
-
|
|
116
|
-
activeColumnId
|
|
647
|
+
rows,
|
|
648
|
+
activeColumnId,
|
|
649
|
+
nav,
|
|
650
|
+
selection
|
|
117
651
|
}) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
652
|
+
const { selectedCell, rowMode, cellMode } = selection;
|
|
653
|
+
function targetOf(target) {
|
|
654
|
+
const position = cellFrom(target);
|
|
655
|
+
const entry = position === null ? void 0 : rows[position.rowIndex];
|
|
656
|
+
return position === null || entry === void 0 ? null : { position, rowId: entry.rowId };
|
|
657
|
+
}
|
|
658
|
+
function select(rowId, columnId, modifiers) {
|
|
659
|
+
const intent = intentOf(modifiers);
|
|
660
|
+
if (rowMode !== false) {
|
|
661
|
+
selection.selectRow(rowId, intent);
|
|
662
|
+
}
|
|
663
|
+
if (cellMode !== false) {
|
|
664
|
+
selection.selectCell({ rowId, columnId }, intent);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return /* @__PURE__ */ jsx3(
|
|
668
|
+
"tbody",
|
|
669
|
+
{
|
|
670
|
+
className: "grid-body",
|
|
671
|
+
onFocus: (event) => {
|
|
672
|
+
const found = targetOf(event.target);
|
|
673
|
+
if (found === null) return;
|
|
674
|
+
nav.focusCell(found.position.rowIndex, found.position.columnIndex);
|
|
132
675
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
676
|
+
onClick: (event) => {
|
|
677
|
+
const found = targetOf(event.target);
|
|
678
|
+
if (found === null) return;
|
|
679
|
+
select(found.rowId, found.position.columnId, event);
|
|
680
|
+
},
|
|
681
|
+
onKeyDown: (event) => {
|
|
682
|
+
if (event.key === " " || event.key === "Enter") {
|
|
683
|
+
const found = targetOf(event.target);
|
|
684
|
+
if (found === null) return;
|
|
685
|
+
event.preventDefault();
|
|
686
|
+
select(found.rowId, found.position.columnId, {
|
|
687
|
+
ctrlKey: event.key === " " || event.ctrlKey,
|
|
688
|
+
metaKey: event.metaKey,
|
|
689
|
+
shiftKey: event.shiftKey
|
|
690
|
+
});
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
nav.onKeyDown(event);
|
|
694
|
+
},
|
|
695
|
+
children: rows.map(({ rowId, row, rowIndex }) => /* @__PURE__ */ jsx3(
|
|
696
|
+
GridRow_default,
|
|
697
|
+
{
|
|
698
|
+
columns,
|
|
699
|
+
rowId,
|
|
700
|
+
row,
|
|
701
|
+
rowIndex,
|
|
702
|
+
activeColumnId,
|
|
703
|
+
selectedColumnIds: selection.selectedColumnIds,
|
|
704
|
+
selected: selection.selectedRowIds.has(rowId),
|
|
705
|
+
selectedColumnId: selectedCell?.rowId === rowId ? selectedCell.columnId : null,
|
|
706
|
+
focusedColumnIndex: nav.focus.rowIndex === rowIndex ? nav.focus.columnIndex : null,
|
|
707
|
+
rowsSelectable: rowMode !== false,
|
|
708
|
+
cellsSelectable: cellMode !== false
|
|
709
|
+
},
|
|
710
|
+
rowId
|
|
711
|
+
))
|
|
712
|
+
}
|
|
713
|
+
);
|
|
136
714
|
}
|
|
137
715
|
|
|
138
716
|
// src/DataGrid/useColumnDrag.ts
|
|
139
|
-
import {
|
|
717
|
+
import {
|
|
718
|
+
useRef as useRef3,
|
|
719
|
+
useState as useState2
|
|
720
|
+
} from "react";
|
|
140
721
|
import {
|
|
141
722
|
movesColumn,
|
|
142
723
|
resolveDropBefore
|
|
@@ -154,9 +735,15 @@ function useColumnDrag({
|
|
|
154
735
|
order,
|
|
155
736
|
onDrop
|
|
156
737
|
}) {
|
|
157
|
-
const [draggedColumnId, setDraggedColumnId] =
|
|
158
|
-
const [dropTarget, setDropTarget] =
|
|
159
|
-
const [ghostTransform, setGhostTransform] =
|
|
738
|
+
const [draggedColumnId, setDraggedColumnId] = useState2(null);
|
|
739
|
+
const [dropTarget, setDropTarget] = useState2(null);
|
|
740
|
+
const [ghostTransform, setGhostTransform] = useState2(null);
|
|
741
|
+
const dragged = useRef3(false);
|
|
742
|
+
function justDragged() {
|
|
743
|
+
const answer = dragged.current;
|
|
744
|
+
dragged.current = false;
|
|
745
|
+
return answer;
|
|
746
|
+
}
|
|
160
747
|
function startDrag(entry, event) {
|
|
161
748
|
const header = event.currentTarget;
|
|
162
749
|
const { pointerId, clientX: startX } = event;
|
|
@@ -174,6 +761,7 @@ function useColumnDrag({
|
|
|
174
761
|
return;
|
|
175
762
|
}
|
|
176
763
|
dragging = true;
|
|
764
|
+
dragged.current = true;
|
|
177
765
|
setDraggedColumnId(entry.id);
|
|
178
766
|
}
|
|
179
767
|
placeGhost(clientX, clientY);
|
|
@@ -235,13 +823,14 @@ function useColumnDrag({
|
|
|
235
823
|
dropTarget,
|
|
236
824
|
ghostTransform,
|
|
237
825
|
startDrag,
|
|
238
|
-
moveByKeyboard
|
|
826
|
+
moveByKeyboard,
|
|
827
|
+
justDragged
|
|
239
828
|
};
|
|
240
829
|
}
|
|
241
830
|
|
|
242
831
|
// src/DataGrid/useColumnResize.ts
|
|
243
832
|
import {
|
|
244
|
-
useState as
|
|
833
|
+
useState as useState3
|
|
245
834
|
} from "react";
|
|
246
835
|
import {
|
|
247
836
|
applyColumnResize,
|
|
@@ -254,7 +843,7 @@ import {
|
|
|
254
843
|
// src/DataGrid/measureColumnContent.ts
|
|
255
844
|
function measureColumnContent(table, columnId) {
|
|
256
845
|
const cells = table.querySelectorAll(
|
|
257
|
-
`[data-gridkit-column="${CSS.escape(columnId)}"]`
|
|
846
|
+
`td[data-gridkit-column="${CSS.escape(columnId)}"]`
|
|
258
847
|
);
|
|
259
848
|
const range = document.createRange();
|
|
260
849
|
let widest = null;
|
|
@@ -277,7 +866,7 @@ function useColumnResize({
|
|
|
277
866
|
columnSizeDefaults,
|
|
278
867
|
onColumnResize
|
|
279
868
|
}) {
|
|
280
|
-
const [activeColumnId, setActiveColumnId] =
|
|
869
|
+
const [activeColumnId, setActiveColumnId] = useState3(null);
|
|
281
870
|
function commit(base, columnId, width, phase) {
|
|
282
871
|
const next = { ...base, [columnId]: width };
|
|
283
872
|
setSizing(next);
|
|
@@ -314,16 +903,28 @@ function useColumnResize({
|
|
|
314
903
|
}
|
|
315
904
|
function end() {
|
|
316
905
|
stop();
|
|
317
|
-
|
|
906
|
+
if (width !== session.startWidth) {
|
|
907
|
+
commit(base, session.columnId, width, "end");
|
|
908
|
+
}
|
|
318
909
|
}
|
|
319
910
|
function cancel() {
|
|
320
911
|
stop();
|
|
321
|
-
setSizing(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
912
|
+
setSizing((current) => {
|
|
913
|
+
const next = Object.fromEntries(
|
|
914
|
+
Object.entries(current).filter(
|
|
915
|
+
([columnId]) => columnId !== session.columnId
|
|
916
|
+
)
|
|
917
|
+
);
|
|
918
|
+
if (session.columnId in base) {
|
|
919
|
+
next[session.columnId] = base[session.columnId];
|
|
920
|
+
}
|
|
921
|
+
onColumnResize?.({
|
|
922
|
+
columnId: session.columnId,
|
|
923
|
+
width: session.startWidth,
|
|
924
|
+
sizing: next,
|
|
925
|
+
phase: "end"
|
|
926
|
+
});
|
|
927
|
+
return next;
|
|
327
928
|
});
|
|
328
929
|
}
|
|
329
930
|
function onKeyDown(keyEvent) {
|
|
@@ -369,10 +970,37 @@ function useColumnResize({
|
|
|
369
970
|
return { activeColumnId, startResize, sizeToContent, nudge };
|
|
370
971
|
}
|
|
371
972
|
|
|
973
|
+
// src/DataGrid/useColumnSort.ts
|
|
974
|
+
import {
|
|
975
|
+
sortDirectionFor,
|
|
976
|
+
sortPriorityFor,
|
|
977
|
+
toggleColumnSort
|
|
978
|
+
} from "@gridkitjs/core";
|
|
979
|
+
function useColumnSort({
|
|
980
|
+
sort,
|
|
981
|
+
setSort,
|
|
982
|
+
onColumnSortChange
|
|
983
|
+
}) {
|
|
984
|
+
function toggle(entry, event) {
|
|
985
|
+
const next = toggleColumnSort(sort, entry.id, { stack: event.shiftKey });
|
|
986
|
+
if (next === sort) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
setSort(next);
|
|
990
|
+
onColumnSortChange?.({ columnId: entry.id, sort: next });
|
|
991
|
+
}
|
|
992
|
+
return {
|
|
993
|
+
sort,
|
|
994
|
+
directionFor: (columnId) => sortDirectionFor(sort, columnId),
|
|
995
|
+
priorityFor: (columnId) => sortPriorityFor(sort, columnId),
|
|
996
|
+
toggle
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
|
|
372
1000
|
// src/DataGrid/useElementWidth.ts
|
|
373
|
-
import { useLayoutEffect, useState as
|
|
1001
|
+
import { useLayoutEffect, useState as useState4 } from "react";
|
|
374
1002
|
function useElementWidth(ref, enabled) {
|
|
375
|
-
const [width, setWidth] =
|
|
1003
|
+
const [width, setWidth] = useState4(null);
|
|
376
1004
|
useLayoutEffect(() => {
|
|
377
1005
|
const element = ref.current;
|
|
378
1006
|
if (element === null || !enabled) {
|
|
@@ -391,12 +1019,17 @@ function useElementWidth(ref, enabled) {
|
|
|
391
1019
|
}
|
|
392
1020
|
|
|
393
1021
|
// src/DataGrid/DataGrid.tsx
|
|
394
|
-
import { jsx as
|
|
1022
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
395
1023
|
function DataGridComponent({
|
|
396
1024
|
dataSource,
|
|
397
1025
|
columns,
|
|
1026
|
+
getRowId,
|
|
398
1027
|
borders,
|
|
399
1028
|
hoverable,
|
|
1029
|
+
selectable,
|
|
1030
|
+
defaultRowSelection,
|
|
1031
|
+
defaultColumnSelection,
|
|
1032
|
+
defaultCellSelection,
|
|
400
1033
|
resizableColumns = false,
|
|
401
1034
|
reorderableColumns = false,
|
|
402
1035
|
resizeMode = "fit",
|
|
@@ -404,31 +1037,58 @@ function DataGridComponent({
|
|
|
404
1037
|
defaultColumnOrder,
|
|
405
1038
|
columnSizeDefaults,
|
|
406
1039
|
onColumnResize,
|
|
407
|
-
onColumnOrderChange
|
|
1040
|
+
onColumnOrderChange,
|
|
1041
|
+
sortableColumns = false,
|
|
1042
|
+
defaultColumnSort,
|
|
1043
|
+
onColumnSortChange,
|
|
1044
|
+
defaultFilter,
|
|
1045
|
+
label,
|
|
1046
|
+
labelledBy,
|
|
1047
|
+
...callbacks
|
|
408
1048
|
}) {
|
|
409
1049
|
const hoverRows = hoverable?.rows ?? true;
|
|
410
1050
|
const hoverColumns = hoverable?.columns ?? true;
|
|
411
1051
|
const hoverCells = hoverable?.cells ?? true;
|
|
412
|
-
const viewportRef =
|
|
413
|
-
const tableRef =
|
|
1052
|
+
const viewportRef = useRef4(null);
|
|
1053
|
+
const tableRef = useRef4(null);
|
|
414
1054
|
const viewportWidth = useElementWidth(viewportRef, resizeMode === "fit");
|
|
415
|
-
const [sizing, setSizing] =
|
|
1055
|
+
const [sizing, setSizing] = useState5(
|
|
416
1056
|
defaultColumnSizing ?? {}
|
|
417
1057
|
);
|
|
418
|
-
const [order, setOrder] =
|
|
1058
|
+
const [order, setOrder] = useState5(
|
|
419
1059
|
defaultColumnOrder ?? []
|
|
420
1060
|
);
|
|
421
|
-
const
|
|
1061
|
+
const [sort, setSort] = useState5(defaultColumnSort ?? []);
|
|
1062
|
+
const [filter] = useState5(defaultFilter ?? []);
|
|
1063
|
+
const [announcement, setAnnouncement] = useState5("");
|
|
1064
|
+
const [rowSelection, setRowSelection] = useState5(
|
|
1065
|
+
defaultRowSelection ?? []
|
|
1066
|
+
);
|
|
1067
|
+
const [columnSelection, setColumnSelection] = useState5(
|
|
1068
|
+
defaultColumnSelection ?? []
|
|
1069
|
+
);
|
|
1070
|
+
const [cellSelection, setCellSelection] = useState5(
|
|
1071
|
+
defaultCellSelection ?? null
|
|
1072
|
+
);
|
|
1073
|
+
const rows = useMemo2(
|
|
1074
|
+
() => dataSource?.map((row, rowIndex) => ({
|
|
1075
|
+
rowId: resolveRowId(row, rowIndex, getRowId),
|
|
1076
|
+
row,
|
|
1077
|
+
rowIndex
|
|
1078
|
+
})) ?? [],
|
|
1079
|
+
[dataSource, getRowId]
|
|
1080
|
+
);
|
|
1081
|
+
const definedColumns = useMemo2(() => {
|
|
422
1082
|
if (columns && columns.length !== 0) return columns;
|
|
423
1083
|
if (dataSource && dataSource.length > 0)
|
|
424
1084
|
return defineColumnsFromRows(dataSource);
|
|
425
1085
|
return [];
|
|
426
1086
|
}, [columns, dataSource]);
|
|
427
|
-
const orderedColumns =
|
|
1087
|
+
const orderedColumns = useMemo2(
|
|
428
1088
|
() => applyColumnOrder(definedColumns, order),
|
|
429
1089
|
[definedColumns, order]
|
|
430
1090
|
);
|
|
431
|
-
const resolved =
|
|
1091
|
+
const resolved = useMemo2(() => {
|
|
432
1092
|
const widths = resolveColumnWidths(orderedColumns, sizing, {
|
|
433
1093
|
sizes: columnSizeDefaults,
|
|
434
1094
|
resizable: resizableColumns,
|
|
@@ -444,14 +1104,41 @@ function DataGridComponent({
|
|
|
444
1104
|
resizableColumns,
|
|
445
1105
|
reorderableColumns
|
|
446
1106
|
]);
|
|
1107
|
+
const shownRows = useMemo2(
|
|
1108
|
+
() => resolveShownRows(rows, filter, sort, resolved),
|
|
1109
|
+
[rows, filter, sort, resolved]
|
|
1110
|
+
);
|
|
1111
|
+
function columnName(columnId) {
|
|
1112
|
+
const entry = resolved.find((candidate) => candidate.id === columnId);
|
|
1113
|
+
if (entry === void 0) {
|
|
1114
|
+
return columnId;
|
|
1115
|
+
}
|
|
1116
|
+
return typeof entry.label === "string" ? entry.label : entry.column.field;
|
|
1117
|
+
}
|
|
1118
|
+
function announce(message) {
|
|
1119
|
+
setAnnouncement(message);
|
|
1120
|
+
}
|
|
1121
|
+
function handleColumnResize(event) {
|
|
1122
|
+
onColumnResize?.(event);
|
|
1123
|
+
if (event.phase === "end") {
|
|
1124
|
+
announce(
|
|
1125
|
+
`${columnName(event.columnId)}, ${String(Math.round(event.width))} pixels wide`
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
447
1129
|
const resize = useColumnResize({
|
|
448
1130
|
tableRef,
|
|
449
1131
|
sizing,
|
|
450
1132
|
setSizing,
|
|
451
1133
|
columnSizeDefaults,
|
|
452
|
-
onColumnResize
|
|
1134
|
+
onColumnResize: handleColumnResize
|
|
1135
|
+
});
|
|
1136
|
+
const nav = useGridNavigation({
|
|
1137
|
+
tableRef,
|
|
1138
|
+
rowCount: shownRows.length,
|
|
1139
|
+
columnCount: resolved.length
|
|
453
1140
|
});
|
|
454
|
-
const displayedIds =
|
|
1141
|
+
const displayedIds = useMemo2(
|
|
455
1142
|
() => orderedColumns.map((column) => column.id ?? column.field),
|
|
456
1143
|
[orderedColumns]
|
|
457
1144
|
);
|
|
@@ -460,36 +1147,115 @@ function DataGridComponent({
|
|
|
460
1147
|
if (next === displayedIds) {
|
|
461
1148
|
return;
|
|
462
1149
|
}
|
|
1150
|
+
const position = next.indexOf(movedId);
|
|
463
1151
|
setOrder(next);
|
|
464
1152
|
onColumnOrderChange?.({ columnId: movedId, order: next });
|
|
1153
|
+
announce(
|
|
1154
|
+
`${columnName(movedId)}, column ${String(position + 1)} of ${String(next.length)}`
|
|
1155
|
+
);
|
|
1156
|
+
nav.focusCell(HEADER_ROW, position);
|
|
465
1157
|
}
|
|
466
1158
|
const drag = useColumnDrag({ order: displayedIds, onDrop: handleDrop });
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
hoverRows ? "" : "no-hover-rows",
|
|
476
|
-
hoverColumns ? "" : "no-hover-columns",
|
|
477
|
-
hoverCells ? "" : "no-hover-cells"
|
|
478
|
-
].filter(Boolean).join(" "),
|
|
479
|
-
children: [
|
|
480
|
-
/* @__PURE__ */ jsx3("colgroup", { children: resolved.map((entry) => /* @__PURE__ */ jsx3("col", { style: { width: entry.width } }, entry.id)) }),
|
|
481
|
-
/* @__PURE__ */ jsx3(GridHeader, { columns: resolved, resize, drag }),
|
|
482
|
-
/* @__PURE__ */ jsx3(
|
|
483
|
-
GridBody,
|
|
484
|
-
{
|
|
485
|
-
columns: resolved,
|
|
486
|
-
dataSource,
|
|
487
|
-
activeColumnId: resize.activeColumnId
|
|
488
|
-
}
|
|
489
|
-
)
|
|
490
|
-
]
|
|
1159
|
+
function handleColumnSortChange(event) {
|
|
1160
|
+
onColumnSortChange?.(event);
|
|
1161
|
+
const entry = event.sort.find(
|
|
1162
|
+
(candidate) => candidate.columnId === event.columnId
|
|
1163
|
+
);
|
|
1164
|
+
if (entry === void 0) {
|
|
1165
|
+
announce(`${columnName(event.columnId)}, sort cleared`);
|
|
1166
|
+
return;
|
|
491
1167
|
}
|
|
492
|
-
|
|
1168
|
+
const direction = entry.direction === "asc" ? "ascending" : "descending";
|
|
1169
|
+
announce(
|
|
1170
|
+
event.sort.length > 1 ? `${columnName(event.columnId)} sorted ${direction}, key ${String(event.sort.indexOf(entry) + 1)} of ${String(event.sort.length)}` : `${columnName(event.columnId)} sorted ${direction}`
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
const columnSort = useColumnSort({
|
|
1174
|
+
sort,
|
|
1175
|
+
setSort,
|
|
1176
|
+
onColumnSortChange: handleColumnSortChange
|
|
1177
|
+
});
|
|
1178
|
+
const selection = useGridSelection({
|
|
1179
|
+
rows: shownRows,
|
|
1180
|
+
columns: resolved,
|
|
1181
|
+
selectable,
|
|
1182
|
+
rowSelection,
|
|
1183
|
+
setRowSelection,
|
|
1184
|
+
columnSelection,
|
|
1185
|
+
setColumnSelection,
|
|
1186
|
+
cellSelection,
|
|
1187
|
+
setCellSelection,
|
|
1188
|
+
callbacks,
|
|
1189
|
+
announce
|
|
1190
|
+
});
|
|
1191
|
+
const multiselectable = selection.rowMode === "multiple" || selection.columnMode === "multiple";
|
|
1192
|
+
return /* @__PURE__ */ jsxs2("div", { className: "gridkit-data-grid-viewport", ref: viewportRef, children: [
|
|
1193
|
+
/* @__PURE__ */ jsxs2(
|
|
1194
|
+
"table",
|
|
1195
|
+
{
|
|
1196
|
+
ref: tableRef,
|
|
1197
|
+
role: "grid",
|
|
1198
|
+
"aria-rowcount": shownRows.length + 1,
|
|
1199
|
+
"aria-colcount": resolved.length,
|
|
1200
|
+
...multiselectable && { "aria-multiselectable": true },
|
|
1201
|
+
...labelledBy !== void 0 && { "aria-labelledby": labelledBy },
|
|
1202
|
+
...labelledBy === void 0 && label !== void 0 && { "aria-label": label },
|
|
1203
|
+
onKeyDown: (event) => {
|
|
1204
|
+
if (event.key === "Escape") {
|
|
1205
|
+
if (drag.draggedColumnId !== null || resize.activeColumnId !== null)
|
|
1206
|
+
return;
|
|
1207
|
+
selection.clear();
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
if ((event.ctrlKey || event.metaKey) && (event.key === "a" || event.key === "A")) {
|
|
1211
|
+
if (selection.rowMode !== "multiple") return;
|
|
1212
|
+
event.preventDefault();
|
|
1213
|
+
selection.selectAllRows();
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
style: { width: totalColumnWidth(resolved) },
|
|
1217
|
+
className: [
|
|
1218
|
+
"gridkit-data-grid",
|
|
1219
|
+
borders ? `borders-${borders}` : "",
|
|
1220
|
+
// Hover is on by default and selection off, so one set of classes
|
|
1221
|
+
// turns styling off and the other turns it on. The polarity differs
|
|
1222
|
+
// because the defaults do.
|
|
1223
|
+
hoverRows ? "" : "no-hover-rows",
|
|
1224
|
+
hoverColumns ? "" : "no-hover-columns",
|
|
1225
|
+
hoverCells ? "" : "no-hover-cells",
|
|
1226
|
+
selection.rowMode === false ? "" : "selectable-rows",
|
|
1227
|
+
selection.columnMode === false ? "" : "selectable-columns",
|
|
1228
|
+
selection.cellMode === false ? "" : "selectable-cells"
|
|
1229
|
+
].filter(Boolean).join(" "),
|
|
1230
|
+
children: [
|
|
1231
|
+
/* @__PURE__ */ jsx4("colgroup", { children: resolved.map((entry) => /* @__PURE__ */ jsx4("col", { style: { width: entry.width } }, entry.id)) }),
|
|
1232
|
+
/* @__PURE__ */ jsx4(
|
|
1233
|
+
GridHeader,
|
|
1234
|
+
{
|
|
1235
|
+
columns: resolved,
|
|
1236
|
+
resize,
|
|
1237
|
+
drag,
|
|
1238
|
+
sort: columnSort,
|
|
1239
|
+
sortableColumns,
|
|
1240
|
+
nav,
|
|
1241
|
+
selection
|
|
1242
|
+
}
|
|
1243
|
+
),
|
|
1244
|
+
/* @__PURE__ */ jsx4(
|
|
1245
|
+
GridBody,
|
|
1246
|
+
{
|
|
1247
|
+
columns: resolved,
|
|
1248
|
+
rows: shownRows,
|
|
1249
|
+
activeColumnId: resize.activeColumnId,
|
|
1250
|
+
nav,
|
|
1251
|
+
selection
|
|
1252
|
+
}
|
|
1253
|
+
)
|
|
1254
|
+
]
|
|
1255
|
+
}
|
|
1256
|
+
),
|
|
1257
|
+
/* @__PURE__ */ jsx4("div", { className: "gridkit-sr-only", role: "status", "aria-live": "polite", children: announcement })
|
|
1258
|
+
] });
|
|
493
1259
|
}
|
|
494
1260
|
export {
|
|
495
1261
|
DataGridComponent
|