@dxos/react-ui-table 0.8.4-main.406dc2a → 0.8.4-main.548089c
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-4K7JPALT.mjs → chunk-2PFAPQL4.mjs} +3 -3
- package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-OZTXLZGM.mjs → chunk-GS2UNGJK.mjs} +434 -463
- package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +41 -38
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +4 -4
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-MK6OVUF6.mjs → chunk-6POOFFYF.mjs} +3 -3
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-S56Z4V4X.mjs → chunk-TRR3C5NX.mjs} +434 -463
- package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +41 -38
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +4 -4
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +2 -3
- 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/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +2 -2
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +3 -3
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +2 -2
- package/dist/types/src/model/table-sorting.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/types/schema.d.ts +3 -3
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +2 -2
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +41 -40
- package/src/components/Table/DynamicTable.stories.tsx +3 -3
- package/src/components/Table/Relations.stories.tsx +16 -22
- package/src/components/Table/RowActionsMenu.tsx +21 -13
- package/src/components/Table/Table.stories.tsx +12 -12
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +6 -5
- package/src/components/TableToolbar/TableToolbar.tsx +31 -21
- package/src/hooks/useTableModel.ts +4 -4
- package/src/model/table-model.test.ts +2 -2
- package/src/model/table-model.ts +10 -7
- package/src/model/table-presentation.test.ts +2 -2
- package/src/model/table-sorting.ts +8 -8
- package/src/playwright/TableManager.ts +1 -1
- package/src/playwright/smoke.spec.ts +7 -5
- package/src/testing/useTestTableModel.ts +2 -2
- package/src/types/schema.ts +4 -4
- package/src/util/dynamic-table.ts +4 -5
- package/dist/lib/browser/chunk-4K7JPALT.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZTXLZGM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-S56Z4V4X.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-table",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.548089c",
|
|
4
4
|
"description": "A table component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -65,61 +65,62 @@
|
|
|
65
65
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
66
66
|
"date-fns": "^3.3.1",
|
|
67
67
|
"lodash.orderby": "^4.6.0",
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/echo": "0.8.4-main.
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/keys": "0.8.4-main.
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/random": "0.8.4-main.
|
|
80
|
-
"@dxos/react-
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/react-ui-
|
|
83
|
-
"@dxos/react-ui-
|
|
84
|
-
"@dxos/react-ui-
|
|
85
|
-
"@dxos/react-ui-
|
|
86
|
-
"@dxos/react-ui-
|
|
87
|
-
"@dxos/react-ui-
|
|
88
|
-
"@dxos/react-ui-
|
|
89
|
-
"@dxos/react-
|
|
90
|
-
"@dxos/
|
|
91
|
-
"@dxos/react-ui-types": "0.8.4-main.
|
|
92
|
-
"@dxos/
|
|
93
|
-
"@dxos/
|
|
68
|
+
"@dxos/context": "0.8.4-main.548089c",
|
|
69
|
+
"@dxos/async": "0.8.4-main.548089c",
|
|
70
|
+
"@dxos/debug": "0.8.4-main.548089c",
|
|
71
|
+
"@dxos/echo": "0.8.4-main.548089c",
|
|
72
|
+
"@dxos/invariant": "0.8.4-main.548089c",
|
|
73
|
+
"@dxos/effect": "0.8.4-main.548089c",
|
|
74
|
+
"@dxos/keys": "0.8.4-main.548089c",
|
|
75
|
+
"@dxos/lit-grid": "0.8.4-main.548089c",
|
|
76
|
+
"@dxos/echo-db": "0.8.4-main.548089c",
|
|
77
|
+
"@dxos/live-object": "0.8.4-main.548089c",
|
|
78
|
+
"@dxos/log": "0.8.4-main.548089c",
|
|
79
|
+
"@dxos/random": "0.8.4-main.548089c",
|
|
80
|
+
"@dxos/react-client": "0.8.4-main.548089c",
|
|
81
|
+
"@dxos/plugin-search": "0.8.4-main.548089c",
|
|
82
|
+
"@dxos/react-ui-attention": "0.8.4-main.548089c",
|
|
83
|
+
"@dxos/react-ui-editor": "0.8.4-main.548089c",
|
|
84
|
+
"@dxos/react-ui-components": "0.8.4-main.548089c",
|
|
85
|
+
"@dxos/react-ui-form": "0.8.4-main.548089c",
|
|
86
|
+
"@dxos/react-ui-grid": "0.8.4-main.548089c",
|
|
87
|
+
"@dxos/react-ui-menu": "0.8.4-main.548089c",
|
|
88
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.548089c",
|
|
89
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.548089c",
|
|
90
|
+
"@dxos/types": "0.8.4-main.548089c",
|
|
91
|
+
"@dxos/react-ui-types": "0.8.4-main.548089c",
|
|
92
|
+
"@dxos/util": "0.8.4-main.548089c",
|
|
93
|
+
"@dxos/react-ui-stack": "0.8.4-main.548089c",
|
|
94
|
+
"@dxos/schema": "0.8.4-main.548089c"
|
|
94
95
|
},
|
|
95
96
|
"devDependencies": {
|
|
96
|
-
"@effect-
|
|
97
|
+
"@effect-atom/atom-react": "^0.3.4",
|
|
97
98
|
"@effect/platform": "0.92.1",
|
|
98
99
|
"@types/lodash.orderby": "^4.6.9",
|
|
99
100
|
"@types/react": "~19.2.2",
|
|
100
|
-
"@types/react-dom": "~19.2.
|
|
101
|
+
"@types/react-dom": "~19.2.2",
|
|
101
102
|
"effect": "3.18.3",
|
|
102
103
|
"fast-check": "^3.19.0",
|
|
103
104
|
"react": "~19.2.0",
|
|
104
105
|
"react-dom": "~19.2.0",
|
|
105
106
|
"vite": "7.1.9",
|
|
106
|
-
"@dxos/
|
|
107
|
-
"@dxos/plugin-search": "0.8.4-main.
|
|
108
|
-
"@dxos/
|
|
109
|
-
"@dxos/
|
|
110
|
-
"@dxos/
|
|
111
|
-
"@dxos/
|
|
112
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
113
|
-
"@dxos/test-utils": "0.8.4-main.
|
|
107
|
+
"@dxos/lit-ui": "0.8.4-main.548089c",
|
|
108
|
+
"@dxos/plugin-search": "0.8.4-main.548089c",
|
|
109
|
+
"@dxos/echo-signals": "0.8.4-main.548089c",
|
|
110
|
+
"@dxos/random": "0.8.4-main.548089c",
|
|
111
|
+
"@dxos/react-ui": "0.8.4-main.548089c",
|
|
112
|
+
"@dxos/react-ui-theme": "0.8.4-main.548089c",
|
|
113
|
+
"@dxos/storybook-utils": "0.8.4-main.548089c",
|
|
114
|
+
"@dxos/test-utils": "0.8.4-main.548089c"
|
|
114
115
|
},
|
|
115
116
|
"peerDependencies": {
|
|
116
|
-
"@effect-
|
|
117
|
+
"@effect-atom/atom-react": "^0.3.4",
|
|
117
118
|
"@effect/platform": "^0.80.12",
|
|
118
119
|
"effect": "^3.13.3",
|
|
119
120
|
"react": "^19.0.0",
|
|
120
121
|
"react-dom": "^19.0.0",
|
|
121
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
122
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
122
|
+
"@dxos/react-ui": "0.8.4-main.548089c",
|
|
123
|
+
"@dxos/react-ui-theme": "0.8.4-main.548089c"
|
|
123
124
|
},
|
|
124
125
|
"publishConfig": {
|
|
125
126
|
"access": "public"
|
|
@@ -132,7 +132,7 @@ export const WithEchoSchema: StoryObj = {
|
|
|
132
132
|
render: () => {
|
|
133
133
|
const client = useClient();
|
|
134
134
|
const { space } = useClientProvider();
|
|
135
|
-
const schema = useSchema(client, space, Testing.
|
|
135
|
+
const schema = useSchema(client, space, Testing.Person.typename);
|
|
136
136
|
const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
|
|
137
137
|
if (!schema) {
|
|
138
138
|
return <div>Loading schema...</div>;
|
|
@@ -142,13 +142,13 @@ export const WithEchoSchema: StoryObj = {
|
|
|
142
142
|
},
|
|
143
143
|
decorators: [
|
|
144
144
|
withClientProvider({
|
|
145
|
-
types: [Testing.
|
|
145
|
+
types: [Testing.Person],
|
|
146
146
|
createIdentity: true,
|
|
147
147
|
createSpace: true,
|
|
148
148
|
onCreateSpace: async ({ space }) => {
|
|
149
149
|
Array.from({ length: 10 }).forEach(() => {
|
|
150
150
|
space.db.add(
|
|
151
|
-
Obj.make(Testing.
|
|
151
|
+
Obj.make(Testing.Person, {
|
|
152
152
|
name: faker.person.fullName(),
|
|
153
153
|
email: faker.internet.email(),
|
|
154
154
|
}),
|
|
@@ -15,10 +15,11 @@ import { faker } from '@dxos/random';
|
|
|
15
15
|
import { useClient } from '@dxos/react-client';
|
|
16
16
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
17
17
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
18
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
18
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
19
19
|
import { translations as formTranslations } from '@dxos/react-ui-form';
|
|
20
|
-
import {
|
|
20
|
+
import { View } from '@dxos/schema';
|
|
21
21
|
import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
|
|
22
|
+
import { Organization, Person } from '@dxos/types';
|
|
22
23
|
|
|
23
24
|
import { useTableModel } from '../../hooks';
|
|
24
25
|
import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
|
|
@@ -37,7 +38,7 @@ const generator: ValueGenerator = faker as any;
|
|
|
37
38
|
const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
38
39
|
const client = useClient();
|
|
39
40
|
const { space } = useClientProvider();
|
|
40
|
-
const [view, setView] = useState<
|
|
41
|
+
const [view, setView] = useState<View.View>();
|
|
41
42
|
const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
|
|
42
43
|
|
|
43
44
|
const features = useMemo<TableFeatures>(
|
|
@@ -87,8 +88,8 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
87
88
|
|
|
88
89
|
const DefaultStory = () => {
|
|
89
90
|
const client = useClient();
|
|
90
|
-
const { model: orgModel, presentation: orgPresentation } = useTestModel(
|
|
91
|
-
const { model: contactModel, presentation: contactPresentation } = useTestModel(
|
|
91
|
+
const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
|
|
92
|
+
const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
|
|
92
93
|
const { space } = useClientProvider();
|
|
93
94
|
|
|
94
95
|
const handleCreate = useCallback(
|
|
@@ -103,7 +104,7 @@ const DefaultStory = () => {
|
|
|
103
104
|
<TableComponent.Root>
|
|
104
105
|
<TableComponent.Main
|
|
105
106
|
model={orgModel}
|
|
106
|
-
schema={
|
|
107
|
+
schema={Organization.Organization}
|
|
107
108
|
presentation={orgPresentation}
|
|
108
109
|
onCreate={handleCreate}
|
|
109
110
|
client={client}
|
|
@@ -114,7 +115,7 @@ const DefaultStory = () => {
|
|
|
114
115
|
<TableComponent.Root>
|
|
115
116
|
<TableComponent.Main
|
|
116
117
|
model={contactModel}
|
|
117
|
-
schema={
|
|
118
|
+
schema={Person.Person}
|
|
118
119
|
presentation={contactPresentation}
|
|
119
120
|
onCreate={handleCreate}
|
|
120
121
|
client={client}
|
|
@@ -131,8 +132,10 @@ const meta = {
|
|
|
131
132
|
render: DefaultStory,
|
|
132
133
|
decorators: [
|
|
133
134
|
withTheme,
|
|
135
|
+
// TODO(thure): Shouldn’t `layout: 'fullscreen'` below make this unnecessary?
|
|
136
|
+
withLayout({ classNames: 'fixed inset-0' }),
|
|
134
137
|
withClientProvider({
|
|
135
|
-
types: [
|
|
138
|
+
types: [View.View, Organization.Organization, Person.Person, Table.Table],
|
|
136
139
|
createIdentity: true,
|
|
137
140
|
createSpace: true,
|
|
138
141
|
}),
|
|
@@ -149,9 +152,7 @@ export default meta;
|
|
|
149
152
|
type Story = StoryObj<typeof meta>;
|
|
150
153
|
|
|
151
154
|
export const Default: Story = {
|
|
152
|
-
|
|
153
|
-
// Most typical failure is `targetCell` returns `null`.
|
|
154
|
-
_play: async ({ canvasElement }: any) => {
|
|
155
|
+
play: async ({ canvasElement }: any) => {
|
|
155
156
|
const canvas = within(canvasElement);
|
|
156
157
|
const body = within(document.body);
|
|
157
158
|
|
|
@@ -165,11 +166,8 @@ export const Default: Story = {
|
|
|
165
166
|
// Scroll to the relations column (column 4) - this mimics the scrollToColumn call
|
|
166
167
|
(secondGrid.closest('dx-grid') as DxGrid).scrollToColumn(4);
|
|
167
168
|
|
|
168
|
-
// Wait for scroll to complete
|
|
169
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
170
|
-
|
|
171
169
|
// Find and click the target cell (first row, relations column)
|
|
172
|
-
const targetCell = secondGrid.
|
|
170
|
+
const targetCell = await within(secondGrid).findByTestId('grid.4.0', undefined, { timeout: 10_000 });
|
|
173
171
|
await expect(targetCell).toBeVisible();
|
|
174
172
|
|
|
175
173
|
// Click to focus the cell
|
|
@@ -185,11 +183,9 @@ export const Default: Story = {
|
|
|
185
183
|
await userEvent.click(searchField);
|
|
186
184
|
|
|
187
185
|
// Get the first organization name from the first table to search for
|
|
188
|
-
const orgCell = firstGrid.
|
|
189
|
-
'[data-dx-grid-plane="grid"] [aria-rowindex="0"][aria-colindex="0"] .dx-grid__cell__content',
|
|
190
|
-
) as HTMLElement;
|
|
186
|
+
const orgCell = await within(firstGrid).findByTestId('grid.0.0', undefined, { timeout: 10_000 });
|
|
191
187
|
|
|
192
|
-
const orgName = orgCell.textContent;
|
|
188
|
+
const orgName = (orgCell.querySelector('.dx-grid__cell__content') as HTMLElement).textContent;
|
|
193
189
|
// Type the first 4 characters to search
|
|
194
190
|
await userEvent.type(searchField, orgName.substring(0, 4));
|
|
195
191
|
|
|
@@ -209,9 +205,7 @@ export const Default: Story = {
|
|
|
209
205
|
|
|
210
206
|
// Test object creation (new relations) - equivalent to "new relations work as expected" test
|
|
211
207
|
// Find a different cell to test object creation (second row, relations column)
|
|
212
|
-
const newTargetCell = secondGrid.
|
|
213
|
-
'[data-dx-grid-plane="grid"] [aria-rowindex="1"][aria-colindex="4"]',
|
|
214
|
-
);
|
|
208
|
+
const newTargetCell = await within(secondGrid).findByTestId('grid.4.1', undefined, { timeout: 10_000 });
|
|
215
209
|
await expect(newTargetCell).toBeTruthy();
|
|
216
210
|
|
|
217
211
|
// Click to focus the cell
|
|
@@ -23,24 +23,32 @@ export const RowActionsMenu = ({ model, modals }: RowActionsMenuProps) => {
|
|
|
23
23
|
<DropdownMenu.VirtualTrigger virtualRef={modals.trigger} />
|
|
24
24
|
<DropdownMenu.Content>
|
|
25
25
|
<DropdownMenu.Viewport>
|
|
26
|
+
{/* Custom actions */}
|
|
27
|
+
{model.rowActions?.length > 0 && (
|
|
28
|
+
<>
|
|
29
|
+
<DropdownMenu.Group>
|
|
30
|
+
{model.rowActions?.map((action) => (
|
|
31
|
+
<DropdownMenu.Item
|
|
32
|
+
key={action.id}
|
|
33
|
+
data-testid={`row-action-${action.id}`}
|
|
34
|
+
onClick={() => {
|
|
35
|
+
modals.close();
|
|
36
|
+
model.handleRowAction(action.id, state.rowIndex);
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
{toLocalizedString(action.label, t)}
|
|
40
|
+
</DropdownMenu.Item>
|
|
41
|
+
))}
|
|
42
|
+
</DropdownMenu.Group>
|
|
43
|
+
<DropdownMenu.Separator />
|
|
44
|
+
</>
|
|
45
|
+
)}
|
|
46
|
+
{/* Default actions */}
|
|
26
47
|
{model.features.dataEditable !== false && (
|
|
27
48
|
<DropdownMenu.Item data-testid='row-menu-delete' onClick={() => model.deleteRow(state.rowIndex)}>
|
|
28
49
|
{t(hasSelection ? 'bulk delete row label' : 'delete row label')}
|
|
29
50
|
</DropdownMenu.Item>
|
|
30
51
|
)}
|
|
31
|
-
{/* Custom actions */}
|
|
32
|
-
{model.rowActions?.map((action) => (
|
|
33
|
-
<DropdownMenu.Item
|
|
34
|
-
key={action.id}
|
|
35
|
-
data-testid={`row-action-${action.id}`}
|
|
36
|
-
onClick={() => {
|
|
37
|
-
modals.close();
|
|
38
|
-
model.handleRowAction(action.id, state.rowIndex);
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
{toLocalizedString(action.label, t)}
|
|
42
|
-
</DropdownMenu.Item>
|
|
43
|
-
))}
|
|
44
52
|
</DropdownMenu.Viewport>
|
|
45
53
|
<DropdownMenu.Arrow />
|
|
46
54
|
</DropdownMenu.Content>
|
|
@@ -24,7 +24,7 @@ import { withClientProvider } from '@dxos/react-client/testing';
|
|
|
24
24
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
25
25
|
import { ViewEditor } from '@dxos/react-ui-form';
|
|
26
26
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
27
|
-
import {
|
|
27
|
+
import { View, getSchemaFromPropertyDefinitions, getTypenameFromQuery } from '@dxos/schema';
|
|
28
28
|
import { Testing, createObjectFactory } from '@dxos/schema/testing';
|
|
29
29
|
|
|
30
30
|
import { useTestTableModel } from '../../testing';
|
|
@@ -70,7 +70,7 @@ const StoryViewEditor = ({
|
|
|
70
70
|
space,
|
|
71
71
|
handleDeleteColumn,
|
|
72
72
|
}: {
|
|
73
|
-
view?:
|
|
73
|
+
view?: View.View;
|
|
74
74
|
schema?: Schema.Schema.AnyNoContext;
|
|
75
75
|
space?: Space;
|
|
76
76
|
handleDeleteColumn: (fieldId: string) => void;
|
|
@@ -138,7 +138,7 @@ const DefaultStory = () => {
|
|
|
138
138
|
/>
|
|
139
139
|
</TableComponent.Root>
|
|
140
140
|
</div>
|
|
141
|
-
<div className='flex flex-col
|
|
141
|
+
<div className='flex flex-col bs-full border-l border-separator overflow-y-auto'>
|
|
142
142
|
<StoryViewEditor view={view} schema={schema} space={space} handleDeleteColumn={handleDeleteColumn} />
|
|
143
143
|
<SyntaxHighlighter language='json' className='text-xs'>
|
|
144
144
|
{JSON.stringify({ view, schema }, null, 2)}
|
|
@@ -162,15 +162,15 @@ const meta = {
|
|
|
162
162
|
decorators: [
|
|
163
163
|
withTheme,
|
|
164
164
|
withClientProvider({
|
|
165
|
-
types: [
|
|
165
|
+
types: [View.View, Table.Table],
|
|
166
166
|
createIdentity: true,
|
|
167
167
|
createSpace: true,
|
|
168
168
|
onCreateSpace: async ({ client, space }) => {
|
|
169
169
|
const [schema] = await space.db.schemaRegistry.register([TestSchema]);
|
|
170
170
|
const { view } = await Table.makeView({ client, space, typename: schema.typename });
|
|
171
171
|
view.projection.fields = [
|
|
172
|
-
view.projection.fields.find((field) => field.path === 'name')!,
|
|
173
|
-
...view.projection.fields.filter((field) => field.path !== 'name'),
|
|
172
|
+
view.projection.fields.find((field: any) => field.path === 'name')!,
|
|
173
|
+
...view.projection.fields.filter((field: any) => field.path !== 'name'),
|
|
174
174
|
];
|
|
175
175
|
|
|
176
176
|
space.db.add(view);
|
|
@@ -206,16 +206,16 @@ export const StaticSchema: StoryObj = {
|
|
|
206
206
|
render: DefaultStory,
|
|
207
207
|
decorators: [
|
|
208
208
|
withClientProvider({
|
|
209
|
-
types: [
|
|
209
|
+
types: [View.View, Table.Table],
|
|
210
210
|
createIdentity: true,
|
|
211
211
|
createSpace: true,
|
|
212
212
|
onCreateSpace: async ({ client, space }) => {
|
|
213
|
-
const { view } = await Table.makeView({ client, space, typename: Testing.
|
|
213
|
+
const { view } = await Table.makeView({ client, space, typename: Testing.Person.typename });
|
|
214
214
|
space.db.add(view);
|
|
215
215
|
|
|
216
216
|
const factory = createObjectFactory(space.db, faker as any);
|
|
217
217
|
await factory([
|
|
218
|
-
{ type: Testing.
|
|
218
|
+
{ type: Testing.Person, count: 10 },
|
|
219
219
|
// { type: Testing.Organization, count: 1 },
|
|
220
220
|
]);
|
|
221
221
|
},
|
|
@@ -248,7 +248,7 @@ export const ArrayOfObjects: StoryObj = {
|
|
|
248
248
|
render: DefaultStory,
|
|
249
249
|
decorators: [
|
|
250
250
|
withClientProvider({
|
|
251
|
-
types: [
|
|
251
|
+
types: [View.View, Table.Table, Testing.Person, Testing.Organization, ContactWithArrayOfEmails],
|
|
252
252
|
createIdentity: true,
|
|
253
253
|
createSpace: true,
|
|
254
254
|
onCreateSpace: async ({ client, space }) => {
|
|
@@ -257,7 +257,7 @@ export const ArrayOfObjects: StoryObj = {
|
|
|
257
257
|
|
|
258
258
|
const factory = createObjectFactory(space.db, faker as any);
|
|
259
259
|
await factory([
|
|
260
|
-
// { type: Testing.
|
|
260
|
+
// { type: Testing.Person, count: 10 },
|
|
261
261
|
// { type: Testing.Organization, count: 1 },
|
|
262
262
|
{ type: ContactWithArrayOfEmails, count: 10 },
|
|
263
263
|
]);
|
|
@@ -275,7 +275,7 @@ export const Tags: Meta<StoryProps> = {
|
|
|
275
275
|
render: DefaultStory,
|
|
276
276
|
decorators: [
|
|
277
277
|
withClientProvider({
|
|
278
|
-
types: [
|
|
278
|
+
types: [View.View, Table.Table],
|
|
279
279
|
createIdentity: true,
|
|
280
280
|
createSpace: true,
|
|
281
281
|
onCreateSpace: async ({ client, space }) => {
|
|
@@ -10,7 +10,8 @@ import { faker } from '@dxos/random';
|
|
|
10
10
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
11
11
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
12
|
import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
|
|
13
|
-
import {
|
|
13
|
+
import { View } from '@dxos/schema';
|
|
14
|
+
import { Task } from '@dxos/types';
|
|
14
15
|
|
|
15
16
|
import { useTestTableModel } from '../../testing';
|
|
16
17
|
import { translations } from '../../translations';
|
|
@@ -32,7 +33,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
32
33
|
return (
|
|
33
34
|
<div className='flex w-[300px] border border-separator' style={{ height: defaultRowSize }}>
|
|
34
35
|
<Grid.Root id='test' editing={editing}>
|
|
35
|
-
<TableCellEditor model={model} schema={
|
|
36
|
+
<TableCellEditor model={model} schema={Task.Task} />
|
|
36
37
|
</Grid.Root>
|
|
37
38
|
</div>
|
|
38
39
|
);
|
|
@@ -45,15 +46,15 @@ const meta = {
|
|
|
45
46
|
decorators: [
|
|
46
47
|
withTheme,
|
|
47
48
|
withClientProvider({
|
|
48
|
-
types: [
|
|
49
|
+
types: [View.View, Task.Task, Table.Table],
|
|
49
50
|
createIdentity: true,
|
|
50
51
|
createSpace: true,
|
|
51
52
|
onCreateSpace: async ({ client, space }) => {
|
|
52
|
-
const { view } = await Table.makeView({ client, space, typename:
|
|
53
|
+
const { view } = await Table.makeView({ client, space, typename: Task.Task.typename });
|
|
53
54
|
space.db.add(view);
|
|
54
55
|
Array.from({ length: 10 }).forEach(() => {
|
|
55
56
|
space.db.add(
|
|
56
|
-
Obj.make(
|
|
57
|
+
Obj.make(Task.Task, {
|
|
57
58
|
title: faker.person.fullName(),
|
|
58
59
|
status: faker.helpers.arrayElement(['todo', 'in-progress', 'done'] as const),
|
|
59
60
|
description: faker.lorem.sentence(),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
7
|
|
|
8
8
|
import { live } from '@dxos/live-object';
|
|
@@ -30,12 +30,14 @@ export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
|
|
|
30
30
|
|
|
31
31
|
type TableToolbarState = Partial<{ viewDirty: boolean }>;
|
|
32
32
|
|
|
33
|
+
// TODO(burdon): Radix style layout.
|
|
34
|
+
|
|
33
35
|
export type TableToolbarProps = ThemedClassName<
|
|
34
36
|
TableToolbarState & {
|
|
35
37
|
onAdd: () => void;
|
|
36
38
|
onSave: () => void;
|
|
37
39
|
attendableId?: string;
|
|
38
|
-
customActions?:
|
|
40
|
+
customActions?: Atom.Atom<ActionGraphProps>;
|
|
39
41
|
}
|
|
40
42
|
>;
|
|
41
43
|
|
|
@@ -46,33 +48,41 @@ const createTableToolbarActions = ({
|
|
|
46
48
|
customActions,
|
|
47
49
|
}: {
|
|
48
50
|
state: TableToolbarState;
|
|
49
|
-
onAdd
|
|
50
|
-
onSave
|
|
51
|
-
customActions?:
|
|
51
|
+
onAdd?: () => void;
|
|
52
|
+
onSave?: () => void;
|
|
53
|
+
customActions?: Atom.Atom<ActionGraphProps>;
|
|
52
54
|
}) =>
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
Atom.make((get) => {
|
|
56
|
+
const nodes: ActionGraphNodes = [];
|
|
57
|
+
if (onAdd) {
|
|
58
|
+
const add = createMenuAction<TableToolbarActionProperties>('add-row', onAdd, {
|
|
59
|
+
type: 'add-row' as const,
|
|
60
|
+
icon: 'ph--plus--regular',
|
|
61
|
+
label: ['add row', { ns: translationKey }],
|
|
62
|
+
testId: 'table.toolbar.add-row',
|
|
63
|
+
});
|
|
64
|
+
nodes.push(add);
|
|
65
|
+
}
|
|
66
|
+
if (onSave) {
|
|
67
|
+
const save = createMenuAction<TableToolbarActionProperties>('save-view', onSave, {
|
|
68
|
+
type: 'save-view' as const,
|
|
69
|
+
icon: 'ph--floppy-disk--regular',
|
|
70
|
+
label: ['save view label', { ns: translationKey }],
|
|
71
|
+
testId: 'table.toolbar.save-view',
|
|
72
|
+
iconOnly: false,
|
|
73
|
+
hidden: get(rxFromSignal(() => !state.viewDirty)),
|
|
74
|
+
});
|
|
75
|
+
nodes.push(save);
|
|
76
|
+
}
|
|
68
77
|
const gap = createGapSeparator();
|
|
69
|
-
|
|
78
|
+
nodes.push(...gap.nodes);
|
|
70
79
|
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
|
|
71
80
|
if (customActions) {
|
|
72
81
|
const custom = get(customActions);
|
|
73
82
|
nodes.push(...custom.nodes);
|
|
74
83
|
edges.push(...custom.edges);
|
|
75
84
|
}
|
|
85
|
+
|
|
76
86
|
return {
|
|
77
87
|
nodes,
|
|
78
88
|
edges,
|
|
@@ -6,17 +6,17 @@ import { effect } from '@preact/signals-core';
|
|
|
6
6
|
import orderBy from 'lodash.orderby';
|
|
7
7
|
import { useEffect, useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
|
+
import { Obj } from '@dxos/echo';
|
|
9
10
|
import { type JsonSchemaType } from '@dxos/echo/internal';
|
|
10
11
|
import { type Live } from '@dxos/live-object';
|
|
11
|
-
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
12
12
|
import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
|
|
13
|
-
import { type
|
|
13
|
+
import { type ProjectionModel, type View } from '@dxos/schema';
|
|
14
14
|
import { isNonNullable } from '@dxos/util';
|
|
15
15
|
|
|
16
16
|
import { TableModel, type TableModelProps, type TableRow, type TableRowAction } from '../model';
|
|
17
17
|
|
|
18
18
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
19
|
-
view?:
|
|
19
|
+
view?: View.View;
|
|
20
20
|
schema?: JsonSchemaType;
|
|
21
21
|
projection?: ProjectionModel;
|
|
22
22
|
rows?: Live<T>[];
|
|
@@ -39,7 +39,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
39
39
|
onRowAction,
|
|
40
40
|
...props
|
|
41
41
|
}: UseTableModelParams<T>): TableModel<T> | undefined => {
|
|
42
|
-
const selected = useSelected(view &&
|
|
42
|
+
const selected = useSelected(view && Obj.getDXN(view).toString(), 'multi');
|
|
43
43
|
const initialSelection = useMemo(() => selected, [view]);
|
|
44
44
|
|
|
45
45
|
const [model, setModel] = useState<TableModel<T>>();
|
|
@@ -12,7 +12,7 @@ import { updateCounter } from '@dxos/echo/testing';
|
|
|
12
12
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
13
13
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
14
14
|
import { live } from '@dxos/live-object';
|
|
15
|
-
import {
|
|
15
|
+
import { View } from '@dxos/schema';
|
|
16
16
|
|
|
17
17
|
import { Table } from '../types';
|
|
18
18
|
|
|
@@ -113,7 +113,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
113
113
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
114
114
|
const schema = createEchoSchema(Test);
|
|
115
115
|
const table = Table.make();
|
|
116
|
-
const view =
|
|
116
|
+
const view = View.make({
|
|
117
117
|
query: Query.select(Filter.type(schema)),
|
|
118
118
|
jsonSchema: schema.jsonSchema,
|
|
119
119
|
presentation: table,
|
package/src/model/table-model.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
import { invariant } from '@dxos/invariant';
|
|
19
19
|
import { ObjectId } from '@dxos/keys';
|
|
20
20
|
import { getSnapshot, isLiveObject } from '@dxos/live-object';
|
|
21
|
-
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
22
21
|
import { type Label } from '@dxos/react-ui';
|
|
23
22
|
import { formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
24
23
|
import {
|
|
@@ -28,11 +27,11 @@ import {
|
|
|
28
27
|
type DxGridPosition,
|
|
29
28
|
} from '@dxos/react-ui-grid';
|
|
30
29
|
import {
|
|
31
|
-
type DataType,
|
|
32
30
|
type FieldSortType,
|
|
33
31
|
ProjectionModel,
|
|
34
32
|
type PropertyType,
|
|
35
33
|
type ValidationError,
|
|
34
|
+
type View,
|
|
36
35
|
validateSchema,
|
|
37
36
|
} from '@dxos/schema';
|
|
38
37
|
|
|
@@ -78,7 +77,7 @@ const defaultFeatures: TableFeatures = {
|
|
|
78
77
|
export type InsertRowResult = 'draft' | 'final';
|
|
79
78
|
|
|
80
79
|
export type TableModelProps<T extends TableRow = TableRow> = {
|
|
81
|
-
view:
|
|
80
|
+
view: View.View;
|
|
82
81
|
schema: JsonSchemaType;
|
|
83
82
|
projection?: ProjectionModel;
|
|
84
83
|
features?: Partial<TableFeatures>;
|
|
@@ -96,7 +95,7 @@ export type TableModelProps<T extends TableRow = TableRow> = {
|
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
99
|
-
private readonly _view:
|
|
98
|
+
private readonly _view: View.View;
|
|
100
99
|
private readonly _projection: ProjectionModel;
|
|
101
100
|
private _table?: Table.Table;
|
|
102
101
|
|
|
@@ -176,10 +175,10 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
176
175
|
}
|
|
177
176
|
|
|
178
177
|
public get id(): string {
|
|
179
|
-
return
|
|
178
|
+
return Obj.getDXN(this._view).toString();
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
public get view():
|
|
181
|
+
public get view(): View.View {
|
|
183
182
|
return this._view;
|
|
184
183
|
}
|
|
185
184
|
|
|
@@ -480,7 +479,11 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
480
479
|
}
|
|
481
480
|
|
|
482
481
|
default: {
|
|
483
|
-
return formatForEditing({
|
|
482
|
+
return formatForEditing({
|
|
483
|
+
type: props.type,
|
|
484
|
+
format: props.format,
|
|
485
|
+
value,
|
|
486
|
+
});
|
|
484
487
|
}
|
|
485
488
|
}
|
|
486
489
|
};
|
|
@@ -9,7 +9,7 @@ import { Filter, Query } from '@dxos/echo';
|
|
|
9
9
|
import { TypedObject } from '@dxos/echo/internal';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
11
|
import { live } from '@dxos/live-object';
|
|
12
|
-
import {
|
|
12
|
+
import { View } from '@dxos/schema';
|
|
13
13
|
|
|
14
14
|
import { Table } from '../types';
|
|
15
15
|
|
|
@@ -96,7 +96,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
96
96
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
97
97
|
const schema = createEchoSchema(Test);
|
|
98
98
|
const table = Table.make();
|
|
99
|
-
const view =
|
|
99
|
+
const view = View.make({
|
|
100
100
|
query: Query.select(Filter.type(schema)),
|
|
101
101
|
jsonSchema: schema.jsonSchema,
|
|
102
102
|
presentation: table,
|