@dxos/react-ui-table 0.8.4-main.9735255 → 0.8.4-main.abd8ff62ef
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-OZBLY7XM.mjs → chunk-FBURZBPH.mjs} +17 -23
- package/dist/lib/browser/chunk-FBURZBPH.mjs.map +7 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/browser/{chunk-I5IFW7UF.mjs → chunk-YGC7LZJN.mjs} +39 -90
- package/dist/lib/browser/chunk-YGC7LZJN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +198 -235
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +7 -6
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/translations.mjs +36 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -1
- package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-DKKMSVKP.mjs} +17 -23
- package/dist/lib/node-esm/chunk-DKKMSVKP.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-DDJKYJFY.mjs → chunk-GKIWDEZ7.mjs} +39 -90
- package/dist/lib/node-esm/chunk-GKIWDEZ7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +198 -235
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +7 -6
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/translations.mjs +37 -0
- package/dist/lib/node-esm/translations.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +5 -4
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +20 -22
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +82 -82
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +20 -21
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +84 -84
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +17 -0
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +21 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +22 -24
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +7 -6
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +19 -21
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +1 -6
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +6 -5
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/src/util/signals.d.ts.map +1 -1
- package/dist/types/src/util/sort.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +20 -20
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +4 -4
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/tag.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +59 -62
- package/src/components/Table/ColumnActionsMenu.tsx +7 -6
- package/src/components/Table/ColumnSettings.tsx +3 -4
- package/src/components/Table/DynamicTable.stories.tsx +19 -18
- package/src/components/Table/DynamicTable.tsx +13 -20
- package/src/components/Table/Relations.stories.tsx +78 -34
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +72 -52
- package/src/components/Table/Table.tsx +41 -435
- package/src/components/Table/TableContent.tsx +421 -0
- package/src/components/{TableToolbar → Table}/TableToolbar.tsx +36 -43
- package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
- package/src/components/TableCellEditor/FormCellEditor.tsx +14 -7
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -12
- package/src/components/TableCellEditor/TableCellEditor.tsx +12 -10
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +10 -10
- package/src/hooks/useAddRow.ts +1 -1
- package/src/hooks/useProjectionModel.ts +3 -3
- package/src/hooks/useTableModel.ts +1 -1
- package/src/index.ts +0 -4
- package/src/model/table-model.test.ts +5 -6
- package/src/model/table-model.ts +18 -17
- package/src/model/table-presentation.test.ts +13 -7
- package/src/model/table-presentation.ts +12 -12
- package/src/playwright/TableManager.ts +10 -3
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +48 -5
- package/src/testing/useTestTableModel.ts +6 -6
- package/src/translations.ts +19 -21
- package/src/types/Table.ts +12 -23
- package/src/util/dynamic-table.test.ts +77 -0
- package/src/util/dynamic-table.ts +17 -16
- package/src/util/query-ast.ts +3 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +0 -7
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
- package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
- package/src/components/TableToolbar/index.ts +0 -5
|
@@ -2,469 +2,75 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Atom
|
|
6
|
-
import
|
|
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
|
|
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
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
import { ColumnActionsMenu } from './ColumnActionsMenu';
|
|
45
|
-
import { ColumnSettings } from './ColumnSettings';
|
|
46
|
-
import { RowActionsMenu } from './RowActionsMenu';
|
|
11
|
+
import { type InsertRowResult } from '../../model';
|
|
12
|
+
import { TableContent } from './TableContent';
|
|
13
|
+
import { TableToolbar } from './TableToolbar';
|
|
47
14
|
|
|
48
15
|
const columnDefault = { grid: { minSize: 80, maxSize: 640 } };
|
|
49
16
|
const rowDefault = { frozenRowsStart: { readonly: true, focusUnfurl: false } };
|
|
50
17
|
const emptyColumnMeta = Atom.make<DxGridAxisMeta>({ grid: {} });
|
|
51
18
|
|
|
52
19
|
//
|
|
53
|
-
//
|
|
20
|
+
// Context
|
|
54
21
|
//
|
|
55
22
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
);
|
|
23
|
+
type TableContextValue = {
|
|
24
|
+
/** Mutable ref populated by Content so Root can expose the controller. */
|
|
25
|
+
controllerRef: RefObject<TableController>;
|
|
74
26
|
};
|
|
75
27
|
|
|
28
|
+
const [TableContextProvider, useTableContext] = createContext<TableContextValue>('Table');
|
|
29
|
+
|
|
76
30
|
//
|
|
77
|
-
//
|
|
31
|
+
// Controller
|
|
78
32
|
//
|
|
79
33
|
|
|
80
|
-
|
|
34
|
+
type TableController = {
|
|
81
35
|
update?: (cell?: DxGridPosition) => void;
|
|
82
36
|
handleInsertRowResult?: (insertRowResult?: InsertRowResult) => void;
|
|
83
37
|
};
|
|
84
38
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
);
|
|
299
|
-
|
|
300
|
-
const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
|
|
301
|
-
(event) => {
|
|
302
|
-
if (!model?.features.dataEditable) {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const cell = closestCell(event.target);
|
|
307
|
-
if (!model || !cell) {
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
39
|
+
//
|
|
40
|
+
// Root
|
|
41
|
+
//
|
|
310
42
|
|
|
311
|
-
|
|
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
|
-
}
|
|
43
|
+
const TABLE_ROOT_NAME = 'Table.Root';
|
|
361
44
|
|
|
362
|
-
|
|
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],
|
|
377
|
-
);
|
|
45
|
+
type TableRootProps = PropsWithChildren;
|
|
378
46
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (event.axis === 'col') {
|
|
382
|
-
const columnIndex = parseInt(event.index, 10);
|
|
383
|
-
model?.setColumnWidth(columnIndex, event.size);
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
[model],
|
|
387
|
-
);
|
|
47
|
+
const TableRoot = forwardRef<TableController, TableRootProps>(({ children }, forwardedRef) => {
|
|
48
|
+
const controllerRef = useRef<TableController>({});
|
|
388
49
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
},
|
|
395
|
-
[
|
|
50
|
+
useImperativeHandle(
|
|
51
|
+
forwardedRef,
|
|
52
|
+
() => ({
|
|
53
|
+
update: (cell) => controllerRef.current?.update?.(cell),
|
|
54
|
+
handleInsertRowResult: (result) => controllerRef.current?.handleInsertRowResult?.(result),
|
|
55
|
+
}),
|
|
56
|
+
[],
|
|
396
57
|
);
|
|
397
58
|
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
}
|
|
59
|
+
return <TableContextProvider controllerRef={controllerRef}>{children}</TableContextProvider>;
|
|
60
|
+
});
|
|
429
61
|
|
|
430
|
-
|
|
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
|
-
};
|
|
62
|
+
TableRoot.displayName = TABLE_ROOT_NAME;
|
|
462
63
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
64
|
+
//
|
|
65
|
+
// Table
|
|
66
|
+
//
|
|
466
67
|
|
|
467
68
|
export const Table = {
|
|
468
69
|
Root: TableRoot,
|
|
469
|
-
|
|
70
|
+
Toolbar: TableToolbar,
|
|
71
|
+
Content: TableContent,
|
|
470
72
|
};
|
|
73
|
+
|
|
74
|
+
export type { TableController, TableRootProps };
|
|
75
|
+
|
|
76
|
+
export { useTableContext };
|