@dxos/react-ui-table 0.8.4-main.fd6878d → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/chunk-4KOQTPFC.mjs +1702 -0
  3. package/dist/lib/browser/chunk-4KOQTPFC.mjs.map +7 -0
  4. package/dist/lib/browser/chunk-62PYOJPY.mjs +67 -0
  5. package/dist/lib/browser/chunk-62PYOJPY.mjs.map +7 -0
  6. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  7. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  8. package/dist/lib/browser/index.mjs +1151 -1175
  9. package/dist/lib/browser/index.mjs.map +4 -4
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/testing/index.mjs +37 -45
  12. package/dist/lib/browser/testing/index.mjs.map +3 -3
  13. package/dist/lib/browser/translations.mjs +36 -0
  14. package/dist/lib/browser/translations.mjs.map +7 -0
  15. package/dist/lib/browser/types/index.mjs +4 -3
  16. package/dist/lib/node-esm/chunk-7LMOZUTS.mjs +68 -0
  17. package/dist/lib/node-esm/chunk-7LMOZUTS.mjs.map +7 -0
  18. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  19. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  20. package/dist/lib/node-esm/chunk-KOSV2J5N.mjs +1704 -0
  21. package/dist/lib/node-esm/chunk-KOSV2J5N.mjs.map +7 -0
  22. package/dist/lib/node-esm/index.mjs +1151 -1175
  23. package/dist/lib/node-esm/index.mjs.map +4 -4
  24. package/dist/lib/node-esm/meta.json +1 -1
  25. package/dist/lib/node-esm/testing/index.mjs +37 -45
  26. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  27. package/dist/lib/node-esm/translations.mjs +37 -0
  28. package/dist/lib/node-esm/translations.mjs.map +7 -0
  29. package/dist/lib/node-esm/types/index.mjs +4 -3
  30. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
  31. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  32. package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
  33. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  34. package/dist/types/src/components/Table/DynamicTable.d.ts +6 -7
  35. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  36. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +36 -3
  37. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/Table/Relations.stories.d.ts +112 -5
  39. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  40. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  41. package/dist/types/src/components/Table/Table.d.ts +21 -19
  42. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  43. package/dist/types/src/components/Table/Table.stories.d.ts +115 -5
  44. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/Table/TableContent.d.ts +18 -0
  46. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
  47. package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
  48. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
  49. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
  50. package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
  51. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +12 -9
  52. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  53. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +7 -14
  54. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  55. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +38 -5
  56. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  57. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  58. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  59. package/dist/types/src/components/index.d.ts +0 -1
  60. package/dist/types/src/components/index.d.ts.map +1 -1
  61. package/dist/types/src/components/testing.d.ts +23 -16
  62. package/dist/types/src/components/testing.d.ts.map +1 -1
  63. package/dist/types/src/hooks/index.d.ts +2 -1
  64. package/dist/types/src/hooks/index.d.ts.map +1 -1
  65. package/dist/types/src/hooks/useAddRow.d.ts +6 -6
  66. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  67. package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
  68. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  69. package/dist/types/src/hooks/useTableModel.d.ts +9 -9
  70. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  71. package/dist/types/src/index.d.ts +0 -2
  72. package/dist/types/src/index.d.ts.map +1 -1
  73. package/dist/types/src/model/modal-controller.d.ts +5 -8
  74. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  75. package/dist/types/src/model/selection-model.d.ts +18 -9
  76. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  77. package/dist/types/src/model/table-model.d.ts +111 -30
  78. package/dist/types/src/model/table-model.d.ts.map +1 -1
  79. package/dist/types/src/model/table-presentation.d.ts +4 -2
  80. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  81. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  82. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  83. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  84. package/dist/types/src/testing/useTestTableModel.d.ts +18 -34
  85. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  86. package/dist/types/src/translations.d.ts +21 -23
  87. package/dist/types/src/translations.d.ts.map +1 -1
  88. package/dist/types/src/types/Table.d.ts +32 -0
  89. package/dist/types/src/types/Table.d.ts.map +1 -0
  90. package/dist/types/src/types/index.d.ts +1 -1
  91. package/dist/types/src/types/index.d.ts.map +1 -1
  92. package/dist/types/src/util/dynamic-table.d.ts +14 -12
  93. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  94. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  95. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  96. package/dist/types/src/util/index.d.ts +2 -1
  97. package/dist/types/src/util/index.d.ts.map +1 -1
  98. package/dist/types/src/util/query-ast.d.ts +7 -0
  99. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  100. package/dist/types/src/util/schema.d.ts +1 -1
  101. package/dist/types/src/util/schema.d.ts.map +1 -1
  102. package/dist/types/src/util/signals.d.ts.map +1 -1
  103. package/dist/types/src/util/sort.d.ts +12 -0
  104. package/dist/types/src/util/sort.d.ts.map +1 -0
  105. package/dist/types/src/util/table-buttons.d.ts +20 -20
  106. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  107. package/dist/types/src/util/table-controls.d.ts +4 -4
  108. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  109. package/dist/types/src/util/tag.d.ts.map +1 -1
  110. package/dist/types/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +68 -77
  112. package/src/components/Table/ColumnActionsMenu.tsx +17 -12
  113. package/src/components/Table/ColumnSettings.tsx +8 -9
  114. package/src/components/Table/DynamicTable.stories.tsx +41 -38
  115. package/src/components/Table/DynamicTable.tsx +32 -36
  116. package/src/components/Table/Relations.stories.tsx +219 -61
  117. package/src/components/Table/RowActionsMenu.tsx +27 -17
  118. package/src/components/Table/Table.stories.tsx +217 -141
  119. package/src/components/Table/Table.tsx +46 -391
  120. package/src/components/Table/TableContent.tsx +426 -0
  121. package/src/components/Table/TableToolbar.tsx +111 -0
  122. package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -6
  123. package/src/components/TableCellEditor/FormCellEditor.tsx +136 -41
  124. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +39 -73
  125. package/src/components/TableCellEditor/TableCellEditor.tsx +81 -223
  126. package/src/components/TableCellEditor/index.ts +1 -0
  127. package/src/components/index.ts +0 -1
  128. package/src/components/testing.ts +42 -21
  129. package/src/hooks/index.ts +2 -1
  130. package/src/hooks/useAddRow.ts +14 -14
  131. package/src/hooks/useProjectionModel.ts +38 -0
  132. package/src/hooks/useTableModel.ts +36 -34
  133. package/src/index.ts +0 -4
  134. package/src/model/modal-controller.ts +23 -40
  135. package/src/model/selection-model.ts +75 -44
  136. package/src/model/table-model.test.ts +86 -56
  137. package/src/model/table-model.ts +464 -182
  138. package/src/model/table-presentation.test.ts +64 -59
  139. package/src/model/table-presentation.ts +107 -83
  140. package/src/playwright/TableManager.ts +13 -6
  141. package/src/playwright/playwright.config.ts +1 -1
  142. package/src/playwright/smoke.spec.ts +92 -46
  143. package/src/testing/useTestTableModel.ts +51 -42
  144. package/src/translations.ts +20 -22
  145. package/src/types/Table.ts +80 -0
  146. package/src/types/index.ts +1 -1
  147. package/src/util/dynamic-table.test.ts +63 -0
  148. package/src/util/dynamic-table.ts +53 -39
  149. package/src/util/index.ts +2 -1
  150. package/src/util/query-ast.ts +22 -0
  151. package/src/util/schema.ts +4 -4
  152. package/src/util/sort.ts +90 -0
  153. package/src/util/table-buttons.ts +3 -3
  154. package/src/util/table-controls.ts +2 -2
  155. package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
  156. package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
  157. package/dist/lib/browser/chunk-ZAOSIG3L.mjs +0 -1814
  158. package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +0 -7
  159. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
  160. package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
  161. package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs +0 -1815
  162. package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +0 -7
  163. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  164. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  165. package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
  166. package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
  167. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
  168. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
  169. package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
  170. package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
  171. package/dist/types/src/model/table-sorting.d.ts +0 -53
  172. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  173. package/dist/types/src/types/schema.d.ts +0 -8
  174. package/dist/types/src/types/schema.d.ts.map +0 -1
  175. package/dist/types/src/util/table.d.ts +0 -12
  176. package/dist/types/src/util/table.d.ts.map +0 -1
  177. package/src/components/Table/CreateRefPanel.tsx +0 -86
  178. package/src/components/TableCellEditor/extension.ts +0 -61
  179. package/src/components/TableToolbar/TableToolbar.tsx +0 -102
  180. package/src/components/TableToolbar/index.ts +0 -5
  181. package/src/model/table-sorting.ts +0 -216
  182. package/src/types/schema.ts +0 -16
  183. package/src/util/table.ts +0 -53
@@ -2,420 +2,75 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { type Schema } from 'effect/Schema';
6
- import React, {
7
- type MouseEvent,
8
- type PropsWithChildren,
9
- type WheelEvent,
10
- forwardRef,
11
- useCallback,
12
- useEffect,
13
- useImperativeHandle,
14
- useMemo,
15
- useState,
16
- } 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';
17
8
 
18
- import { type Client } from '@dxos/client';
19
- import { Filter } from '@dxos/echo';
20
- import { getValue } from '@dxos/echo-schema';
21
- import { invariant } from '@dxos/invariant';
22
- // TODO(wittjosiah): Remove dependency on react-client.
23
- import { getSpace } from '@dxos/react-client/echo';
24
- import { useTranslation } from '@dxos/react-ui';
25
- import { useAttention } from '@dxos/react-ui-attention';
26
- import {
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 { mx } from '@dxos/react-ui-theme';
38
- import { isNotFalsy, safeParseInt } from '@dxos/util';
9
+ import { type DxGridAxisMeta, type DxGridPosition } from '@dxos/react-ui-grid';
39
10
 
40
- import { ModalController, type TableModel, type TablePresentation } from '../../model';
41
- import { translationKey } from '../../translations';
42
- import { tableButtons, tableControls } from '../../util';
43
- import { type TableCellEditorProps, TableValueEditor, createOption } from '../TableCellEditor';
11
+ import { type InsertRowResult } from '../../model';
12
+ import { TableContent } from './TableContent';
13
+ import { TableToolbar } from './TableToolbar';
44
14
 
45
- import { ColumnActionsMenu } from './ColumnActionsMenu';
46
- import { ColumnSettings } from './ColumnSettings';
47
- import { CreateRefPanel } from './CreateRefPanel';
48
- import { RowActionsMenu } from './RowActionsMenu';
15
+ const columnDefault = { grid: { minSize: 80, maxSize: 640 } };
16
+ const rowDefault = { frozenRowsStart: { readonly: true, focusUnfurl: false } };
17
+ const emptyColumnMeta = Atom.make<DxGridAxisMeta>({ grid: {} });
49
18
 
50
19
  //
51
- // Table.Root
20
+ // Context
52
21
  //
53
22
 
54
- export type TableRootProps = PropsWithChildren<{ role?: string }>;
55
-
56
- const TableRoot = ({ children, role = 'article' }: TableRootProps) => {
57
- return (
58
- <div
59
- role='none'
60
- className={mx(
61
- 'relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]',
62
- role === 'card--popover' && 'popover-card-height',
63
- role === 'section' && 'attention-surface',
64
- role === 'card--intrinsic' && '[&_.dx-grid]:bs-[--dx-grid-content-block-size]',
65
- ['card--popover', 'section', 'card--extrinsic'].includes(role) && 'overflow-hidden',
66
- ['article', 'slide'].includes(role) && 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0',
67
- )}
68
- >
69
- {children}
70
- </div>
71
- );
23
+ type TableContextValue = {
24
+ /** Mutable ref populated by Content so Root can expose the controller. */
25
+ controllerRef: RefObject<TableController>;
72
26
  };
73
27
 
28
+ const [TableContextProvider, useTableContext] = createContext<TableContextValue>('Table');
29
+
74
30
  //
75
- // Table.Main
31
+ // Controller
76
32
  //
77
33
 
78
- export type TableController = {
34
+ type TableController = {
79
35
  update?: (cell?: DxGridPosition) => void;
80
- focusDraft?: () => void;
81
- };
82
-
83
- export type TableMainProps = {
84
- model?: TableModel;
85
- presentation?: TablePresentation;
86
- schema?: Schema.AnyNoContext;
87
- client?: Client;
88
- // TODO(burdon): Rename since attention isn't a useful concept here? Standardize across other components. Pass property into useAttention.
89
- ignoreAttention?: boolean;
90
- onRowClick?: (row: any) => void;
36
+ handleInsertRowResult?: (insertRowResult?: InsertRowResult) => void;
91
37
  };
92
38
 
93
- const TableMain = forwardRef<TableController, TableMainProps>(
94
- ({ model, presentation, ignoreAttention, schema, client, onRowClick }, forwardedRef) => {
95
- const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
96
- const { hasAttention } = useAttention(model?.id ?? 'table');
97
- const { t } = useTranslation(translationKey);
98
- const modals = useMemo(() => new ModalController(), []);
99
-
100
- const draftRowCount = model?.getDraftRowCount() ?? 0;
101
-
102
- const frozen = useMemo(() => {
103
- const noActionColumn =
104
- model?.features.dataEditable === false &&
105
- model?.features.schemaEditable === false &&
106
- model.rowActions.length === 0;
107
-
108
- return {
109
- frozenRowsStart: 1,
110
- frozenRowsEnd: draftRowCount,
111
- frozenColsStart: model?.features.selection.enabled ? 1 : 0,
112
- frozenColsEnd: noActionColumn ? 0 : 1,
113
- };
114
- }, [model, draftRowCount]);
115
-
116
- const getCells = useCallback<NonNullable<GridContentProps['getCells']>>(
117
- (range: DxGridPlaneRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
118
- [presentation],
119
- );
120
-
121
- useEffect(() => {
122
- if (!presentation || !dxGrid) {
123
- return;
124
- }
125
- dxGrid.getCells = getCells;
126
- }, [presentation, dxGrid, getCells]);
127
-
128
- /**
129
- * Provides an external controller that can be called to repaint the table.
130
- */
131
- useImperativeHandle<TableController, TableController>(forwardedRef, () => {
132
- if (!presentation || !dxGrid) {
133
- return {};
134
- }
135
-
136
- return {
137
- update: (cell) => {
138
- if (cell) {
139
- dxGrid.updateIfWithinBounds(cell, true);
140
- } else {
141
- dxGrid.updateCells(true);
142
- dxGrid.requestUpdate();
143
- }
144
- },
145
- focusDraft: () => {
146
- requestAnimationFrame(() => {
147
- dxGrid.setFocus({ plane: 'frozenRowsEnd', col: 0, row: 0 });
148
- dxGrid.refocus();
149
- });
150
- },
151
- };
152
- }, [presentation, dxGrid]);
153
-
154
- const handleSaveDraftRow = useCallback(
155
- (rowIndex = 0) => {
156
- if (model && dxGrid) {
157
- const didCommitSuccessfully = model.commitDraftRow(rowIndex);
158
- if (didCommitSuccessfully) {
159
- requestAnimationFrame(() => {
160
- dxGrid.scrollToEndRow();
161
- });
162
- }
163
- }
164
- },
165
- [model, dxGrid],
166
- );
167
-
168
- const handleGridClick = useCallback(
169
- (event: MouseEvent) => {
170
- const rowIndex = safeParseInt((event.target as HTMLElement).closest('[aria-rowindex]')?.ariaRowIndex ?? '');
171
- if (rowIndex != null) {
172
- if (onRowClick) {
173
- const row = model?.getRowAt(rowIndex);
174
- row && onRowClick(row);
175
- }
176
-
177
- if (model?.features.selection.enabled && model?.selection.selectionMode === 'single') {
178
- model.selection.toggleSelectionForRowIndex(rowIndex);
179
- }
180
- }
181
-
182
- if (!modals) {
183
- return;
184
- }
185
-
186
- const buttonSelector = Object.values(tableButtons)
187
- .map((button) => `button[${button.attr}]`)
188
- .join(',');
189
-
190
- const tableButtonElement = (event.target as HTMLElement).closest(buttonSelector) as HTMLElement;
191
- if (tableButtonElement) {
192
- const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
193
- if (!button) {
194
- return;
195
- }
196
-
197
- modals.setTrigger(tableButtonElement);
198
- const data = button.getData(tableButtonElement);
199
- switch (data.type) {
200
- case 'rowMenu': {
201
- modals.showRowMenu(data.rowIndex);
202
- break;
203
- }
204
- case 'columnSettings': {
205
- modals.showColumnMenu(data.fieldId);
206
- break;
207
- }
208
- case 'newColumn': {
209
- modals.showColumnCreator();
210
- break;
211
- }
212
- case 'sort': {
213
- model?.sorting?.toggleSort(data.fieldId);
214
- break;
215
- }
216
- case 'saveDraftRow': {
217
- handleSaveDraftRow();
218
- break;
219
- }
220
- }
221
- return;
222
- }
223
-
224
- const controlSelector = Object.values(tableControls)
225
- .map((control) => `input[${control.attr}]`)
226
- .join(',');
227
-
228
- const controlElement = (event.target as HTMLElement).closest(controlSelector) as HTMLElement;
229
- if (controlElement) {
230
- const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
231
- if (!control) {
232
- return;
233
- }
234
-
235
- const data = control.getData(controlElement);
236
- switch (data.type) {
237
- case 'checkbox': {
238
- if (data.header) {
239
- model?.selection.setSelection(model.selection.allRowsSeleted.value ? 'none' : 'all');
240
- } else {
241
- model?.selection.toggleSelectionForRowIndex(data.rowIndex);
242
- }
243
- break;
244
- }
245
- case 'switch': {
246
- if (model) {
247
- model.updateCellData({ row: data.rowIndex, col: data.colIndex }, (value) => !value);
248
- }
249
- break;
250
- }
251
- }
252
- }
253
- },
254
- [model, modals, dxGrid],
255
- );
256
-
257
- const handleFocus = useCallback<NonNullable<TableCellEditorProps['onFocus']>>(
258
- (increment, delta, cell) => {
259
- if (dxGrid && model) {
260
- if (cell?.plane === 'grid' && cell?.row >= model.getRowCount() - 1) {
261
- if (draftRowCount < 1) {
262
- model.insertRow();
263
- }
264
- dxGrid.setFocus({ plane: 'frozenRowsEnd', col: 0, row: 0 });
265
- } else if (cell?.plane === 'frozenRowsEnd' && increment === 'row') {
266
- handleSaveDraftRow(cell.row);
267
- model.insertRow();
268
- dxGrid.setFocus({ plane: 'frozenRowsEnd', col: 0, row: 0 });
269
- } else {
270
- dxGrid.refocus(increment, delta);
271
- }
272
- }
273
- },
274
- [dxGrid, model],
275
- );
276
-
277
- const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
278
- (event) => {
279
- if (!model?.features.dataEditable) {
280
- return;
281
- }
282
-
283
- const cell = closestCell(event.target);
284
- if (!model || !cell) {
285
- return;
286
- }
287
-
288
- switch (event.key) {
289
- case 'Backspace':
290
- case 'Delete': {
291
- try {
292
- model.setCellData(cell, undefined);
293
- event.preventDefault();
294
- } catch {
295
- // Delete results in a validation error; don’t prevent default so dx-grid can emit an edit request.
296
- }
297
- break;
298
- }
299
- }
300
- },
301
- [model],
302
- );
303
-
304
- const handleAxisResize = useCallback<NonNullable<GridContentProps['onAxisResize']>>(
305
- (event) => {
306
- if (event.axis === 'col') {
307
- const columnIndex = parseInt(event.index, 10);
308
- model?.setColumnWidth(columnIndex, event.size);
309
- }
310
- },
311
- [model],
312
- );
313
-
314
- const handleWheel = useCallback(
315
- (event: WheelEvent) => {
316
- if (!ignoreAttention && !hasAttention) {
317
- event.stopPropagation();
318
- }
319
- },
320
- [hasAttention, ignoreAttention],
321
- );
322
-
323
- const handleNewColumn = useCallback(() => {
324
- const columns = model?.getColumnCount();
325
- if (dxGrid && columns) {
326
- dxGrid.scrollToColumn(columns - 1);
327
- }
328
- }, [model, dxGrid]);
329
-
330
- // TODO(burdon): Factor out?
331
- // TODO(burdon): Generalize to handle other value types (e.g., enums).
332
- const handleQuery = useCallback<NonNullable<TableCellEditorProps['onQuery']>>(
333
- async ({ field, props }, text) => {
334
- if (model && props.referenceSchema && field.referencePath) {
335
- const space = getSpace(model.view);
336
- invariant(space);
337
-
338
- let schema;
339
- if (client) {
340
- schema = client.graph.schemaRegistry.getSchema(props.referenceSchema);
341
- }
342
- if (!schema) {
343
- schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
344
- }
39
+ //
40
+ // Root
41
+ //
345
42
 
346
- if (schema) {
347
- const { objects } = await space.db.query(Filter.type(schema)).run();
348
- const options = objects
349
- .map((obj) => {
350
- const value = getValue(obj, field.referencePath!);
351
- if (!value || typeof value !== 'string') {
352
- return undefined;
353
- }
43
+ const TABLE_ROOT_NAME = 'Table.Root';
354
44
 
355
- return {
356
- label: value,
357
- data: obj,
358
- };
359
- })
360
- .filter(isNotFalsy);
45
+ type TableRootProps = PropsWithChildren;
361
46
 
362
- return [
363
- ...options,
364
- {
365
- label: t('create new object label', { text }),
366
- data: createOption(text),
367
- },
368
- ];
369
- }
370
- }
47
+ const TableRoot = forwardRef<TableController, TableRootProps>(({ children }, forwardedRef) => {
48
+ const controllerRef = useRef<TableController>({});
371
49
 
372
- return [];
373
- },
374
- [model, client, t],
375
- );
50
+ useImperativeHandle(
51
+ forwardedRef,
52
+ () => ({
53
+ update: (cell) => controllerRef.current?.update?.(cell),
54
+ handleInsertRowResult: (result) => controllerRef.current?.handleInsertRowResult?.(result),
55
+ }),
56
+ [],
57
+ );
376
58
 
377
- const handleSave = useCallback(() => {
378
- dxGrid?.updateCells(true);
379
- dxGrid?.requestUpdate();
380
- }, [dxGrid]);
59
+ return <TableContextProvider controllerRef={controllerRef}>{children}</TableContextProvider>;
60
+ });
381
61
 
382
- if (!model || !modals) {
383
- return <span role='none' className='attention-surface' />;
384
- }
62
+ TableRoot.displayName = TABLE_ROOT_NAME;
385
63
 
386
- return (
387
- <Grid.Root id={model.id ?? 'table-grid'}>
388
- <TableValueEditor
389
- model={model}
390
- modals={modals}
391
- schema={schema}
392
- onFocus={handleFocus}
393
- onQuery={handleQuery}
394
- onSave={handleSave}
395
- />
396
- <Grid.Content
397
- className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
398
- frozen={frozen}
399
- columns={model.columnMeta.value}
400
- limitRows={model.getRowCount() ?? 0}
401
- limitColumns={model.projection.fields.length}
402
- overscroll='trap'
403
- onAxisResize={handleAxisResize}
404
- onClick={handleGridClick}
405
- onKeyDownCapture={handleKeyDown}
406
- onWheelCapture={handleWheel}
407
- ref={setDxGrid}
408
- />
409
- <RowActionsMenu model={model} modals={modals} />
410
- <ColumnActionsMenu model={model} modals={modals} />
411
- <ColumnSettings model={model} modals={modals} onNewColumn={handleNewColumn} />
412
- <CreateRefPanel model={model} modals={modals} />
413
- </Grid.Root>
414
- );
415
- },
416
- );
64
+ //
65
+ // Table
66
+ //
417
67
 
418
68
  export const Table = {
419
69
  Root: TableRoot,
420
- Main: TableMain,
70
+ Toolbar: TableToolbar,
71
+ Content: TableContent,
421
72
  };
73
+
74
+ export type { TableController, TableRootProps };
75
+
76
+ export { useTableContext };