@dxos/react-ui-table 0.8.3 → 0.8.4-main.1f223c7
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-63UUF4IG.mjs +66 -0
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +7 -0
- package/dist/lib/browser/chunk-BBXDTWCX.mjs +1822 -0
- package/dist/lib/browser/chunk-BBXDTWCX.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +814 -2152
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +124 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +3 -5
- package/dist/lib/node-esm/chunk-K734DJB7.mjs +67 -0
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-RNQDU7IQ.mjs +1823 -0
- package/dist/lib/node-esm/chunk-RNQDU7IQ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +814 -2152
- 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 +125 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +3 -5
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +4 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +50 -2
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +44 -0
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +3 -2
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +42 -3
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +8 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +9 -15
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -3
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +14 -0
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +8 -7
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -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 +2 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +59 -21
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +10 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +3 -4
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/useTestTableModel.d.ts +22 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +5 -6
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +28 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util/dynamic-table.d.ts +4 -5
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +0 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +15 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +57 -44
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +15 -11
- package/src/components/Table/CreateRefPanel.tsx +30 -11
- package/src/components/Table/DynamicTable.stories.tsx +8 -5
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +56 -47
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +126 -159
- package/src/components/Table/Table.tsx +167 -123
- package/src/components/TableCellEditor/CellValidationMessage.tsx +7 -2
- package/src/components/TableCellEditor/FormCellEditor.tsx +158 -48
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +31 -47
- package/src/components/TableCellEditor/TableCellEditor.tsx +106 -111
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +7 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAddRow.ts +38 -0
- package/src/hooks/useTableModel.ts +21 -17
- package/src/index.ts +1 -2
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +6 -3
- package/src/model/table-model.test.ts +12 -9
- package/src/model/table-model.ts +288 -88
- package/src/model/table-presentation.test.ts +12 -8
- package/src/model/table-presentation.ts +263 -138
- package/src/model/table-sorting.ts +22 -13
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +137 -44
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +124 -0
- package/src/translations.ts +7 -5
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +73 -0
- package/src/util/dynamic-table.ts +24 -21
- package/src/util/index.ts +0 -1
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +28 -4
- package/src/util/table-controls.ts +4 -1
- package/dist/lib/browser/chunk-TL7CKQQV.mjs +0 -27
- package/dist/lib/browser/chunk-TL7CKQQV.mjs.map +0 -7
- package/dist/lib/node/chunk-O2VPBNS5.cjs +0 -50
- package/dist/lib/node/chunk-O2VPBNS5.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -2739
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/types/index.cjs +0 -31
- package/dist/lib/node/types/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs +0 -29
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs.map +0 -7
- package/dist/types/src/components/Table/relations.stories.d.ts +0 -9
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
- package/dist/types/src/types/table.d.ts +0 -56
- package/dist/types/src/types/table.d.ts.map +0 -1
- package/dist/types/src/util/table.d.ts +0 -14
- package/dist/types/src/util/table.d.ts.map +0 -1
- package/src/components/TableCellEditor/extension.ts +0 -61
- package/src/playwright/playwright.config.cts +0 -18
- package/src/types/table.ts +0 -27
- package/src/util/table.ts +0 -127
|
@@ -9,40 +9,40 @@ import React, {
|
|
|
9
9
|
type WheelEvent,
|
|
10
10
|
forwardRef,
|
|
11
11
|
useCallback,
|
|
12
|
+
useEffect,
|
|
12
13
|
useImperativeHandle,
|
|
13
|
-
useState,
|
|
14
14
|
useMemo,
|
|
15
|
-
|
|
15
|
+
useState,
|
|
16
16
|
} from 'react';
|
|
17
17
|
|
|
18
|
-
import { type Client } from '@dxos/client';
|
|
19
|
-
|
|
20
|
-
import { invariant } from '@dxos/invariant';
|
|
21
|
-
import { Filter } from '@dxos/react-client/echo';
|
|
22
|
-
import { useTranslation } from '@dxos/react-ui';
|
|
18
|
+
import { type Client } from '@dxos/react-client';
|
|
19
|
+
// TODO(wittjosiah): Remove dependency on react-client.
|
|
23
20
|
import { useAttention } from '@dxos/react-ui-attention';
|
|
24
21
|
import {
|
|
25
|
-
closestCell,
|
|
26
22
|
type DxGridElement,
|
|
27
|
-
type DxGridPosition,
|
|
28
|
-
type GridContentProps,
|
|
29
|
-
Grid,
|
|
30
23
|
type DxGridPlane,
|
|
31
24
|
type DxGridPlaneRange,
|
|
32
|
-
|
|
25
|
+
type DxGridPosition,
|
|
26
|
+
Grid,
|
|
27
|
+
type GridContentProps,
|
|
28
|
+
closestCell,
|
|
33
29
|
gridSeparatorBlockEnd,
|
|
30
|
+
gridSeparatorInlineEnd,
|
|
34
31
|
} from '@dxos/react-ui-grid';
|
|
32
|
+
import { DxEditRequest } from '@dxos/react-ui-grid';
|
|
35
33
|
import { mx } from '@dxos/react-ui-theme';
|
|
36
|
-
|
|
34
|
+
|
|
35
|
+
import { type InsertRowResult, ModalController, type TableModel, type TablePresentation } from '../../model';
|
|
36
|
+
import { tableButtons, tableControls } from '../../util';
|
|
37
|
+
import { type TableCellEditorProps, TableValueEditor } from '../TableCellEditor';
|
|
37
38
|
|
|
38
39
|
import { ColumnActionsMenu } from './ColumnActionsMenu';
|
|
39
40
|
import { ColumnSettings } from './ColumnSettings';
|
|
40
41
|
import { CreateRefPanel } from './CreateRefPanel';
|
|
41
42
|
import { RowActionsMenu } from './RowActionsMenu';
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
import { createOption, TableValueEditor, type TableCellEditorProps } from '../TableCellEditor';
|
|
43
|
+
|
|
44
|
+
const columnDefault = { grid: { minSize: 80, maxSize: 640 } };
|
|
45
|
+
const rowDefault = { frozenRowsStart: { readonly: true, focusUnfurl: false } };
|
|
46
46
|
|
|
47
47
|
//
|
|
48
48
|
// Table.Root
|
|
@@ -50,15 +50,17 @@ import { createOption, TableValueEditor, type TableCellEditorProps } from '../Ta
|
|
|
50
50
|
|
|
51
51
|
export type TableRootProps = PropsWithChildren<{ role?: string }>;
|
|
52
52
|
|
|
53
|
-
const TableRoot = ({ children, role }: TableRootProps) => {
|
|
53
|
+
const TableRoot = ({ children, role = 'article' }: TableRootProps) => {
|
|
54
54
|
return (
|
|
55
55
|
<div
|
|
56
56
|
role='none'
|
|
57
57
|
className={mx(
|
|
58
|
-
'relative !border-separator',
|
|
59
|
-
role === '
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
'relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]',
|
|
59
|
+
role === 'card--popover' && 'popover-card-height',
|
|
60
|
+
role === 'section' && 'attention-surface',
|
|
61
|
+
role === 'card--intrinsic' && '[&_.dx-grid]:bs-[--dx-grid-content-block-size]',
|
|
62
|
+
['card--popover', 'section', 'card--extrinsic'].includes(role) && 'overflow-hidden',
|
|
63
|
+
['article', 'slide'].includes(role) && 'flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0',
|
|
62
64
|
)}
|
|
63
65
|
>
|
|
64
66
|
{children}
|
|
@@ -72,6 +74,7 @@ const TableRoot = ({ children, role }: TableRootProps) => {
|
|
|
72
74
|
|
|
73
75
|
export type TableController = {
|
|
74
76
|
update?: (cell?: DxGridPosition) => void;
|
|
77
|
+
handleInsertRowResult?: (insertRowResult?: InsertRowResult) => void;
|
|
75
78
|
};
|
|
76
79
|
|
|
77
80
|
export type TableMainProps = {
|
|
@@ -88,9 +91,15 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
88
91
|
({ model, presentation, ignoreAttention, schema, client, onRowClick }, forwardedRef) => {
|
|
89
92
|
const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
|
|
90
93
|
const { hasAttention } = useAttention(model?.id ?? 'table');
|
|
91
|
-
const { t } = useTranslation(translationKey);
|
|
92
94
|
const modals = useMemo(() => new ModalController(), []);
|
|
93
95
|
|
|
96
|
+
const draftRowCount = model?.getDraftRowCount() ?? 0;
|
|
97
|
+
|
|
98
|
+
const handleSave = useCallback(() => {
|
|
99
|
+
dxGrid?.updateCells(true);
|
|
100
|
+
dxGrid?.requestUpdate();
|
|
101
|
+
}, [dxGrid]);
|
|
102
|
+
|
|
94
103
|
const frozen = useMemo(() => {
|
|
95
104
|
const noActionColumn =
|
|
96
105
|
model?.features.dataEditable === false &&
|
|
@@ -99,10 +108,11 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
99
108
|
|
|
100
109
|
return {
|
|
101
110
|
frozenRowsStart: 1,
|
|
111
|
+
frozenRowsEnd: Math.max(1, draftRowCount),
|
|
102
112
|
frozenColsStart: model?.features.selection.enabled ? 1 : 0,
|
|
103
113
|
frozenColsEnd: noActionColumn ? 0 : 1,
|
|
104
114
|
};
|
|
105
|
-
}, [model]);
|
|
115
|
+
}, [model, draftRowCount]);
|
|
106
116
|
|
|
107
117
|
const getCells = useCallback<NonNullable<GridContentProps['getCells']>>(
|
|
108
118
|
(range: DxGridPlaneRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
|
|
@@ -116,37 +126,70 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
116
126
|
dxGrid.getCells = getCells;
|
|
117
127
|
}, [presentation, dxGrid, getCells]);
|
|
118
128
|
|
|
129
|
+
const handleInsertRowResult = useCallback(
|
|
130
|
+
(insertResult?: InsertRowResult) => {
|
|
131
|
+
if (insertResult === 'draft') {
|
|
132
|
+
requestAnimationFrame(() => {
|
|
133
|
+
dxGrid?.setFocus({ plane: 'frozenRowsEnd', col: 0, row: 0 });
|
|
134
|
+
dxGrid?.refocus();
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
requestAnimationFrame(() => {
|
|
138
|
+
dxGrid?.setFocus({ plane: 'grid', col: 0, row: model ? model.getRowCount() - 1 : 0 });
|
|
139
|
+
dxGrid?.refocus();
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
[model, dxGrid],
|
|
144
|
+
);
|
|
145
|
+
|
|
119
146
|
/**
|
|
120
147
|
* Provides an external controller that can be called to repaint the table.
|
|
121
148
|
*/
|
|
122
|
-
useImperativeHandle<TableController, TableController>(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return {};
|
|
127
|
-
}
|
|
149
|
+
useImperativeHandle<TableController, TableController>(forwardedRef, () => {
|
|
150
|
+
if (!presentation || !dxGrid) {
|
|
151
|
+
return {};
|
|
152
|
+
}
|
|
128
153
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
154
|
+
return {
|
|
155
|
+
update: (cell) => {
|
|
156
|
+
if (cell) {
|
|
157
|
+
dxGrid.updateIfWithinBounds(cell, true);
|
|
158
|
+
} else {
|
|
159
|
+
dxGrid.updateCells(true);
|
|
160
|
+
dxGrid.requestUpdate();
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
handleInsertRowResult,
|
|
164
|
+
};
|
|
165
|
+
}, [presentation, dxGrid, model]);
|
|
166
|
+
|
|
167
|
+
const handleSaveDraftRow = useCallback(
|
|
168
|
+
(rowIndex = 0) => {
|
|
169
|
+
if (model && dxGrid) {
|
|
170
|
+
const didCommitSuccessfully = model.commitDraftRow(rowIndex);
|
|
171
|
+
if (didCommitSuccessfully) {
|
|
172
|
+
requestAnimationFrame(() => {
|
|
173
|
+
dxGrid.scrollToEndRow();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
139
177
|
},
|
|
140
|
-
[
|
|
178
|
+
[model, dxGrid],
|
|
141
179
|
);
|
|
142
180
|
|
|
143
181
|
const handleGridClick = useCallback(
|
|
144
182
|
(event: MouseEvent) => {
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
183
|
+
const cell = closestCell(event.target as HTMLElement);
|
|
184
|
+
if (cell) {
|
|
185
|
+
const { row: rowIndex, plane } = cell;
|
|
147
186
|
if (onRowClick) {
|
|
148
|
-
|
|
149
|
-
|
|
187
|
+
if (plane === 'grid') {
|
|
188
|
+
const row = model?.getRowAt(rowIndex);
|
|
189
|
+
row && onRowClick(row);
|
|
190
|
+
} else {
|
|
191
|
+
onRowClick(cell);
|
|
192
|
+
}
|
|
150
193
|
}
|
|
151
194
|
|
|
152
195
|
if (model?.features.selection.enabled && model?.selection.selectionMode === 'single') {
|
|
@@ -188,6 +231,10 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
188
231
|
model?.sorting?.toggleSort(data.fieldId);
|
|
189
232
|
break;
|
|
190
233
|
}
|
|
234
|
+
case 'saveDraftRow': {
|
|
235
|
+
handleSaveDraftRow();
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
191
238
|
}
|
|
192
239
|
return;
|
|
193
240
|
}
|
|
@@ -222,36 +269,23 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
222
269
|
}
|
|
223
270
|
}
|
|
224
271
|
},
|
|
225
|
-
[model, modals],
|
|
272
|
+
[model, modals, dxGrid],
|
|
226
273
|
);
|
|
227
274
|
|
|
228
275
|
const handleFocus = useCallback<NonNullable<TableCellEditorProps['onFocus']>>(
|
|
229
|
-
(increment, delta) => {
|
|
230
|
-
if (dxGrid) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
(cell) => {
|
|
239
|
-
if (!model?.features.dataEditable) {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// TODO(burdon): Insert row only if bottom row isn't completely blank already.
|
|
244
|
-
if (model && cell.row === model.getRowCount() - 1) {
|
|
245
|
-
model.insertRow(cell.row);
|
|
246
|
-
if (dxGrid) {
|
|
247
|
-
requestAnimationFrame(() => {
|
|
248
|
-
dxGrid?.scrollToRow(cell.row + 1);
|
|
249
|
-
dxGrid?.refocus('row', 1);
|
|
250
|
-
});
|
|
276
|
+
(increment, delta, cell) => {
|
|
277
|
+
if (dxGrid && model) {
|
|
278
|
+
if (cell?.plane === 'grid' && cell?.row >= model.getRowCount() - 1) {
|
|
279
|
+
handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : 'final');
|
|
280
|
+
} else if (cell?.plane === 'frozenRowsEnd' && increment === 'row') {
|
|
281
|
+
handleSaveDraftRow(cell.row);
|
|
282
|
+
handleInsertRowResult(model.insertRow());
|
|
283
|
+
} else {
|
|
284
|
+
dxGrid.refocus(increment, delta);
|
|
251
285
|
}
|
|
252
286
|
}
|
|
253
287
|
},
|
|
254
|
-
[
|
|
288
|
+
[dxGrid, model],
|
|
255
289
|
);
|
|
256
290
|
|
|
257
291
|
const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
|
|
@@ -265,15 +299,71 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
265
299
|
return;
|
|
266
300
|
}
|
|
267
301
|
|
|
302
|
+
// Handle Meta+C (Copy) and Meta+V (Paste) commands
|
|
303
|
+
if (event.metaKey || event.ctrlKey) {
|
|
304
|
+
switch (event.key) {
|
|
305
|
+
case 'c': {
|
|
306
|
+
// Copy focused cell's text content to clipboard
|
|
307
|
+
try {
|
|
308
|
+
const cellData = model.getCellData(cell);
|
|
309
|
+
const textContent = cellData?.toString() ?? '';
|
|
310
|
+
void navigator.clipboard.writeText(textContent);
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
} catch (error) {
|
|
313
|
+
console.warn('Failed to copy cell content:', error);
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
case 'v': {
|
|
318
|
+
// Paste clipboard content to focused cell
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
void navigator.clipboard.readText().then((clipboardText) => {
|
|
321
|
+
try {
|
|
322
|
+
// Attempt to set the cell's content to clipboard content
|
|
323
|
+
model.setCellData(cell, clipboardText);
|
|
324
|
+
handleSave();
|
|
325
|
+
} catch {
|
|
326
|
+
// If validation fails, emit a DxEditRequest event with initialContent from clipboard
|
|
327
|
+
// TODO(thure): Should `dx-grid` expose a method like this?
|
|
328
|
+
const cellElement = (event.target as HTMLElement).closest(
|
|
329
|
+
'[data-dx-grid-action="cell"]',
|
|
330
|
+
) as HTMLElement;
|
|
331
|
+
if (cellElement) {
|
|
332
|
+
const rect = cellElement.getBoundingClientRect();
|
|
333
|
+
const editRequest = new DxEditRequest({
|
|
334
|
+
cellIndex: `${cell.plane},${cell.col},${cell.row}`,
|
|
335
|
+
cellBox: {
|
|
336
|
+
insetInlineStart: rect.left,
|
|
337
|
+
insetBlockStart: rect.top,
|
|
338
|
+
inlineSize: rect.width,
|
|
339
|
+
blockSize: rect.height,
|
|
340
|
+
},
|
|
341
|
+
cellElement,
|
|
342
|
+
initialContent: clipboardText,
|
|
343
|
+
});
|
|
344
|
+
cellElement.dispatchEvent(editRequest);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
268
353
|
switch (event.key) {
|
|
269
354
|
case 'Backspace':
|
|
270
355
|
case 'Delete': {
|
|
271
|
-
|
|
356
|
+
try {
|
|
357
|
+
model.setCellData(cell, undefined);
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
} catch {
|
|
360
|
+
// Delete results in a validation error; don’t prevent default so dx-grid can emit an edit request.
|
|
361
|
+
}
|
|
272
362
|
break;
|
|
273
363
|
}
|
|
274
364
|
}
|
|
275
365
|
},
|
|
276
|
-
[model],
|
|
366
|
+
[model, dxGrid, handleSave],
|
|
277
367
|
);
|
|
278
368
|
|
|
279
369
|
const handleAxisResize = useCallback<NonNullable<GridContentProps['onAxisResize']>>(
|
|
@@ -302,53 +392,6 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
302
392
|
}
|
|
303
393
|
}, [model, dxGrid]);
|
|
304
394
|
|
|
305
|
-
// TODO(burdon): Factor out?
|
|
306
|
-
// TODO(burdon): Generalize to handle other value types (e.g., enums).
|
|
307
|
-
const handleQuery = useCallback<NonNullable<TableCellEditorProps['onQuery']>>(
|
|
308
|
-
async ({ field, props }, text) => {
|
|
309
|
-
if (model && props.referenceSchema && field.referencePath) {
|
|
310
|
-
const space = model.space;
|
|
311
|
-
invariant(space);
|
|
312
|
-
|
|
313
|
-
let schema;
|
|
314
|
-
if (client) {
|
|
315
|
-
schema = client.graph.schemaRegistry.getSchema(props.referenceSchema);
|
|
316
|
-
}
|
|
317
|
-
if (!schema) {
|
|
318
|
-
schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (schema) {
|
|
322
|
-
const { objects } = await space.db.query(Filter.type(schema)).run();
|
|
323
|
-
const options = objects
|
|
324
|
-
.map((obj) => {
|
|
325
|
-
const value = getValue(obj, field.referencePath!);
|
|
326
|
-
if (!value || typeof value !== 'string') {
|
|
327
|
-
return undefined;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
label: value,
|
|
332
|
-
data: obj,
|
|
333
|
-
};
|
|
334
|
-
})
|
|
335
|
-
.filter(isNotFalsy);
|
|
336
|
-
|
|
337
|
-
return [
|
|
338
|
-
...options,
|
|
339
|
-
{
|
|
340
|
-
label: t('create new object label', { text }),
|
|
341
|
-
data: createOption(text),
|
|
342
|
-
},
|
|
343
|
-
];
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return [];
|
|
348
|
-
},
|
|
349
|
-
[model, client, t],
|
|
350
|
-
);
|
|
351
|
-
|
|
352
395
|
if (!model || !modals) {
|
|
353
396
|
return <span role='none' className='attention-surface' />;
|
|
354
397
|
}
|
|
@@ -359,21 +402,22 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
359
402
|
model={model}
|
|
360
403
|
modals={modals}
|
|
361
404
|
schema={schema}
|
|
362
|
-
onEnter={handleEnter}
|
|
363
405
|
onFocus={handleFocus}
|
|
364
|
-
|
|
406
|
+
onSave={handleSave}
|
|
407
|
+
client={client}
|
|
365
408
|
/>
|
|
366
409
|
<Grid.Content
|
|
367
410
|
className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
|
|
368
411
|
frozen={frozen}
|
|
369
|
-
// getCells={getCells}
|
|
370
412
|
columns={model.columnMeta.value}
|
|
413
|
+
columnDefault={columnDefault}
|
|
414
|
+
rowDefault={rowDefault}
|
|
371
415
|
limitRows={model.getRowCount() ?? 0}
|
|
372
|
-
limitColumns={model.
|
|
416
|
+
limitColumns={model.projection.fields.length}
|
|
373
417
|
overscroll='trap'
|
|
374
418
|
onAxisResize={handleAxisResize}
|
|
375
419
|
onClick={handleGridClick}
|
|
376
|
-
|
|
420
|
+
onKeyDownCapture={handleKeyDown}
|
|
377
421
|
onWheelCapture={handleWheel}
|
|
378
422
|
ref={setDxGrid}
|
|
379
423
|
/>
|
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { type GridScopedProps, useGridContext } from '@dxos/react-ui-grid';
|
|
8
8
|
|
|
9
9
|
export type CellValidationMessageProps = {
|
|
10
10
|
validationError: string | null;
|
|
11
|
+
variant?: 'error' | 'warning';
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export const CellValidationMessage = ({
|
|
14
15
|
validationError,
|
|
16
|
+
variant = 'error',
|
|
15
17
|
__gridScope,
|
|
16
18
|
}: GridScopedProps<CellValidationMessageProps>) => {
|
|
17
19
|
const { editing, editBox: box } = useGridContext('GridSheetCellEditor', __gridScope);
|
|
@@ -20,10 +22,13 @@ export const CellValidationMessage = ({
|
|
|
20
22
|
return null;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
const bgClass = variant === 'error' ? 'bg-errorSurface' : 'bg-warningSurface';
|
|
26
|
+
const textClass = variant === 'error' ? 'text-errorSurfaceText' : 'text-warningSurfaceText';
|
|
27
|
+
|
|
23
28
|
return (
|
|
24
29
|
<div
|
|
25
30
|
role='none'
|
|
26
|
-
className=
|
|
31
|
+
className={`absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`}
|
|
27
32
|
style={{
|
|
28
33
|
...{ '--dx-gridCellWidth': `${box?.inlineSize ?? 200}px` },
|
|
29
34
|
zIndex: 10,
|