@dxos/react-ui-table 0.8.3 → 0.8.4-main.28f8d3d
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 +728 -2104
- 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 +728 -2104
- 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 +2 -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 +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/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +13 -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 +58 -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 +4 -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 +41 -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 +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 +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/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 +52 -40
- 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 +4 -3
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +37 -37
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +109 -148
- package/src/components/Table/Table.tsx +95 -66
- package/src/components/TableCellEditor/CellValidationMessage.tsx +7 -2
- package/src/components/TableCellEditor/FormCellEditor.tsx +30 -21
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +27 -30
- package/src/components/TableCellEditor/TableCellEditor.tsx +88 -34
- 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/index.ts +1 -0
- package/src/hooks/useAddRow.ts +36 -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 +8 -8
- package/src/model/table-model.ts +278 -86
- package/src/model/table-presentation.test.ts +8 -8
- package/src/model/table-presentation.ts +240 -135
- package/src/model/table-sorting.ts +11 -11
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +113 -20
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +123 -0
- package/src/translations.ts +7 -5
- 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 +26 -2
- package/src/util/table-controls.ts +4 -1
- package/src/util/table.ts +37 -111
- 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.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/playwright/playwright.config.cts +0 -18
- 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 {
|
|
27
|
-
import {
|
|
28
|
-
import
|
|
29
|
-
import { TableType } from '../../types';
|
|
30
|
-
import { initializeTable } from '../../util';
|
|
31
|
+
import { useTestTableModel } from '../../testing';
|
|
32
|
+
import { translations } from '../../translations';
|
|
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
|
-
if (space && schema) {
|
|
77
|
-
space.db.add(live(schema, {}));
|
|
78
|
-
}
|
|
79
|
-
}, [space, schema]);
|
|
80
|
-
|
|
81
|
-
const handleDeleteRows = useCallback(
|
|
82
|
-
(_: number, objects: any[]) => {
|
|
83
|
-
for (const object of objects) {
|
|
84
|
-
space?.db.remove(object);
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
[space],
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const handleDeleteColumn = useCallback(
|
|
91
|
-
(fieldId: string) => {
|
|
92
|
-
if (projection) {
|
|
93
|
-
projection.deleteFieldProjection(fieldId);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
[projection],
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const model = useTableModel({
|
|
100
|
-
table,
|
|
101
|
-
projection,
|
|
102
|
-
features,
|
|
103
|
-
rows: filteredObjects,
|
|
104
|
-
onInsertRow: handleInsertRow,
|
|
105
|
-
onDeleteRows: handleDeleteRows,
|
|
106
|
-
onDeleteColumn: handleDeleteColumn,
|
|
107
|
-
onCellUpdate: handleCellUpdate,
|
|
108
|
-
onRowOrderChange: handleRowOrderChange,
|
|
109
|
-
});
|
|
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>
|
|
@@ -209,26 +161,36 @@ type StoryProps = { rows?: number };
|
|
|
209
161
|
const meta: Meta<StoryProps> = {
|
|
210
162
|
title: 'ui/react-ui-table/Table',
|
|
211
163
|
render: DefaultStory,
|
|
212
|
-
parameters: {
|
|
164
|
+
parameters: {
|
|
165
|
+
translations,
|
|
166
|
+
layout: 'fullscreen',
|
|
167
|
+
controls: {
|
|
168
|
+
disable: true,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
213
171
|
decorators: [
|
|
172
|
+
withTheme,
|
|
173
|
+
withLayout({ fullscreen: true }),
|
|
214
174
|
withClientProvider({
|
|
215
|
-
types: [
|
|
175
|
+
types: [DataType.View, TableView],
|
|
216
176
|
createIdentity: true,
|
|
217
177
|
createSpace: true,
|
|
218
178
|
onSpaceCreated: async ({ client, space }) => {
|
|
219
|
-
const
|
|
220
|
-
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
|
+
|
|
221
183
|
Array.from({ length: 10 }).map(() => {
|
|
222
184
|
return space.db.add(
|
|
223
|
-
|
|
224
|
-
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(),
|
|
225
189
|
}),
|
|
226
190
|
);
|
|
227
191
|
});
|
|
228
192
|
},
|
|
229
193
|
}),
|
|
230
|
-
withLayout({ fullscreen: true }),
|
|
231
|
-
withTheme,
|
|
232
194
|
],
|
|
233
195
|
};
|
|
234
196
|
|
|
@@ -241,18 +203,17 @@ export const StaticSchema: StoryObj = {
|
|
|
241
203
|
parameters: { translations },
|
|
242
204
|
decorators: [
|
|
243
205
|
withClientProvider({
|
|
244
|
-
types: [
|
|
206
|
+
types: [DataType.View, TableView, Testing.Contact, Testing.Organization],
|
|
245
207
|
createIdentity: true,
|
|
246
208
|
createSpace: true,
|
|
247
209
|
onSpaceCreated: async ({ client, space }) => {
|
|
248
|
-
const
|
|
249
|
-
|
|
210
|
+
const { view } = await createTable({ client, space, typename: Testing.Contact.typename });
|
|
211
|
+
space.db.add(view);
|
|
250
212
|
|
|
251
213
|
const factory = createObjectFactory(space.db, faker as any);
|
|
252
214
|
await factory([
|
|
253
|
-
|
|
215
|
+
{ type: Testing.Contact, count: 10 },
|
|
254
216
|
// { type: Testing.Organization, count: 1 },
|
|
255
|
-
{ type: ContactWithArrayOfEmails, count: 10 },
|
|
256
217
|
]);
|
|
257
218
|
},
|
|
258
219
|
}),
|
|
@@ -283,12 +244,12 @@ export const ArrayOfObjects: StoryObj = {
|
|
|
283
244
|
parameters: { translations },
|
|
284
245
|
decorators: [
|
|
285
246
|
withClientProvider({
|
|
286
|
-
types: [
|
|
247
|
+
types: [DataType.View, TableView, Testing.Contact, Testing.Organization, ContactWithArrayOfEmails],
|
|
287
248
|
createIdentity: true,
|
|
288
249
|
createSpace: true,
|
|
289
250
|
onSpaceCreated: async ({ client, space }) => {
|
|
290
|
-
const
|
|
291
|
-
|
|
251
|
+
const { view } = await createTable({ client, space, typename: ContactWithArrayOfEmails.typename });
|
|
252
|
+
space.db.add(view);
|
|
292
253
|
|
|
293
254
|
const factory = createObjectFactory(space.db, faker as any);
|
|
294
255
|
await factory([
|
|
@@ -309,7 +270,7 @@ export const Tags: Meta<StoryProps> = {
|
|
|
309
270
|
parameters: { translations },
|
|
310
271
|
decorators: [
|
|
311
272
|
withClientProvider({
|
|
312
|
-
types: [
|
|
273
|
+
types: [DataType.View, TableView],
|
|
313
274
|
createIdentity: true,
|
|
314
275
|
createSpace: true,
|
|
315
276
|
onSpaceCreated: async ({ client, space }) => {
|
|
@@ -340,8 +301,8 @@ export const Tags: Meta<StoryProps> = {
|
|
|
340
301
|
const [storedSchema] = await space.db.schemaRegistry.register([schema]);
|
|
341
302
|
|
|
342
303
|
// Initialize table.
|
|
343
|
-
const
|
|
344
|
-
|
|
304
|
+
const { view } = await createTable({ client, space, typename });
|
|
305
|
+
space.db.add(view);
|
|
345
306
|
|
|
346
307
|
// Populate.
|
|
347
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 = {
|
|
@@ -91,6 +97,8 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
91
97
|
const { t } = useTranslation(translationKey);
|
|
92
98
|
const modals = useMemo(() => new ModalController(), []);
|
|
93
99
|
|
|
100
|
+
const draftRowCount = model?.getDraftRowCount() ?? 0;
|
|
101
|
+
|
|
94
102
|
const frozen = useMemo(() => {
|
|
95
103
|
const noActionColumn =
|
|
96
104
|
model?.features.dataEditable === false &&
|
|
@@ -99,10 +107,11 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
99
107
|
|
|
100
108
|
return {
|
|
101
109
|
frozenRowsStart: 1,
|
|
110
|
+
frozenRowsEnd: draftRowCount,
|
|
102
111
|
frozenColsStart: model?.features.selection.enabled ? 1 : 0,
|
|
103
112
|
frozenColsEnd: noActionColumn ? 0 : 1,
|
|
104
113
|
};
|
|
105
|
-
}, [model]);
|
|
114
|
+
}, [model, draftRowCount]);
|
|
106
115
|
|
|
107
116
|
const getCells = useCallback<NonNullable<GridContentProps['getCells']>>(
|
|
108
117
|
(range: DxGridPlaneRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
|
|
@@ -119,30 +128,46 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
119
128
|
/**
|
|
120
129
|
* Provides an external controller that can be called to repaint the table.
|
|
121
130
|
*/
|
|
122
|
-
useImperativeHandle<TableController, TableController>(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return {};
|
|
127
|
-
}
|
|
131
|
+
useImperativeHandle<TableController, TableController>(forwardedRef, () => {
|
|
132
|
+
if (!presentation || !dxGrid) {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
+
}
|
|
139
164
|
},
|
|
140
|
-
[
|
|
165
|
+
[model, dxGrid],
|
|
141
166
|
);
|
|
142
167
|
|
|
143
168
|
const handleGridClick = useCallback(
|
|
144
169
|
(event: MouseEvent) => {
|
|
145
|
-
const rowIndex = safeParseInt((event.target as HTMLElement).ariaRowIndex ?? '');
|
|
170
|
+
const rowIndex = safeParseInt((event.target as HTMLElement).closest('[aria-rowindex]')?.ariaRowIndex ?? '');
|
|
146
171
|
if (rowIndex != null) {
|
|
147
172
|
if (onRowClick) {
|
|
148
173
|
const row = model?.getRowAt(rowIndex);
|
|
@@ -188,6 +213,10 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
188
213
|
model?.sorting?.toggleSort(data.fieldId);
|
|
189
214
|
break;
|
|
190
215
|
}
|
|
216
|
+
case 'saveDraftRow': {
|
|
217
|
+
handleSaveDraftRow();
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
191
220
|
}
|
|
192
221
|
return;
|
|
193
222
|
}
|
|
@@ -222,36 +251,27 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
222
251
|
}
|
|
223
252
|
}
|
|
224
253
|
},
|
|
225
|
-
[model, modals],
|
|
254
|
+
[model, modals, dxGrid],
|
|
226
255
|
);
|
|
227
256
|
|
|
228
257
|
const handleFocus = useCallback<NonNullable<TableCellEditorProps['onFocus']>>(
|
|
229
|
-
(increment, delta) => {
|
|
230
|
-
if (dxGrid) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
});
|
|
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);
|
|
251
271
|
}
|
|
252
272
|
}
|
|
253
273
|
},
|
|
254
|
-
[
|
|
274
|
+
[dxGrid, model],
|
|
255
275
|
);
|
|
256
276
|
|
|
257
277
|
const handleKeyDown = useCallback<NonNullable<GridContentProps['onKeyDown']>>(
|
|
@@ -268,7 +288,12 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
268
288
|
switch (event.key) {
|
|
269
289
|
case 'Backspace':
|
|
270
290
|
case 'Delete': {
|
|
271
|
-
|
|
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
|
+
}
|
|
272
297
|
break;
|
|
273
298
|
}
|
|
274
299
|
}
|
|
@@ -307,7 +332,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
307
332
|
const handleQuery = useCallback<NonNullable<TableCellEditorProps['onQuery']>>(
|
|
308
333
|
async ({ field, props }, text) => {
|
|
309
334
|
if (model && props.referenceSchema && field.referencePath) {
|
|
310
|
-
const space = model.
|
|
335
|
+
const space = getSpace(model.view);
|
|
311
336
|
invariant(space);
|
|
312
337
|
|
|
313
338
|
let schema;
|
|
@@ -349,6 +374,11 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
349
374
|
[model, client, t],
|
|
350
375
|
);
|
|
351
376
|
|
|
377
|
+
const handleSave = useCallback(() => {
|
|
378
|
+
dxGrid?.updateCells(true);
|
|
379
|
+
dxGrid?.requestUpdate();
|
|
380
|
+
}, [dxGrid]);
|
|
381
|
+
|
|
352
382
|
if (!model || !modals) {
|
|
353
383
|
return <span role='none' className='attention-surface' />;
|
|
354
384
|
}
|
|
@@ -359,21 +389,20 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
359
389
|
model={model}
|
|
360
390
|
modals={modals}
|
|
361
391
|
schema={schema}
|
|
362
|
-
onEnter={handleEnter}
|
|
363
392
|
onFocus={handleFocus}
|
|
364
393
|
onQuery={handleQuery}
|
|
394
|
+
onSave={handleSave}
|
|
365
395
|
/>
|
|
366
396
|
<Grid.Content
|
|
367
397
|
className={mx('[--dx-grid-base:var(--baseSurface)]', gridSeparatorInlineEnd, gridSeparatorBlockEnd)}
|
|
368
398
|
frozen={frozen}
|
|
369
|
-
// getCells={getCells}
|
|
370
399
|
columns={model.columnMeta.value}
|
|
371
400
|
limitRows={model.getRowCount() ?? 0}
|
|
372
|
-
limitColumns={model.
|
|
401
|
+
limitColumns={model.projection.fields.length}
|
|
373
402
|
overscroll='trap'
|
|
374
403
|
onAxisResize={handleAxisResize}
|
|
375
404
|
onClick={handleGridClick}
|
|
376
|
-
|
|
405
|
+
onKeyDownCapture={handleKeyDown}
|
|
377
406
|
onWheelCapture={handleWheel}
|
|
378
407
|
ref={setDxGrid}
|
|
379
408
|
/>
|