@dxos/react-ui-table 0.8.4-main.f9ba587 → 0.8.4-main.fd6878d
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-L2LSB6KV.mjs +18 -0
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs +1814 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +658 -2359
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +123 -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-NXZ4CJYX.mjs +20 -0
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs +1815 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +658 -2359
- 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 +124 -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 +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/{relations.stories.d.ts → Relations.stories.d.ts} +2 -2
- 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 +1 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/extension.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/useTableModel.d.ts +8 -7
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- 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 +17 -16
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +2 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +4 -4
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- 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 +41 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +2 -2
- 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 +8 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util/dynamic-table.d.ts +3 -4
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -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/src/util/table.d.ts +9 -11
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +50 -36
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +15 -11
- package/src/components/Table/CreateRefPanel.tsx +6 -4
- package/src/components/Table/DynamicTable.stories.tsx +3 -2
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +36 -35
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +99 -144
- package/src/components/Table/Table.tsx +87 -71
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +30 -21
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -27
- package/src/components/TableCellEditor/TableCellEditor.tsx +42 -27
- package/src/components/TableCellEditor/extension.ts +2 -2
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +7 -24
- package/src/hooks/useTableModel.ts +21 -17
- package/src/index.ts +0 -1
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +6 -3
- package/src/model/table-model.test.ts +8 -8
- package/src/model/table-model.ts +68 -62
- package/src/model/table-presentation.test.ts +8 -8
- package/src/model/table-presentation.ts +35 -13
- package/src/model/table-sorting.ts +11 -11
- package/src/playwright/smoke.spec.ts +105 -14
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +123 -0
- package/src/translations.ts +2 -2
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +16 -0
- package/src/util/dynamic-table.ts +20 -18
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +2 -1
- package/src/util/table-controls.ts +4 -1
- package/src/util/table.ts +37 -111
- package/dist/lib/browser/chunk-Q5QQXJUO.mjs +0 -27
- package/dist/lib/browser/chunk-Q5QQXJUO.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs +0 -29
- package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs.map +0 -7
- package/dist/types/src/components/Table/relations.stories.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/src/types/table.ts +0 -27
|
@@ -4,150 +4,91 @@
|
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
8
|
import { Schema } from 'effect';
|
|
9
|
-
import React, { useCallback
|
|
9
|
+
import React, { useCallback } from 'react';
|
|
10
10
|
|
|
11
11
|
import { Obj, Type } from '@dxos/echo';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
EchoObject,
|
|
14
|
+
FormatAnnotation,
|
|
15
|
+
FormatEnum,
|
|
16
|
+
GeneratorAnnotation,
|
|
17
|
+
LabelAnnotation,
|
|
18
|
+
PropertyMetaAnnotationId,
|
|
19
|
+
} from '@dxos/echo-schema';
|
|
13
20
|
import { invariant } from '@dxos/invariant';
|
|
14
|
-
import { useGlobalFilteredObjects } from '@dxos/plugin-search';
|
|
15
21
|
import { faker } from '@dxos/random';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
22
|
+
import { PublicKey } from '@dxos/react-client';
|
|
23
|
+
import { type Space, live } from '@dxos/react-client/echo';
|
|
24
|
+
import { withClientProvider } from '@dxos/react-client/testing';
|
|
19
25
|
import { ViewEditor } from '@dxos/react-ui-form';
|
|
20
26
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
21
|
-
import {
|
|
27
|
+
import { DataType, getSchemaFromPropertyDefinitions } from '@dxos/schema';
|
|
22
28
|
import { Testing, createObjectFactory } from '@dxos/schema/testing';
|
|
23
29
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
24
30
|
|
|
25
|
-
import {
|
|
26
|
-
import { useTableModel, useAddRow } from '../../hooks';
|
|
27
|
-
import { TablePresentation } from '../../model';
|
|
31
|
+
import { useTestTableModel } from '../../testing';
|
|
28
32
|
import { translations } from '../../translations';
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
33
|
+
import { TableView } from '../../types';
|
|
34
|
+
import { createTable } from '../../util';
|
|
31
35
|
import { TableToolbar } from '../TableToolbar';
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const handleDeleteRows = useCallback(
|
|
78
|
-
(_: number, objects: any[]) => {
|
|
79
|
-
for (const object of objects) {
|
|
80
|
-
space?.db.remove(object);
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
[space],
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
const handleDeleteColumn = useCallback(
|
|
87
|
-
(fieldId: string) => {
|
|
88
|
-
if (projection) {
|
|
89
|
-
projection.deleteFieldProjection(fieldId);
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
[projection],
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
const model = useTableModel({
|
|
96
|
-
table,
|
|
97
|
-
projection,
|
|
98
|
-
features,
|
|
99
|
-
rows: filteredObjects,
|
|
100
|
-
onInsertRow: addRow,
|
|
101
|
-
onDeleteRows: handleDeleteRows,
|
|
102
|
-
onDeleteColumn: handleDeleteColumn,
|
|
103
|
-
onCellUpdate: handleCellUpdate,
|
|
104
|
-
onRowOrderChange: handleRowOrderChange,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const handleInsertRow = useCallback(() => {
|
|
108
|
-
model?.insertRow();
|
|
109
|
-
}, [model]);
|
|
110
|
-
|
|
111
|
-
const handleSaveView = useCallback(() => {
|
|
112
|
-
model?.saveView();
|
|
113
|
-
}, [model]);
|
|
114
|
-
|
|
115
|
-
const presentation = useMemo(() => {
|
|
116
|
-
if (model) {
|
|
117
|
-
return new TablePresentation(model);
|
|
118
|
-
}
|
|
119
|
-
}, [model]);
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
schema,
|
|
123
|
-
table,
|
|
124
|
-
tableRef,
|
|
125
|
-
model,
|
|
126
|
-
presentation,
|
|
127
|
-
space,
|
|
128
|
-
client,
|
|
129
|
-
handleInsertRow,
|
|
130
|
-
handleSaveView,
|
|
131
|
-
handleDeleteRows,
|
|
132
|
-
handleDeleteColumn,
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const StoryViewEditor = () => {
|
|
137
|
-
const { table, space, schema, handleDeleteColumn } = useTestTableModel();
|
|
138
|
-
|
|
37
|
+
import { Table } from './Table';
|
|
38
|
+
|
|
39
|
+
const TestSchema = Schema.Struct({
|
|
40
|
+
// TODO(wittjosiah): Should be title. Currently name to work with default label.
|
|
41
|
+
name: Schema.optional(Schema.String).annotations({ title: 'Title' }),
|
|
42
|
+
urgent: Schema.optional(Schema.Boolean).annotations({ title: 'Urgent' }),
|
|
43
|
+
status: Schema.optional(
|
|
44
|
+
Schema.Literal('todo', 'in-progress', 'done')
|
|
45
|
+
.pipe(FormatAnnotation.set(FormatEnum.SingleSelect))
|
|
46
|
+
.annotations({
|
|
47
|
+
title: 'Status',
|
|
48
|
+
[PropertyMetaAnnotationId]: {
|
|
49
|
+
singleSelect: {
|
|
50
|
+
options: [
|
|
51
|
+
{ id: 'todo', title: 'Todo', color: 'indigo' },
|
|
52
|
+
{ id: 'in-progress', title: 'In Progress', color: 'purple' },
|
|
53
|
+
{ id: 'done', title: 'Done', color: 'amber' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
),
|
|
59
|
+
description: Schema.optional(Schema.String).annotations({ title: 'Description' }),
|
|
60
|
+
parent: Schema.optional(Schema.suspend((): Type.Ref<TestSchema> => Type.Ref(TestSchema))).annotations({
|
|
61
|
+
title: 'Parent',
|
|
62
|
+
}),
|
|
63
|
+
}).pipe(
|
|
64
|
+
Type.Obj({ typename: `example.com/type/${PublicKey.random().truncate()}`, version: '0.1.0' }),
|
|
65
|
+
LabelAnnotation.set(['name']),
|
|
66
|
+
);
|
|
67
|
+
interface TestSchema extends Schema.Schema.Type<typeof TestSchema> {}
|
|
68
|
+
|
|
69
|
+
const StoryViewEditor = ({
|
|
70
|
+
view,
|
|
71
|
+
schema,
|
|
72
|
+
space,
|
|
73
|
+
handleDeleteColumn,
|
|
74
|
+
}: {
|
|
75
|
+
view?: DataType.View;
|
|
76
|
+
schema?: Schema.Schema.AnyNoContext;
|
|
77
|
+
space?: Space;
|
|
78
|
+
handleDeleteColumn: (fieldId: string) => void;
|
|
79
|
+
}) => {
|
|
139
80
|
const handleTypenameChanged = useCallback(
|
|
140
81
|
(typename: string) => {
|
|
141
82
|
invariant(schema);
|
|
142
83
|
invariant(Type.isMutable(schema));
|
|
143
84
|
schema.updateTypename(typename);
|
|
144
|
-
invariant(
|
|
145
|
-
|
|
85
|
+
invariant(view);
|
|
86
|
+
view.query.typename = typename;
|
|
146
87
|
},
|
|
147
|
-
[schema,
|
|
88
|
+
[schema, view],
|
|
148
89
|
);
|
|
149
90
|
|
|
150
|
-
if (!
|
|
91
|
+
if (!view || !schema) {
|
|
151
92
|
return null;
|
|
152
93
|
}
|
|
153
94
|
|
|
@@ -155,7 +96,7 @@ const StoryViewEditor = () => {
|
|
|
155
96
|
<ViewEditor
|
|
156
97
|
registry={space?.db.schemaRegistry}
|
|
157
98
|
schema={schema}
|
|
158
|
-
view={
|
|
99
|
+
view={view}
|
|
159
100
|
onTypenameChanged={handleTypenameChanged}
|
|
160
101
|
onDelete={handleDeleteColumn}
|
|
161
102
|
/>
|
|
@@ -167,9 +108,20 @@ const StoryViewEditor = () => {
|
|
|
167
108
|
//
|
|
168
109
|
|
|
169
110
|
const DefaultStory = () => {
|
|
170
|
-
const {
|
|
111
|
+
const {
|
|
112
|
+
space,
|
|
113
|
+
schema,
|
|
114
|
+
view,
|
|
115
|
+
tableRef,
|
|
116
|
+
model,
|
|
117
|
+
presentation,
|
|
118
|
+
client,
|
|
119
|
+
handleInsertRow,
|
|
120
|
+
handleSaveView,
|
|
121
|
+
handleDeleteColumn,
|
|
122
|
+
} = useTestTableModel();
|
|
171
123
|
|
|
172
|
-
if (!schema || !
|
|
124
|
+
if (!schema || !view) {
|
|
173
125
|
return <div />;
|
|
174
126
|
}
|
|
175
127
|
|
|
@@ -189,9 +141,9 @@ const DefaultStory = () => {
|
|
|
189
141
|
</Table.Root>
|
|
190
142
|
</div>
|
|
191
143
|
<div className='flex flex-col h-full border-l border-separator overflow-y-auto'>
|
|
192
|
-
<StoryViewEditor />
|
|
144
|
+
<StoryViewEditor view={view} schema={schema} space={space} handleDeleteColumn={handleDeleteColumn} />
|
|
193
145
|
<SyntaxHighlighter language='json' className='w-full text-xs'>
|
|
194
|
-
{JSON.stringify({ view
|
|
146
|
+
{JSON.stringify({ view, schema }, null, 2)}
|
|
195
147
|
</SyntaxHighlighter>
|
|
196
148
|
</div>
|
|
197
149
|
</div>
|
|
@@ -220,16 +172,20 @@ const meta: Meta<StoryProps> = {
|
|
|
220
172
|
withTheme,
|
|
221
173
|
withLayout({ fullscreen: true }),
|
|
222
174
|
withClientProvider({
|
|
223
|
-
types: [
|
|
175
|
+
types: [DataType.View, TableView],
|
|
224
176
|
createIdentity: true,
|
|
225
177
|
createSpace: true,
|
|
226
178
|
onSpaceCreated: async ({ client, space }) => {
|
|
227
|
-
const
|
|
228
|
-
const
|
|
179
|
+
const [schema] = await space.db.schemaRegistry.register([TestSchema]);
|
|
180
|
+
const { view } = await createTable({ client, space, typename: schema.typename });
|
|
181
|
+
space.db.add(view);
|
|
182
|
+
|
|
229
183
|
Array.from({ length: 10 }).map(() => {
|
|
230
184
|
return space.db.add(
|
|
231
|
-
|
|
232
|
-
name: faker.
|
|
185
|
+
Obj.make(schema, {
|
|
186
|
+
name: faker.lorem.sentence(),
|
|
187
|
+
status: faker.helpers.arrayElement(['todo', 'in-progress', 'done'] as const),
|
|
188
|
+
description: faker.lorem.paragraph(),
|
|
233
189
|
}),
|
|
234
190
|
);
|
|
235
191
|
});
|
|
@@ -247,18 +203,17 @@ export const StaticSchema: StoryObj = {
|
|
|
247
203
|
parameters: { translations },
|
|
248
204
|
decorators: [
|
|
249
205
|
withClientProvider({
|
|
250
|
-
types: [
|
|
206
|
+
types: [DataType.View, TableView, Testing.Contact, Testing.Organization],
|
|
251
207
|
createIdentity: true,
|
|
252
208
|
createSpace: true,
|
|
253
209
|
onSpaceCreated: async ({ client, space }) => {
|
|
254
|
-
const
|
|
255
|
-
|
|
210
|
+
const { view } = await createTable({ client, space, typename: Testing.Contact.typename });
|
|
211
|
+
space.db.add(view);
|
|
256
212
|
|
|
257
213
|
const factory = createObjectFactory(space.db, faker as any);
|
|
258
214
|
await factory([
|
|
259
|
-
|
|
215
|
+
{ type: Testing.Contact, count: 10 },
|
|
260
216
|
// { type: Testing.Organization, count: 1 },
|
|
261
|
-
{ type: ContactWithArrayOfEmails, count: 10 },
|
|
262
217
|
]);
|
|
263
218
|
},
|
|
264
219
|
}),
|
|
@@ -289,12 +244,12 @@ export const ArrayOfObjects: StoryObj = {
|
|
|
289
244
|
parameters: { translations },
|
|
290
245
|
decorators: [
|
|
291
246
|
withClientProvider({
|
|
292
|
-
types: [
|
|
247
|
+
types: [DataType.View, TableView, Testing.Contact, Testing.Organization, ContactWithArrayOfEmails],
|
|
293
248
|
createIdentity: true,
|
|
294
249
|
createSpace: true,
|
|
295
250
|
onSpaceCreated: async ({ client, space }) => {
|
|
296
|
-
const
|
|
297
|
-
|
|
251
|
+
const { view } = await createTable({ client, space, typename: ContactWithArrayOfEmails.typename });
|
|
252
|
+
space.db.add(view);
|
|
298
253
|
|
|
299
254
|
const factory = createObjectFactory(space.db, faker as any);
|
|
300
255
|
await factory([
|
|
@@ -315,7 +270,7 @@ export const Tags: Meta<StoryProps> = {
|
|
|
315
270
|
parameters: { translations },
|
|
316
271
|
decorators: [
|
|
317
272
|
withClientProvider({
|
|
318
|
-
types: [
|
|
273
|
+
types: [DataType.View, TableView],
|
|
319
274
|
createIdentity: true,
|
|
320
275
|
createSpace: true,
|
|
321
276
|
onSpaceCreated: async ({ client, space }) => {
|
|
@@ -346,8 +301,8 @@ export const Tags: Meta<StoryProps> = {
|
|
|
346
301
|
const [storedSchema] = await space.db.schemaRegistry.register([schema]);
|
|
347
302
|
|
|
348
303
|
// Initialize table.
|
|
349
|
-
const
|
|
350
|
-
|
|
304
|
+
const { view } = await createTable({ client, space, typename });
|
|
305
|
+
space.db.add(view);
|
|
351
306
|
|
|
352
307
|
// Populate.
|
|
353
308
|
Array.from({ length: 10 }).map(() => {
|
|
@@ -9,40 +9,43 @@ 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
18
|
import { type Client } from '@dxos/client';
|
|
19
|
+
import { Filter } from '@dxos/echo';
|
|
19
20
|
import { getValue } from '@dxos/echo-schema';
|
|
20
21
|
import { invariant } from '@dxos/invariant';
|
|
21
|
-
|
|
22
|
+
// TODO(wittjosiah): Remove dependency on react-client.
|
|
23
|
+
import { getSpace } from '@dxos/react-client/echo';
|
|
22
24
|
import { useTranslation } from '@dxos/react-ui';
|
|
23
25
|
import { useAttention } from '@dxos/react-ui-attention';
|
|
24
26
|
import {
|
|
25
|
-
closestCell,
|
|
26
27
|
type DxGridElement,
|
|
27
|
-
type DxGridPosition,
|
|
28
|
-
type GridContentProps,
|
|
29
|
-
Grid,
|
|
30
28
|
type DxGridPlane,
|
|
31
29
|
type DxGridPlaneRange,
|
|
32
|
-
|
|
30
|
+
type DxGridPosition,
|
|
31
|
+
Grid,
|
|
32
|
+
type GridContentProps,
|
|
33
|
+
closestCell,
|
|
33
34
|
gridSeparatorBlockEnd,
|
|
35
|
+
gridSeparatorInlineEnd,
|
|
34
36
|
} from '@dxos/react-ui-grid';
|
|
35
37
|
import { mx } from '@dxos/react-ui-theme';
|
|
36
38
|
import { isNotFalsy, safeParseInt } from '@dxos/util';
|
|
37
39
|
|
|
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';
|
|
44
|
+
|
|
38
45
|
import { ColumnActionsMenu } from './ColumnActionsMenu';
|
|
39
46
|
import { ColumnSettings } from './ColumnSettings';
|
|
40
47
|
import { CreateRefPanel } from './CreateRefPanel';
|
|
41
48
|
import { RowActionsMenu } from './RowActionsMenu';
|
|
42
|
-
import { ModalController, type TableModel, type TablePresentation } from '../../model';
|
|
43
|
-
import { translationKey } from '../../translations';
|
|
44
|
-
import { tableButtons, tableControls } from '../../util';
|
|
45
|
-
import { createOption, TableValueEditor, type TableCellEditorProps } from '../TableCellEditor';
|
|
46
49
|
|
|
47
50
|
//
|
|
48
51
|
// Table.Root
|
|
@@ -50,15 +53,17 @@ import { createOption, TableValueEditor, type TableCellEditorProps } from '../Ta
|
|
|
50
53
|
|
|
51
54
|
export type TableRootProps = PropsWithChildren<{ role?: string }>;
|
|
52
55
|
|
|
53
|
-
const TableRoot = ({ children, role }: TableRootProps) => {
|
|
56
|
+
const TableRoot = ({ children, role = 'article' }: TableRootProps) => {
|
|
54
57
|
return (
|
|
55
58
|
<div
|
|
56
59
|
role='none'
|
|
57
60
|
className={mx(
|
|
58
|
-
'relative !border-separator',
|
|
59
|
-
role === '
|
|
60
|
-
|
|
61
|
-
|
|
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',
|
|
62
67
|
)}
|
|
63
68
|
>
|
|
64
69
|
{children}
|
|
@@ -72,6 +77,7 @@ const TableRoot = ({ children, role }: TableRootProps) => {
|
|
|
72
77
|
|
|
73
78
|
export type TableController = {
|
|
74
79
|
update?: (cell?: DxGridPosition) => void;
|
|
80
|
+
focusDraft?: () => void;
|
|
75
81
|
};
|
|
76
82
|
|
|
77
83
|
export type TableMainProps = {
|
|
@@ -122,30 +128,46 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
122
128
|
/**
|
|
123
129
|
* Provides an external controller that can be called to repaint the table.
|
|
124
130
|
*/
|
|
125
|
-
useImperativeHandle<TableController, TableController>(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return {};
|
|
130
|
-
}
|
|
131
|
+
useImperativeHandle<TableController, TableController>(forwardedRef, () => {
|
|
132
|
+
if (!presentation || !dxGrid) {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
131
135
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
+
}
|
|
142
164
|
},
|
|
143
|
-
[
|
|
165
|
+
[model, dxGrid],
|
|
144
166
|
);
|
|
145
167
|
|
|
146
168
|
const handleGridClick = useCallback(
|
|
147
169
|
(event: MouseEvent) => {
|
|
148
|
-
const rowIndex = safeParseInt((event.target as HTMLElement).ariaRowIndex ?? '');
|
|
170
|
+
const rowIndex = safeParseInt((event.target as HTMLElement).closest('[aria-rowindex]')?.ariaRowIndex ?? '');
|
|
149
171
|
if (rowIndex != null) {
|
|
150
172
|
if (onRowClick) {
|
|
151
173
|
const row = model?.getRowAt(rowIndex);
|
|
@@ -192,14 +214,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
192
214
|
break;
|
|
193
215
|
}
|
|
194
216
|
case 'saveDraftRow': {
|
|
195
|
-
|
|
196
|
-
const didCommitSuccessfully = model.commitDraftRow(data.rowIndex);
|
|
197
|
-
if (dxGrid && didCommitSuccessfully) {
|
|
198
|
-
requestAnimationFrame(() => {
|
|
199
|
-
dxGrid.scrollToEndRow();
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
217
|
+
handleSaveDraftRow();
|
|
203
218
|
break;
|
|
204
219
|
}
|
|
205
220
|
}
|
|
@@ -240,32 +255,23 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
240
255
|
);
|
|
241
256
|
|
|
242
257
|
const handleFocus = useCallback<NonNullable<TableCellEditorProps['onFocus']>>(
|
|
243
|
-
(increment, delta) => {
|
|
244
|
-
if (dxGrid) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// TODO(burdon): Insert row only if bottom row isn't completely blank already.
|
|
258
|
-
if (model && cell.row === model.getRowCount() - 1) {
|
|
259
|
-
model.insertRow();
|
|
260
|
-
if (dxGrid) {
|
|
261
|
-
requestAnimationFrame(() => {
|
|
262
|
-
dxGrid?.scrollToRow(cell.row + 1);
|
|
263
|
-
dxGrid?.refocus('row', 1);
|
|
264
|
-
});
|
|
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);
|
|
265
271
|
}
|
|
266
272
|
}
|
|
267
273
|
},
|
|
268
|
-
[
|
|
274
|
+
[dxGrid, model],
|
|
269
275
|
);
|
|
270
276
|
|
|
271
277
|
const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
|
|
@@ -282,7 +288,12 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
282
288
|
switch (event.key) {
|
|
283
289
|
case 'Backspace':
|
|
284
290
|
case 'Delete': {
|
|
285
|
-
|
|
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
|
+
}
|
|
286
297
|
break;
|
|
287
298
|
}
|
|
288
299
|
}
|
|
@@ -321,7 +332,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
321
332
|
const handleQuery = useCallback<NonNullable<TableCellEditorProps['onQuery']>>(
|
|
322
333
|
async ({ field, props }, text) => {
|
|
323
334
|
if (model && props.referenceSchema && field.referencePath) {
|
|
324
|
-
const space = model.
|
|
335
|
+
const space = getSpace(model.view);
|
|
325
336
|
invariant(space);
|
|
326
337
|
|
|
327
338
|
let schema;
|
|
@@ -363,6 +374,11 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
363
374
|
[model, client, t],
|
|
364
375
|
);
|
|
365
376
|
|
|
377
|
+
const handleSave = useCallback(() => {
|
|
378
|
+
dxGrid?.updateCells(true);
|
|
379
|
+
dxGrid?.requestUpdate();
|
|
380
|
+
}, [dxGrid]);
|
|
381
|
+
|
|
366
382
|
if (!model || !modals) {
|
|
367
383
|
return <span role='none' className='attention-surface' />;
|
|
368
384
|
}
|
|
@@ -373,20 +389,20 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
373
389
|
model={model}
|
|
374
390
|
modals={modals}
|
|
375
391
|
schema={schema}
|
|
376
|
-
onEnter={handleEnter}
|
|
377
392
|
onFocus={handleFocus}
|
|
378
393
|
onQuery={handleQuery}
|
|
394
|
+
onSave={handleSave}
|
|
379
395
|
/>
|
|
380
396
|
<Grid.Content
|
|
381
397
|
className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
|
|
382
398
|
frozen={frozen}
|
|
383
399
|
columns={model.columnMeta.value}
|
|
384
400
|
limitRows={model.getRowCount() ?? 0}
|
|
385
|
-
limitColumns={model.
|
|
401
|
+
limitColumns={model.projection.fields.length}
|
|
386
402
|
overscroll='trap'
|
|
387
403
|
onAxisResize={handleAxisResize}
|
|
388
404
|
onClick={handleGridClick}
|
|
389
|
-
|
|
405
|
+
onKeyDownCapture={handleKeyDown}
|
|
390
406
|
onWheelCapture={handleWheel}
|
|
391
407
|
ref={setDxGrid}
|
|
392
408
|
/>
|
|
@@ -4,7 +4,7 @@
|
|
|
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;
|