@dxos/react-ui-table 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/lib/browser/{chunk-OZBLY7XM.mjs → chunk-BF54SSNL.mjs} +12 -18
  2. package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-I5IFW7UF.mjs → chunk-EK6D2CVM.mjs} +37 -32
  4. package/dist/lib/browser/chunk-EK6D2CVM.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +200 -173
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +4 -4
  9. package/dist/lib/browser/testing/index.mjs.map +2 -2
  10. package/dist/lib/browser/types/index.mjs +1 -1
  11. package/dist/lib/node-esm/{chunk-DDJKYJFY.mjs → chunk-LYYDF6JP.mjs} +37 -32
  12. package/dist/lib/node-esm/chunk-LYYDF6JP.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-SIRDIPHZ.mjs} +12 -18
  14. package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +200 -173
  16. package/dist/lib/node-esm/index.mjs.map +4 -4
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +4 -4
  19. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  20. package/dist/lib/node-esm/types/index.mjs +1 -1
  21. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/DynamicTable.d.ts +5 -4
  23. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  24. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +20 -22
  25. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  26. package/dist/types/src/components/Table/Relations.stories.d.ts +80 -82
  27. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.d.ts +28 -21
  29. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.stories.d.ts +82 -84
  31. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/Table/TableContent.d.ts +21 -0
  33. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  34. package/dist/types/src/components/Table/TableToolbar.d.ts +25 -0
  35. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  36. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
  37. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
  39. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  40. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +22 -24
  41. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  42. package/dist/types/src/components/index.d.ts +0 -1
  43. package/dist/types/src/components/index.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useAddRow.d.ts +1 -1
  45. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
  47. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  48. package/dist/types/src/hooks/useTableModel.d.ts +1 -1
  49. package/dist/types/src/model/table-model.d.ts +5 -4
  50. package/dist/types/src/model/table-model.d.ts.map +1 -1
  51. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  52. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  53. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  54. package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
  55. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  56. package/dist/types/src/translations.d.ts +19 -21
  57. package/dist/types/src/translations.d.ts.map +1 -1
  58. package/dist/types/src/types/Table.d.ts +1 -6
  59. package/dist/types/src/types/Table.d.ts.map +1 -1
  60. package/dist/types/src/util/dynamic-table.d.ts +6 -5
  61. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  62. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  63. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  64. package/dist/types/src/util/query-ast.d.ts.map +1 -1
  65. package/dist/types/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +43 -43
  67. package/src/components/Table/ColumnActionsMenu.tsx +5 -5
  68. package/src/components/Table/ColumnSettings.tsx +3 -4
  69. package/src/components/Table/DynamicTable.stories.tsx +6 -5
  70. package/src/components/Table/DynamicTable.tsx +13 -19
  71. package/src/components/Table/Relations.stories.tsx +35 -25
  72. package/src/components/Table/Table.stories.tsx +61 -40
  73. package/src/components/Table/Table.tsx +41 -434
  74. package/src/components/Table/TableContent.tsx +422 -0
  75. package/src/components/{TableToolbar → Table}/TableToolbar.tsx +35 -42
  76. package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
  77. package/src/components/TableCellEditor/FormCellEditor.tsx +12 -6
  78. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +6 -5
  79. package/src/components/TableCellEditor/TableCellEditor.tsx +10 -5
  80. package/src/components/index.ts +0 -1
  81. package/src/components/testing.ts +1 -1
  82. package/src/hooks/useAddRow.ts +1 -1
  83. package/src/hooks/useProjectionModel.ts +3 -3
  84. package/src/hooks/useTableModel.ts +1 -1
  85. package/src/model/table-model.test.ts +5 -5
  86. package/src/model/table-model.ts +13 -12
  87. package/src/model/table-presentation.test.ts +5 -5
  88. package/src/model/table-presentation.ts +12 -11
  89. package/src/playwright/TableManager.ts +10 -3
  90. package/src/playwright/playwright.config.ts +1 -1
  91. package/src/playwright/smoke.spec.ts +45 -2
  92. package/src/testing/useTestTableModel.ts +4 -4
  93. package/src/translations.ts +19 -21
  94. package/src/types/Table.ts +11 -22
  95. package/src/util/dynamic-table.test.ts +70 -0
  96. package/src/util/dynamic-table.ts +17 -16
  97. package/src/util/query-ast.ts +3 -0
  98. package/src/util/table-buttons.ts +1 -1
  99. package/src/util/table-controls.ts +2 -2
  100. package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +0 -7
  101. package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
  102. package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +0 -7
  103. package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +0 -7
  104. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  105. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  106. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  107. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  108. package/src/components/TableToolbar/index.ts +0 -5
@@ -2,469 +2,76 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Atom, RegistryContext, useAtomValue } from '@effect-atom/atom-react';
6
- import * as String from 'effect/String';
7
- import React, {
8
- type JSX,
9
- type MouseEvent,
10
- type PropsWithChildren,
11
- type Ref,
12
- type WheelEvent,
13
- forwardRef,
14
- useCallback,
15
- useContext,
16
- useEffect,
17
- useImperativeHandle,
18
- useMemo,
19
- useState,
20
- } from 'react';
5
+ import { Atom } from '@effect-atom/atom-react';
6
+ import { createContext } from '@radix-ui/react-context';
7
+ import React, { type PropsWithChildren, type RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
21
8
 
22
- import { type Type } from '@dxos/echo';
23
- import { log } from '@dxos/log';
24
- import { useAttention } from '@dxos/react-ui-attention';
25
- import {
26
- type DxGridAxisMeta,
27
- type DxGridElement,
28
- type DxGridPlane,
29
- type DxGridPlaneRange,
30
- type DxGridPosition,
31
- Grid,
32
- type GridContentProps,
33
- closestCell,
34
- gridSeparatorBlockEnd,
35
- gridSeparatorInlineEnd,
36
- } from '@dxos/react-ui-grid';
37
- import { DxEditRequest } from '@dxos/react-ui-grid';
38
- import { mx } from '@dxos/ui-theme';
9
+ import { type DxGridAxisMeta, type DxGridPosition } from '@dxos/react-ui-grid';
39
10
 
40
- import { type InsertRowResult, ModalController, type TableModel, type TablePresentation } from '../../model';
41
- import { tableButtons, tableControls } from '../../util';
42
- import { type OnCreateHandler, type TableCellEditorProps, TableValueEditor } from '../TableCellEditor';
11
+ import { type InsertRowResult } from '../../model';
43
12
 
44
- import { ColumnActionsMenu } from './ColumnActionsMenu';
45
- import { ColumnSettings } from './ColumnSettings';
46
- import { RowActionsMenu } from './RowActionsMenu';
13
+ import { TableContent } from './TableContent';
14
+ import { TableToolbar } from './TableToolbar';
47
15
 
48
16
  const columnDefault = { grid: { minSize: 80, maxSize: 640 } };
49
17
  const rowDefault = { frozenRowsStart: { readonly: true, focusUnfurl: false } };
50
18
  const emptyColumnMeta = Atom.make<DxGridAxisMeta>({ grid: {} });
51
19
 
52
20
  //
53
- // Table.Root
21
+ // Context
54
22
  //
55
23
 
56
- export type TableRootProps = PropsWithChildren<{ role?: string }>;
57
-
58
- const TableRoot = ({ children, role = 'article' }: TableRootProps) => {
59
- return (
60
- <div
61
- role='none'
62
- className={mx(
63
- 'relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]',
64
- role === 'card--popover' && 'popover-card-height',
65
- role === 'section' && 'attention-surface',
66
- role === 'card--intrinsic' && '[&_.dx-grid]:bs-[--dx-grid-content-block-size]',
67
- ['card--popover', 'section', 'card--extrinsic'].includes(role) && 'overflow-hidden',
68
- ['article', 'slide'].includes(role) && 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0',
69
- )}
70
- >
71
- {children}
72
- </div>
73
- );
24
+ type TableContextValue = {
25
+ /** Mutable ref populated by Content so Root can expose the controller. */
26
+ controllerRef: RefObject<TableController>;
74
27
  };
75
28
 
29
+ const [TableContextProvider, useTableContext] = createContext<TableContextValue>('Table');
30
+
76
31
  //
77
- // Table.Main
32
+ // Controller
78
33
  //
79
34
 
80
- export type TableController = {
35
+ type TableController = {
81
36
  update?: (cell?: DxGridPosition) => void;
82
37
  handleInsertRowResult?: (insertRowResult?: InsertRowResult) => void;
83
38
  };
84
39
 
85
- export type TableMainProps<T extends Type.Entity.Any = Type.Entity.Any> = {
86
- schema?: T;
87
- model?: TableModel;
88
- presentation?: TablePresentation;
89
- // TODO(burdon): Rename since attention isn't a useful concept here? Standardize across other components. Pass property into useAttention.
90
- ignoreAttention?: boolean;
91
- onCreate?: OnCreateHandler;
92
- onRowClick?: (row: any) => void;
93
- testId?: string;
94
- };
95
-
96
- const emptyRowsAtom = Atom.make<unknown[]>([]);
97
- const emptyCellUpdateAtom = Atom.make<number>(0);
98
-
99
- const TableMainInner = <T extends Type.Entity.Any = Type.Entity.Any>(
100
- { schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId }: TableMainProps<T>,
101
- forwardedRef: Ref<TableController>,
102
- ) => {
103
- const registry = useContext(RegistryContext);
104
- const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
105
- const { hasAttention } = useAttention(model?.id ?? 'table');
106
- const modals = useMemo(() => new ModalController(registry), [registry]);
107
- const columnMeta = useAtomValue(model?.columnMeta ?? emptyColumnMeta);
108
- // Subscribe to rows atom to trigger re-render when rows change.
109
- const rows = useAtomValue(model?.rows ?? emptyRowsAtom);
110
- // Subscribe to cell update counter to trigger re-render when individual cells change.
111
- const cellUpdateCounter = useAtomValue(model?.cellUpdate ?? emptyCellUpdateAtom);
112
- // Derive column count from columnMeta (reactive) with fallback.
113
- const columnCount = Object.keys(columnMeta.grid).length || model?.projection.getFields().length || 0;
114
-
115
- const draftRowCount = model?.getDraftRowCount() ?? 0;
116
-
117
- const frozen = useMemo(() => {
118
- const noActionColumn =
119
- model?.features.dataEditable === false &&
120
- model?.features.schemaEditable === false &&
121
- model.rowActions.length === 0;
122
-
123
- return {
124
- frozenRowsStart: 1,
125
- frozenRowsEnd: Math.max(1, draftRowCount),
126
- frozenColsStart: model?.features.selection.enabled ? 1 : 0,
127
- frozenColsEnd: noActionColumn ? 0 : 1,
128
- };
129
- }, [model, draftRowCount]);
130
-
131
- const getCells = useCallback<NonNullable<GridContentProps['getCells']>>(
132
- (range: DxGridPlaneRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
133
- [presentation],
134
- );
135
-
136
- useEffect(() => {
137
- if (!dxGrid || !presentation) {
138
- return;
139
- }
140
-
141
- dxGrid.getCells = getCells;
142
- }, [dxGrid, presentation, getCells]);
143
-
144
- // Trigger grid update when rows or cell values change.
145
- useEffect(() => {
146
- if (!dxGrid) {
147
- return;
148
- }
149
-
150
- dxGrid.updateCells(true);
151
- dxGrid.requestUpdate();
152
- }, [dxGrid, rows, cellUpdateCounter]);
153
-
154
- const handleInsertRowResult = useCallback(
155
- (insertResult?: InsertRowResult) => {
156
- if (insertResult === 'draft') {
157
- requestAnimationFrame(() => {
158
- dxGrid?.setFocus({ plane: 'frozenRowsEnd', col: 0, row: 0 });
159
- dxGrid?.refocus();
160
- });
161
- } else {
162
- requestAnimationFrame(() => {
163
- dxGrid?.setFocus({ plane: 'grid', col: 0, row: model ? model.getRowCount() - 1 : 0 });
164
- dxGrid?.refocus();
165
- });
166
- }
167
- },
168
- [model, dxGrid],
169
- );
170
-
171
- const handleSave = useCallback(() => {
172
- dxGrid?.updateCells(true);
173
- dxGrid?.requestUpdate();
174
- }, [dxGrid]);
175
-
176
- const handleSaveDraftRow = useCallback(
177
- (rowIndex = 0) => {
178
- if (model && dxGrid) {
179
- const didCommitSuccessfully = model.commitDraftRow(rowIndex);
180
- if (didCommitSuccessfully) {
181
- requestAnimationFrame(() => {
182
- dxGrid.scrollToEndRow();
183
- });
184
- }
185
- }
186
- },
187
- [model, dxGrid],
188
- );
189
-
190
- const handleGridClick = useCallback(
191
- (event: MouseEvent) => {
192
- const cell = closestCell(event.target as HTMLElement);
193
- if (cell) {
194
- const { row: rowIndex, plane } = cell;
195
- if (onRowClick) {
196
- if (plane === 'grid') {
197
- const row = model?.getRowAt(rowIndex);
198
- row && onRowClick(row);
199
- } else {
200
- onRowClick(cell);
201
- }
202
- }
203
-
204
- if (model?.features.selection.enabled && model?.selection.selectionMode === 'single') {
205
- model.selection.toggleSelectionForRowIndex(rowIndex);
206
- }
207
- }
208
-
209
- if (!modals) {
210
- return;
211
- }
212
-
213
- const buttonSelector = Object.values(tableButtons)
214
- .map((button) => `button[${button.attr}]`)
215
- .join(',');
216
-
217
- const tableButtonElement = (event.target as HTMLElement).closest(buttonSelector) as HTMLElement;
218
- if (tableButtonElement) {
219
- const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
220
- if (!button) {
221
- return;
222
- }
223
-
224
- modals.setTrigger(tableButtonElement);
225
- const data = button.getData(tableButtonElement);
226
- switch (data.type) {
227
- case 'rowMenu': {
228
- modals.showRowMenu(data.rowIndex);
229
- break;
230
- }
231
- case 'columnSettings': {
232
- modals.showColumnMenu(data.fieldId);
233
- break;
234
- }
235
- case 'newColumn': {
236
- modals.showColumnCreator();
237
- break;
238
- }
239
- case 'sort': {
240
- model?.toggleSort(data.fieldId);
241
- break;
242
- }
243
- case 'saveDraftRow': {
244
- handleSaveDraftRow();
245
- break;
246
- }
247
- }
248
- return;
249
- }
250
-
251
- const controlSelector = Object.values(tableControls)
252
- .map((control) => `input[${control.attr}]`)
253
- .join(',');
254
-
255
- const controlElement = (event.target as HTMLElement).closest(controlSelector) as HTMLElement;
256
- if (controlElement) {
257
- const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
258
- if (!control) {
259
- return;
260
- }
261
-
262
- const data = control.getData(controlElement);
263
- switch (data.type) {
264
- case 'checkbox': {
265
- if (data.header) {
266
- model?.selection.setSelection(model.selection.allRowsSelected ? 'none' : 'all');
267
- } else {
268
- model?.selection.toggleSelectionForRowIndex(data.rowIndex);
269
- }
270
- break;
271
- }
272
- case 'switch': {
273
- if (model) {
274
- model.updateCellData({ row: data.rowIndex, col: data.colIndex }, (value) => !value);
275
- }
276
- break;
277
- }
278
- }
279
- }
280
- },
281
- [model, modals, dxGrid],
282
- );
283
-
284
- const handleFocus = useCallback<NonNullable<TableCellEditorProps['onFocus']>>(
285
- (increment, delta, cell) => {
286
- if (dxGrid && model) {
287
- if (cell?.plane === 'grid' && cell?.row >= model.getRowCount() - 1 && increment !== 'col') {
288
- handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : 'final');
289
- } else if (cell?.plane === 'frozenRowsEnd' && increment === 'row') {
290
- handleSaveDraftRow(cell.row);
291
- handleInsertRowResult(model.insertRow());
292
- } else {
293
- dxGrid.refocus(increment, delta);
294
- }
295
- }
296
- },
297
- [dxGrid, model],
298
- );
40
+ //
41
+ // Root
42
+ //
299
43
 
300
- const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
301
- (event) => {
302
- if (!model?.features.dataEditable) {
303
- return;
304
- }
44
+ const TABLE_ROOT_NAME = 'Table.Root';
305
45
 
306
- const cell = closestCell(event.target);
307
- if (!model || !cell) {
308
- return;
309
- }
46
+ type TableRootProps = PropsWithChildren;
310
47
 
311
- // Handle Meta+C (Copy) and Meta+V (Paste) commands
312
- if (event.metaKey || event.ctrlKey) {
313
- switch (event.key) {
314
- case 'c': {
315
- // Copy focused cell's text content to clipboard
316
- try {
317
- const cellData = model.getCellData(cell);
318
- const textContent = cellData?.toString() ?? '';
319
- void navigator.clipboard.writeText(textContent);
320
- event.preventDefault();
321
- } catch (error) {
322
- log.catch(error);
323
- }
324
- break;
325
- }
326
- case 'v': {
327
- // Paste clipboard content to focused cell
328
- event.preventDefault();
329
- void navigator.clipboard.readText().then((clipboardText) => {
330
- try {
331
- // Attempt to set the cell's content to clipboard content
332
- model.setCellData(cell, String.trim(clipboardText).replace(/[\n\r]+/, ' '));
333
- handleSave();
334
- } catch {
335
- // If validation fails, emit a DxEditRequest event with initialContent from clipboard
336
- // TODO(thure): Should `dx-grid` expose a method like this?
337
- const cellElement = (event.target as HTMLElement).closest(
338
- '[data-dx-grid-action="cell"]',
339
- ) as HTMLElement;
340
- if (cellElement) {
341
- const rect = cellElement.getBoundingClientRect();
342
- const editRequest = new DxEditRequest({
343
- cellIndex: `${cell.plane},${cell.col},${cell.row}`,
344
- cellBox: {
345
- insetInlineStart: rect.left,
346
- insetBlockStart: rect.top,
347
- inlineSize: rect.width,
348
- blockSize: rect.height,
349
- },
350
- cellElement,
351
- initialContent: clipboardText,
352
- });
353
- cellElement.dispatchEvent(editRequest);
354
- }
355
- }
356
- });
357
- break;
358
- }
359
- }
360
- }
48
+ const TableRoot = forwardRef<TableController, TableRootProps>(({ children }, forwardedRef) => {
49
+ const controllerRef = useRef<TableController>({});
361
50
 
362
- switch (event.key) {
363
- case 'Backspace':
364
- case 'Delete': {
365
- try {
366
- model.setCellData(cell, undefined);
367
- event.preventDefault();
368
- handleSave();
369
- } catch {
370
- // Delete results in a validation error; don’t prevent default so dx-grid can emit an edit request.
371
- }
372
- break;
373
- }
374
- }
375
- },
376
- [model, dxGrid, handleSave],
51
+ useImperativeHandle(
52
+ forwardedRef,
53
+ () => ({
54
+ update: (cell) => controllerRef.current?.update?.(cell),
55
+ handleInsertRowResult: (result) => controllerRef.current?.handleInsertRowResult?.(result),
56
+ }),
57
+ [],
377
58
  );
378
59
 
379
- const handleAxisResize = useCallback<NonNullable<GridContentProps['onAxisResize']>>(
380
- (event) => {
381
- if (event.axis === 'col') {
382
- const columnIndex = parseInt(event.index, 10);
383
- model?.setColumnWidth(columnIndex, event.size);
384
- }
385
- },
386
- [model],
387
- );
60
+ return <TableContextProvider controllerRef={controllerRef}>{children}</TableContextProvider>;
61
+ });
388
62
 
389
- const handleWheel = useCallback(
390
- (event: WheelEvent) => {
391
- if (!ignoreAttention && !hasAttention) {
392
- event.stopPropagation();
393
- }
394
- },
395
- [hasAttention, ignoreAttention],
396
- );
63
+ TableRoot.displayName = TABLE_ROOT_NAME;
397
64
 
398
- const handleNewColumn = useCallback(() => {
399
- const columns = model?.getColumnCount();
400
- if (dxGrid && columns) {
401
- dxGrid.scrollToColumn(columns - 1);
402
- }
403
- }, [model, dxGrid]);
404
-
405
- /**
406
- * Provides an external controller that can be called to repaint the table.
407
- */
408
- useImperativeHandle<TableController, TableController>(forwardedRef, () => {
409
- if (!dxGrid || !presentation) {
410
- return {};
411
- }
412
-
413
- return {
414
- update: (cell) => {
415
- if (cell) {
416
- dxGrid.updateIfWithinBounds(cell, true);
417
- } else {
418
- dxGrid.updateCells(true);
419
- dxGrid.requestUpdate();
420
- }
421
- },
422
- handleInsertRowResult,
423
- };
424
- }, [dxGrid, model, presentation]);
425
-
426
- if (!model || !modals) {
427
- return <span role='none' className='attention-surface' />;
428
- }
429
-
430
- return (
431
- <Grid.Root id={model.id ?? 'table-grid'}>
432
- <TableValueEditor<T>
433
- model={model}
434
- modals={modals}
435
- schema={schema}
436
- onFocus={handleFocus}
437
- onSave={handleSave}
438
- onCreate={onCreate}
439
- />
440
- <Grid.Content
441
- className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
442
- frozen={frozen}
443
- columns={columnMeta}
444
- columnDefault={columnDefault}
445
- rowDefault={rowDefault}
446
- limitRows={rows.length}
447
- limitColumns={columnCount}
448
- overscroll='trap'
449
- onAxisResize={handleAxisResize}
450
- onClick={handleGridClick}
451
- onKeyDownCapture={handleKeyDown}
452
- onWheelCapture={handleWheel}
453
- {...(testId && { 'data-testid': testId })}
454
- ref={setDxGrid}
455
- />
456
- <RowActionsMenu model={model} modals={modals} />
457
- <ColumnActionsMenu model={model} modals={modals} />
458
- <ColumnSettings model={model} modals={modals} onNewColumn={handleNewColumn} />
459
- </Grid.Root>
460
- );
461
- };
462
-
463
- const TableMain = forwardRef(TableMainInner) as <T extends Type.Entity.Any = Type.Entity.Any>(
464
- props: TableMainProps<T> & { ref?: Ref<TableController> },
465
- ) => JSX.Element;
65
+ //
66
+ // Table
67
+ //
466
68
 
467
69
  export const Table = {
468
70
  Root: TableRoot,
469
- Main: TableMain,
71
+ Toolbar: TableToolbar,
72
+ Content: TableContent,
470
73
  };
74
+
75
+ export type { TableController, TableRootProps };
76
+
77
+ export { useTableContext };