@dxos/react-ui-table 0.10.0 → 0.11.1
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/chunk-types.mjs +65 -0
- package/dist/lib/chunk-types.mjs.map +1 -0
- package/dist/lib/chunk-useTableModel.mjs +1595 -0
- package/dist/lib/chunk-useTableModel.mjs.map +1 -0
- package/dist/lib/index.mjs +1133 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing.mjs +88 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +30 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/types/src/components/Table/DynamicTable.d.ts +4 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +1 -0
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +3 -9
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +2 -0
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContext.d.ts +10 -0
- package/dist/types/src/components/Table/TableContext.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +1 -1
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/Table/index.d.ts +1 -0
- package/dist/types/src/components/Table/index.d.ts.map +1 -1
- 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/testing.d.ts +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +8 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +42 -7
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +20 -5
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-view-state.d.ts +5 -1
- package/dist/types/src/model/table-view-state.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +1 -1
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +5 -3
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +48 -50
- package/src/components/Table/DynamicTable.stories.tsx +59 -1
- package/src/components/Table/DynamicTable.tsx +16 -8
- package/src/components/Table/Relations.stories.tsx +2 -2
- package/src/components/Table/Table.stories.tsx +76 -2
- package/src/components/Table/Table.tsx +3 -15
- package/src/components/Table/TableContent.tsx +6 -5
- package/src/components/Table/TableContext.ts +21 -0
- package/src/components/Table/index.ts +2 -0
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +2 -3
- package/src/components/TableCellEditor/TableCellEditor.tsx +27 -22
- package/src/components/testing.ts +1 -1
- package/src/hooks/useProjectionModel.ts +1 -1
- package/src/hooks/useTableModel.ts +18 -6
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +1 -1
- package/src/model/table-model.test.ts +3 -3
- package/src/model/table-model.ts +148 -31
- package/src/model/table-presentation.test.ts +59 -1
- package/src/model/table-presentation.ts +171 -81
- package/src/model/table-view-state.ts +2 -2
- package/src/testing/useTestTableModel.ts +1 -1
- package/src/types/index.ts +4 -0
- package/src/util/dynamic-table.test.ts +1 -1
- package/src/util/dynamic-table.ts +1 -1
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +18 -10
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/browser/chunk-K4J2CKTZ.mjs +0 -1751
- package/dist/lib/browser/chunk-K4J2CKTZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-VV3P3ADH.mjs +0 -67
- package/dist/lib/browser/chunk-VV3P3ADH.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -1416
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs +0 -115
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/browser/translations.mjs +0 -39
- package/dist/lib/browser/translations.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -8
- package/dist/lib/browser/types/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-E6V274ZW.mjs +0 -1753
- package/dist/lib/node-esm/chunk-E6V274ZW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RPEWNOZZ.mjs +0 -68
- package/dist/lib/node-esm/chunk-RPEWNOZZ.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -1417
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -116
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/translations.mjs +0 -40
- package/dist/lib/node-esm/translations.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -9
- package/dist/lib/node-esm/types/index.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "A table component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -19,26 +19,22 @@
|
|
|
19
19
|
".": {
|
|
20
20
|
"source": "./src/index.ts",
|
|
21
21
|
"types": "./dist/types/src/index.d.ts",
|
|
22
|
-
"
|
|
23
|
-
|
|
22
|
+
"import": "./dist/lib/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"./types": {
|
|
25
|
+
"source": "./src/types/index.ts",
|
|
26
|
+
"types": "./dist/types/src/types/index.d.ts",
|
|
27
|
+
"import": "./dist/lib/types.mjs"
|
|
24
28
|
},
|
|
25
29
|
"./testing": {
|
|
26
30
|
"source": "./src/testing/index.ts",
|
|
27
31
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
28
|
-
"
|
|
29
|
-
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
32
|
+
"import": "./dist/lib/testing.mjs"
|
|
30
33
|
},
|
|
31
34
|
"./translations": {
|
|
32
35
|
"source": "./src/translations.ts",
|
|
33
36
|
"types": "./dist/types/src/translations.d.ts",
|
|
34
|
-
"
|
|
35
|
-
"node": "./dist/lib/node-esm/translations.mjs"
|
|
36
|
-
},
|
|
37
|
-
"./types": {
|
|
38
|
-
"source": "./src/types/index.ts",
|
|
39
|
-
"types": "./dist/types/src/types/index.d.ts",
|
|
40
|
-
"browser": "./dist/lib/browser/types/index.mjs",
|
|
41
|
-
"node": "./dist/lib/node-esm/types/index.mjs"
|
|
37
|
+
"import": "./dist/lib/translations.mjs"
|
|
42
38
|
}
|
|
43
39
|
},
|
|
44
40
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -47,9 +43,10 @@
|
|
|
47
43
|
"src"
|
|
48
44
|
],
|
|
49
45
|
"dependencies": {
|
|
50
|
-
"@codemirror/autocomplete": "^6.20.
|
|
51
|
-
"@codemirror/state": "^6.
|
|
52
|
-
"@codemirror/view": "^6.43.
|
|
46
|
+
"@codemirror/autocomplete": "^6.20.3",
|
|
47
|
+
"@codemirror/state": "^6.7.1",
|
|
48
|
+
"@codemirror/view": "^6.43.6",
|
|
49
|
+
"@effect-atom/atom": "^0.5.3",
|
|
53
50
|
"@effect-atom/atom-react": "^0.5.0",
|
|
54
51
|
"@fluentui/react-tabster": "9.26.11",
|
|
55
52
|
"@radix-ui/react-context": "1.1.1",
|
|
@@ -58,30 +55,31 @@
|
|
|
58
55
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
59
56
|
"date-fns": "^3.6.0",
|
|
60
57
|
"lodash.orderby": "^4.6.0",
|
|
61
|
-
"@dxos/
|
|
62
|
-
"@dxos/
|
|
63
|
-
"@dxos/
|
|
64
|
-
"@dxos/echo": "0.
|
|
65
|
-
"@dxos/
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/invariant": "0.
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/react-
|
|
73
|
-
"@dxos/react-ui-
|
|
74
|
-
"@dxos/react-ui-
|
|
75
|
-
"@dxos/react-ui-
|
|
76
|
-
"@dxos/react-ui-
|
|
77
|
-
"@dxos/react-ui-grid": "0.
|
|
78
|
-
"@dxos/react-ui-
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/
|
|
58
|
+
"@dxos/async": "0.11.1",
|
|
59
|
+
"@dxos/context": "0.11.1",
|
|
60
|
+
"@dxos/debug": "0.11.1",
|
|
61
|
+
"@dxos/echo": "0.11.1",
|
|
62
|
+
"@dxos/echo-react": "0.11.1",
|
|
63
|
+
"@dxos/effect": "0.11.1",
|
|
64
|
+
"@dxos/invariant": "0.11.1",
|
|
65
|
+
"@dxos/keys": "0.11.1",
|
|
66
|
+
"@dxos/lit-grid": "0.11.1",
|
|
67
|
+
"@dxos/log": "0.11.1",
|
|
68
|
+
"@dxos/random": "0.11.1",
|
|
69
|
+
"@dxos/react-client": "0.11.1",
|
|
70
|
+
"@dxos/react-ui-attention": "0.11.1",
|
|
71
|
+
"@dxos/react-ui-components": "0.11.1",
|
|
72
|
+
"@dxos/react-ui-editor": "0.11.1",
|
|
73
|
+
"@dxos/react-ui-form": "0.11.1",
|
|
74
|
+
"@dxos/react-ui-grid": "0.11.1",
|
|
75
|
+
"@dxos/react-ui-list": "0.11.1",
|
|
76
|
+
"@dxos/react-ui-menu": "0.11.1",
|
|
77
|
+
"@dxos/react-ui-search": "0.11.1",
|
|
78
|
+
"@dxos/react-ui-syntax-highlighter": "0.11.1",
|
|
79
|
+
"@dxos/schema": "0.11.1",
|
|
80
|
+
"@dxos/types": "0.11.1",
|
|
81
|
+
"@dxos/ui-types": "0.11.1",
|
|
82
|
+
"@dxos/util": "0.11.1"
|
|
85
83
|
},
|
|
86
84
|
"devDependencies": {
|
|
87
85
|
"@effect/platform": "0.96.2",
|
|
@@ -92,14 +90,14 @@
|
|
|
92
90
|
"fast-check": "^3.19.0",
|
|
93
91
|
"react": "~19.2.7",
|
|
94
92
|
"react-dom": "~19.2.7",
|
|
95
|
-
"vite": "^8.
|
|
96
|
-
"@dxos/
|
|
97
|
-
"@dxos/
|
|
98
|
-
"@dxos/react-client": "0.
|
|
99
|
-
"@dxos/react-ui": "0.
|
|
100
|
-
"@dxos/storybook-utils": "0.
|
|
101
|
-
"@dxos/test-utils": "0.
|
|
102
|
-
"@dxos/ui-theme": "0.
|
|
93
|
+
"vite": "^8.1.4",
|
|
94
|
+
"@dxos/lit-ui": "0.11.1",
|
|
95
|
+
"@dxos/random": "0.11.1",
|
|
96
|
+
"@dxos/react-client": "0.11.1",
|
|
97
|
+
"@dxos/react-ui": "0.11.1",
|
|
98
|
+
"@dxos/storybook-utils": "0.11.1",
|
|
99
|
+
"@dxos/test-utils": "0.11.1",
|
|
100
|
+
"@dxos/ui-theme": "0.11.1"
|
|
103
101
|
},
|
|
104
102
|
"peerDependencies": {
|
|
105
103
|
"@effect-atom/atom-react": "^0.5.0",
|
|
@@ -107,8 +105,8 @@
|
|
|
107
105
|
"effect": "3.21.4",
|
|
108
106
|
"react": "~19.2.7",
|
|
109
107
|
"react-dom": "~19.2.7",
|
|
110
|
-
"@dxos/ui
|
|
111
|
-
"@dxos/
|
|
108
|
+
"@dxos/react-ui": "^0.11.1",
|
|
109
|
+
"@dxos/ui-theme": "^0.11.1"
|
|
112
110
|
},
|
|
113
111
|
"publishConfig": {
|
|
114
112
|
"access": "public"
|
|
@@ -7,9 +7,9 @@ import type * as Types from 'effect/Types';
|
|
|
7
7
|
import React, { useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
9
|
import { Filter, type JsonSchema, Obj, Type } from '@dxos/echo';
|
|
10
|
+
import { useQuery, useType } from '@dxos/echo-react';
|
|
10
11
|
import { Format } from '@dxos/echo/Format';
|
|
11
12
|
import { random } from '@dxos/random';
|
|
12
|
-
import { useQuery, useType } from '@dxos/react-client/echo';
|
|
13
13
|
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
14
14
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
15
15
|
import { type SchemaPropertyDefinition } from '@dxos/schema';
|
|
@@ -52,6 +52,36 @@ const DynamicTableStory = () => {
|
|
|
52
52
|
return <DynamicTable properties={properties} rows={rows} />;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
const useWidePropertiesAndObjects = () => {
|
|
56
|
+
const properties = useMemo<SchemaPropertyDefinition[]>(
|
|
57
|
+
() => [
|
|
58
|
+
{ name: 'name', format: Format.TypeFormat.String },
|
|
59
|
+
{ name: 'email', format: Format.TypeFormat.String },
|
|
60
|
+
{ name: 'company', format: Format.TypeFormat.String },
|
|
61
|
+
{ name: 'product', format: Format.TypeFormat.String },
|
|
62
|
+
{ name: 'city', format: Format.TypeFormat.String },
|
|
63
|
+
{ name: 'url', format: Format.TypeFormat.String },
|
|
64
|
+
{ name: 'age', format: Format.TypeFormat.Number },
|
|
65
|
+
],
|
|
66
|
+
[],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const [rows] = useState<any[]>(
|
|
70
|
+
Array.from({ length: 20 }, () => ({
|
|
71
|
+
id: random.string.uuid(),
|
|
72
|
+
name: random.person.fullName(),
|
|
73
|
+
email: random.internet.email(),
|
|
74
|
+
company: random.company.name(),
|
|
75
|
+
product: random.commerce.productName(),
|
|
76
|
+
city: random.geo.airport().name,
|
|
77
|
+
url: random.internet.url(),
|
|
78
|
+
age: random.number.int({ min: 18, max: 80 }),
|
|
79
|
+
})),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return { properties, rows };
|
|
83
|
+
};
|
|
84
|
+
|
|
55
85
|
//
|
|
56
86
|
// Story definitions.
|
|
57
87
|
//
|
|
@@ -100,6 +130,34 @@ export const WithClickToSelect: StoryObj = {
|
|
|
100
130
|
},
|
|
101
131
|
};
|
|
102
132
|
|
|
133
|
+
export const WithPinnedColumns: StoryObj = {
|
|
134
|
+
render: () => {
|
|
135
|
+
const { properties, rows } = useWidePropertiesAndObjects();
|
|
136
|
+
|
|
137
|
+
// Pin the first data column (Name) so it stays visible while the rest scroll horizontally.
|
|
138
|
+
const features = useMemo<Partial<TableFeatures>>(
|
|
139
|
+
() => ({ selection: { enabled: true, mode: 'multiple' as const }, dataEditable: false, pinColumns: 1 }),
|
|
140
|
+
[],
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return <DynamicTable properties={properties} rows={rows} features={features} />;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const WithMultiplePinnedColumns: StoryObj = {
|
|
148
|
+
render: () => {
|
|
149
|
+
const { properties, rows } = useWidePropertiesAndObjects();
|
|
150
|
+
|
|
151
|
+
// Pin the first two data columns (Name, Email).
|
|
152
|
+
const features = useMemo<Partial<TableFeatures>>(
|
|
153
|
+
() => ({ selection: { enabled: false }, dataEditable: false, pinColumns: 2 }),
|
|
154
|
+
[],
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return <DynamicTable properties={properties} rows={rows} features={features} />;
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
103
161
|
export const WithJsonSchema: StoryObj = {
|
|
104
162
|
render: () => {
|
|
105
163
|
const schema = useMemo<Types.DeepMutable<JsonSchema.JsonSchema>>(
|
|
@@ -9,7 +9,6 @@ import React, { useCallback, useContext, useEffect, useMemo, useRef, useState }
|
|
|
9
9
|
import { type JsonSchema, type Type } from '@dxos/echo';
|
|
10
10
|
import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
|
|
11
11
|
import { type ProjectionModel } from '@dxos/schema';
|
|
12
|
-
import { mx } from '@dxos/ui-theme';
|
|
13
12
|
|
|
14
13
|
import { useTableModel } from '../../hooks';
|
|
15
14
|
import { type TableFeatures, TablePresentation, type TableRowAction } from '../../model';
|
|
@@ -25,8 +24,11 @@ export type DynamicTableProps<T extends Type.AnyEntity = Type.AnyEntity> = Theme
|
|
|
25
24
|
jsonSchema?: Types.DeepMutable<JsonSchema.JsonSchema>;
|
|
26
25
|
features?: Partial<TableFeatures>;
|
|
27
26
|
rowActions?: TableRowAction[];
|
|
27
|
+
/** Rows selected when the table mounts; see `useTableModel`'s `selection`. */
|
|
28
|
+
selection?: readonly string[];
|
|
28
29
|
onRowClick?: (row: any) => void;
|
|
29
30
|
onRowAction?: (actionId: string, datum: any) => void;
|
|
31
|
+
onSelectionChanged?: (selection: string[]) => void;
|
|
30
32
|
}>;
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -42,8 +44,10 @@ export const DynamicTable = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
|
42
44
|
properties,
|
|
43
45
|
jsonSchema: jsonSchemaProp,
|
|
44
46
|
rowActions,
|
|
47
|
+
selection,
|
|
45
48
|
onRowClick,
|
|
46
49
|
onRowAction,
|
|
50
|
+
onSelectionChanged,
|
|
47
51
|
...props
|
|
48
52
|
}: DynamicTableProps<T>) => {
|
|
49
53
|
const registry = useContext(RegistryContext);
|
|
@@ -83,9 +87,11 @@ export const DynamicTable = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
|
83
87
|
projection: dynamicTable?.projection,
|
|
84
88
|
features,
|
|
85
89
|
rowActions,
|
|
90
|
+
selection,
|
|
86
91
|
onCellUpdate: handleCellUpdate,
|
|
87
92
|
onRowOrderChange: handleRowOrderChange,
|
|
88
93
|
onRowAction,
|
|
94
|
+
onSelectionChanged,
|
|
89
95
|
});
|
|
90
96
|
|
|
91
97
|
const presentation = useMemo(() => {
|
|
@@ -95,12 +101,14 @@ export const DynamicTable = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
|
95
101
|
}, [registry, model]);
|
|
96
102
|
|
|
97
103
|
return (
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
<Table.Root ref={tableRef}>
|
|
105
|
+
<Table.Content
|
|
106
|
+
classNames={classNames}
|
|
107
|
+
model={model}
|
|
108
|
+
presentation={presentation}
|
|
109
|
+
ignoreAttention
|
|
110
|
+
onRowClick={onRowClick}
|
|
111
|
+
/>
|
|
112
|
+
</Table.Root>
|
|
105
113
|
);
|
|
106
114
|
};
|
|
@@ -18,8 +18,8 @@ import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
|
18
18
|
import { ViewModel } from '@dxos/schema';
|
|
19
19
|
import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
|
|
20
20
|
import { withRegistry } from '@dxos/storybook-utils';
|
|
21
|
-
import { Organization, Person } from '@dxos/types';
|
|
22
21
|
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
22
|
+
import { Organization, Person } from '@dxos/types';
|
|
23
23
|
|
|
24
24
|
import { translations } from '#translations';
|
|
25
25
|
|
|
@@ -40,7 +40,7 @@ const useTestModel = <S extends Type.AnyObj>(schema: S, count: number) => {
|
|
|
40
40
|
const { space } = useClientStory();
|
|
41
41
|
const [object, setObject] = useState<Table.Table>();
|
|
42
42
|
|
|
43
|
-
const features = useMemo<TableFeatures
|
|
43
|
+
const features = useMemo<Partial<TableFeatures>>(
|
|
44
44
|
() => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
|
|
45
45
|
[],
|
|
46
46
|
);
|
|
@@ -7,13 +7,14 @@ import * as Schema from 'effect/Schema';
|
|
|
7
7
|
import React, { useCallback } from 'react';
|
|
8
8
|
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
|
9
9
|
|
|
10
|
-
import { Annotation, type Database, DXN, Format, Obj, type QueryAST, Ref, Type, View } from '@dxos/echo';
|
|
10
|
+
import { Annotation, type Database, DXN, Filter, Format, Obj, type QueryAST, Ref, Type, View } from '@dxos/echo';
|
|
11
|
+
import { useQuery } from '@dxos/echo-react';
|
|
11
12
|
import { type Mutable, PropertyMetaAnnotationId } from '@dxos/echo/internal';
|
|
12
13
|
import { invariant } from '@dxos/invariant';
|
|
13
14
|
import { random } from '@dxos/random';
|
|
14
15
|
import { PublicKey } from '@dxos/react-client';
|
|
15
16
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
16
|
-
import { Panel, ScrollArea } from '@dxos/react-ui';
|
|
17
|
+
import { Panel, ScrollArea, Toolbar } from '@dxos/react-ui';
|
|
17
18
|
import { ViewEditor } from '@dxos/react-ui-form';
|
|
18
19
|
import { translations as formTranslations } from '@dxos/react-ui-form/translations';
|
|
19
20
|
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
@@ -338,6 +339,79 @@ export const RequiredSchema: StoryObj = {
|
|
|
338
339
|
},
|
|
339
340
|
};
|
|
340
341
|
|
|
342
|
+
// Reproduces the "form → table" reactivity path: an ECHO object edited *outside* the grid (as the selected-objects
|
|
343
|
+
// companion form does via `Obj.update`) must refresh the corresponding cell. The table model subscribes to each
|
|
344
|
+
// visible row via `Obj.subscribe`, bumping a `cellUpdate` atom the grid re-renders on.
|
|
345
|
+
const ExternalMutationStory = () => {
|
|
346
|
+
const { schema, table, tableRef, model, presentation, db } = useTestTableModel<typeof TestSchema.Person>();
|
|
347
|
+
const objects = useQuery(db, schema ? Filter.type(schema) : Filter.nothing());
|
|
348
|
+
const target = objects.at(0);
|
|
349
|
+
|
|
350
|
+
const handleMutate = useCallback(() => {
|
|
351
|
+
if (!target) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
Obj.update(target, (target) => {
|
|
355
|
+
target.name = 'Zoe Mutated';
|
|
356
|
+
});
|
|
357
|
+
}, [target]);
|
|
358
|
+
|
|
359
|
+
if (!schema || !table?.view.target) {
|
|
360
|
+
return <div />;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return (
|
|
364
|
+
<div className='flex flex-col h-full'>
|
|
365
|
+
<Toolbar.Root>
|
|
366
|
+
<Toolbar.Button onClick={handleMutate}>Mutate row externally</Toolbar.Button>
|
|
367
|
+
</Toolbar.Root>
|
|
368
|
+
<TableComponent.Root ref={tableRef}>
|
|
369
|
+
<Panel.Root>
|
|
370
|
+
<Panel.Content asChild>
|
|
371
|
+
<TableComponent.Content schema={schema} model={model} presentation={presentation} ignoreAttention />
|
|
372
|
+
</Panel.Content>
|
|
373
|
+
</Panel.Root>
|
|
374
|
+
</TableComponent.Root>
|
|
375
|
+
</div>
|
|
376
|
+
);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
export const ExternalMutation: StoryObj = {
|
|
380
|
+
render: () => <ExternalMutationStory />,
|
|
381
|
+
decorators: [
|
|
382
|
+
withClientProvider({
|
|
383
|
+
types: [View.View, Table.Table, TestSchema.Person],
|
|
384
|
+
createIdentity: true,
|
|
385
|
+
createSpace: true,
|
|
386
|
+
onCreateSpace: async ({ space }) => {
|
|
387
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({
|
|
388
|
+
db: space.db,
|
|
389
|
+
typename: Type.getTypename(TestSchema.Person),
|
|
390
|
+
});
|
|
391
|
+
space.db.add(Table.make({ view, jsonSchema }));
|
|
392
|
+
space.db.add(Obj.make(TestSchema.Person, { name: 'Alice' }));
|
|
393
|
+
},
|
|
394
|
+
}),
|
|
395
|
+
],
|
|
396
|
+
parameters: {
|
|
397
|
+
layout: 'fullscreen',
|
|
398
|
+
translations: [...translations, ...formTranslations],
|
|
399
|
+
},
|
|
400
|
+
play: async ({ canvasElement }) => {
|
|
401
|
+
const canvas = within(canvasElement);
|
|
402
|
+
const cellText = () => canvas.getByTestId('grid.0.0').querySelector('.dx-grid__cell__content')?.textContent;
|
|
403
|
+
|
|
404
|
+
// Wait for the client/space/table and the initial row to render (30s covers ECHO setup).
|
|
405
|
+
await waitFor(() => expect(cellText()).toBe('Alice'), { timeout: 30_000 });
|
|
406
|
+
|
|
407
|
+
// Mutate the object outside the grid (as an external/companion edit would).
|
|
408
|
+
await userEvent.click(await canvas.findByRole('button', { name: /mutate row externally/i }));
|
|
409
|
+
|
|
410
|
+
// The cell must reflect the external mutation.
|
|
411
|
+
await waitFor(() => expect(cellText()).toBe('Zoe Mutated'), { timeout: 5_000 });
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
|
|
341
415
|
export const Tags: Meta<StoryArgs> = {
|
|
342
416
|
title: 'ui/react-ui-table/Table',
|
|
343
417
|
render: DefaultStory,
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Atom } from '@effect-atom/atom
|
|
6
|
-
import {
|
|
7
|
-
import React, { type PropsWithChildren, type RefObject, forwardRef, useImperativeHandle, useRef } from 'react';
|
|
5
|
+
import { Atom } from '@effect-atom/atom';
|
|
6
|
+
import React, { type PropsWithChildren, forwardRef, useImperativeHandle, useRef } from 'react';
|
|
8
7
|
|
|
9
8
|
import { type DxGridAxisMeta, type DxGridPosition } from '@dxos/react-ui-grid';
|
|
10
9
|
|
|
11
10
|
import { type InsertRowResult } from '../../model';
|
|
12
11
|
import { TableContent } from './TableContent';
|
|
12
|
+
import { TableContextProvider } from './TableContext';
|
|
13
13
|
import { TableToolbar } from './TableToolbar';
|
|
14
14
|
|
|
15
15
|
const columnDefault = { grid: { minSize: 80, maxSize: 640 } };
|
|
@@ -17,16 +17,6 @@ const rowDefault = { frozenRowsStart: { readonly: true, focusUnfurl: false } };
|
|
|
17
17
|
const emptyColumnMeta = Atom.make<DxGridAxisMeta>({ grid: {} });
|
|
18
18
|
|
|
19
19
|
//
|
|
20
|
-
// Context
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
type TableContextValue = {
|
|
24
|
-
/** Mutable ref populated by Content so Root can expose the controller. */
|
|
25
|
-
controllerRef: RefObject<TableController>;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const [TableContextProvider, useTableContext] = createContext<TableContextValue>('Table');
|
|
29
|
-
|
|
30
20
|
//
|
|
31
21
|
// Controller
|
|
32
22
|
//
|
|
@@ -73,5 +63,3 @@ export const Table = {
|
|
|
73
63
|
|
|
74
64
|
export type { TableController, TableRootProps };
|
|
75
65
|
export type { TableExportFormat } from './TableToolbar';
|
|
76
|
-
|
|
77
|
-
export { useTableContext };
|
|
@@ -30,7 +30,8 @@ import { type OnCreateHandler, type TableCellEditorProps, TableValueEditor } fro
|
|
|
30
30
|
import { ColumnActionsMenu } from './ColumnActionsMenu';
|
|
31
31
|
import { ColumnSettings } from './ColumnSettings';
|
|
32
32
|
import { RowActionsMenu } from './RowActionsMenu';
|
|
33
|
-
import { TableController
|
|
33
|
+
import { type TableController } from './Table';
|
|
34
|
+
import { useTableContext } from './TableContext';
|
|
34
35
|
|
|
35
36
|
const columnDefault = { grid: { minSize: 80, maxSize: 640 } };
|
|
36
37
|
const rowDefault = { frozenRowsStart: { readonly: true, focusUnfurl: false } };
|
|
@@ -84,10 +85,10 @@ export const TableContent = composable<HTMLDivElement, TableContentProps>(
|
|
|
84
85
|
return {
|
|
85
86
|
frozenRowsStart: 1,
|
|
86
87
|
frozenRowsEnd: Math.max(1, draftRowCount),
|
|
87
|
-
frozenColsStart: model?.
|
|
88
|
+
frozenColsStart: model?.frozenColsStart ?? 0,
|
|
88
89
|
frozenColsEnd: noActionColumn ? 0 : 1,
|
|
89
90
|
};
|
|
90
|
-
}, [model, draftRowCount]);
|
|
91
|
+
}, [model, draftRowCount, columnCount]);
|
|
91
92
|
|
|
92
93
|
const getCells = useCallback<NonNullable<GridContentProps['getCells']>>(
|
|
93
94
|
(range: DxGridPlaneRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
|
|
@@ -232,7 +233,7 @@ export const TableContent = composable<HTMLDivElement, TableContentProps>(
|
|
|
232
233
|
}
|
|
233
234
|
case 'switch': {
|
|
234
235
|
if (model) {
|
|
235
|
-
model.updateCellData({ row: data.rowIndex, col: data.colIndex }, (value) => !value);
|
|
236
|
+
model.updateCellData({ row: data.rowIndex, col: data.colIndex, plane: data.plane }, (value) => !value);
|
|
236
237
|
}
|
|
237
238
|
break;
|
|
238
239
|
}
|
|
@@ -341,7 +342,7 @@ export const TableContent = composable<HTMLDivElement, TableContentProps>(
|
|
|
341
342
|
(event) => {
|
|
342
343
|
if (event.axis === 'col') {
|
|
343
344
|
const columnIndex = parseInt(event.index, 10);
|
|
344
|
-
model?.setColumnWidth(columnIndex, event.size);
|
|
345
|
+
model?.setColumnWidth(columnIndex, event.size, event.plane);
|
|
345
346
|
}
|
|
346
347
|
},
|
|
347
348
|
[model],
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { createContext } from '@radix-ui/react-context';
|
|
6
|
+
import { type RefObject } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type TableController } from './Table';
|
|
9
|
+
|
|
10
|
+
// Kept out of `Table.tsx`: react-refresh only fast-refreshes a module whose exports are all
|
|
11
|
+
// components, so a context and its hook exported beside them force a full page reload on every edit.
|
|
12
|
+
|
|
13
|
+
// Context
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
export type TableContextValue = {
|
|
17
|
+
/** Mutable ref populated by Content so Root can expose the controller. */
|
|
18
|
+
controllerRef: RefObject<TableController>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const [TableContextProvider, useTableContext] = createContext<TableContextValue>('Table');
|
|
@@ -9,7 +9,7 @@ import { Obj, Type, View } from '@dxos/echo';
|
|
|
9
9
|
import { random } from '@dxos/random';
|
|
10
10
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
11
11
|
import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
|
|
12
|
-
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
12
|
+
import { Loading, withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
13
13
|
import { ViewModel } from '@dxos/schema';
|
|
14
14
|
import { Task } from '@dxos/types';
|
|
15
15
|
|
|
@@ -26,9 +26,8 @@ type StoryArgs = {
|
|
|
26
26
|
// TODO(burdon): Broken layout.
|
|
27
27
|
const DefaultStory = ({ editing }: StoryArgs) => {
|
|
28
28
|
const { model, table } = useTestTableModel();
|
|
29
|
-
|
|
30
29
|
if (!model || !table) {
|
|
31
|
-
return <
|
|
30
|
+
return <Loading />;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
return (
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type EditorKeyOrBlurHandler,
|
|
18
18
|
GridCellEditor,
|
|
19
19
|
type GridCellEditorProps,
|
|
20
|
+
type GridEditing,
|
|
20
21
|
type GridScopedProps,
|
|
21
22
|
editorKeys,
|
|
22
23
|
parseCellIndex,
|
|
@@ -44,6 +45,30 @@ export type TableCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
|
|
|
44
45
|
onSave?: () => void;
|
|
45
46
|
};
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the field projection for the cell being edited, guarding against an out-of-range field
|
|
50
|
+
* index (e.g. a stale cell, or a pinned plane whose `getFieldIndex` maps beyond the projection).
|
|
51
|
+
*/
|
|
52
|
+
const resolveFieldProjection = (
|
|
53
|
+
model: TableModel | undefined,
|
|
54
|
+
editing: GridEditing | undefined,
|
|
55
|
+
): FieldProjection | undefined => {
|
|
56
|
+
if (!model || !editing) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { col, plane } = parseCellIndex(editing.index);
|
|
61
|
+
const fieldIndex = model.getFieldIndex(plane, col);
|
|
62
|
+
const fields = model.projection.getFields();
|
|
63
|
+
if (fieldIndex === undefined || fieldIndex < 0 || fieldIndex >= fields.length) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const fieldProjection = model.projection.getFieldProjection(fields[fieldIndex].id);
|
|
68
|
+
invariant(fieldProjection);
|
|
69
|
+
return fieldProjection;
|
|
70
|
+
};
|
|
71
|
+
|
|
47
72
|
export const TableValueEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
48
73
|
__gridScope,
|
|
49
74
|
schema,
|
|
@@ -55,17 +80,7 @@ export const TableValueEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
|
55
80
|
}: GridScopedProps<TableCellEditorProps<T>>) => {
|
|
56
81
|
const { editing } = useGridContext('TableValueEditor', __gridScope);
|
|
57
82
|
|
|
58
|
-
const fieldProjection = useMemo
|
|
59
|
-
if (!model || !editing) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const { col } = parseCellIndex(editing.index);
|
|
64
|
-
const field = model.projection.getFields()[col];
|
|
65
|
-
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
66
|
-
invariant(fieldProjection);
|
|
67
|
-
return fieldProjection;
|
|
68
|
-
}, [model, editing]);
|
|
83
|
+
const fieldProjection = useMemo(() => resolveFieldProjection(model, editing), [model, editing]);
|
|
69
84
|
|
|
70
85
|
if (
|
|
71
86
|
fieldProjection?.props.type === TypeEnum.Array ||
|
|
@@ -108,17 +123,7 @@ export const TableCellEditor = ({
|
|
|
108
123
|
const [validationError, setValidationError] = useState<string | null>(null);
|
|
109
124
|
const [validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
|
|
110
125
|
|
|
111
|
-
const fieldProjection = useMemo
|
|
112
|
-
if (!model || !editing) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const { col } = parseCellIndex(editing.index);
|
|
117
|
-
const field = model.projection.getFields()[col];
|
|
118
|
-
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
119
|
-
invariant(fieldProjection);
|
|
120
|
-
return fieldProjection;
|
|
121
|
-
}, [model, editing]);
|
|
126
|
+
const fieldProjection = useMemo(() => resolveFieldProjection(model, editing), [model, editing]);
|
|
122
127
|
|
|
123
128
|
// TOOD(burdon): Attach to event handler?
|
|
124
129
|
const handleEnter = useCallback(
|