@dxos/react-ui-table 0.8.4-main.72ec0f3 → 0.8.4-main.8360d9e660
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-BF54SSNL.mjs +70 -0
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GS2UNGJK.mjs → chunk-PTHMYAAM.mjs} +656 -680
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1158 -1028
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +30 -42
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node-esm/{chunk-TRR3C5NX.mjs → chunk-GI72MQSG.mjs} +656 -679
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs +71 -0
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1158 -1028
- 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 +30 -42
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +2 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +4 -18
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +12 -8
- 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/Table/Table.d.ts +11 -11
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +71 -5
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +4 -4
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +3 -3
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +22 -15
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +5 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +9 -9
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +17 -8
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +104 -24
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +3 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -15
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +4 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +26 -0
- package/dist/types/src/types/Table.d.ts.map +1 -0
- 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/util/dynamic-table.d.ts +14 -11
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -55
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +16 -16
- package/src/components/Table/DynamicTable.tsx +30 -22
- package/src/components/Table/Relations.stories.tsx +39 -41
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +110 -92
- package/src/components/Table/Table.tsx +324 -299
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -5
- package/src/components/TableCellEditor/FormCellEditor.tsx +118 -135
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +14 -10
- package/src/components/TableCellEditor/TableCellEditor.tsx +31 -31
- package/src/components/TableToolbar/TableToolbar.tsx +25 -19
- package/src/components/testing.ts +38 -12
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +39 -0
- package/src/hooks/useTableModel.ts +36 -34
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +82 -54
- package/src/model/table-model.ts +430 -167
- package/src/model/table-presentation.test.ts +59 -49
- package/src/model/table-presentation.ts +43 -37
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +49 -5
- package/src/testing/useTestTableModel.ts +45 -38
- package/src/translations.ts +3 -3
- package/src/types/Table.ts +79 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +40 -26
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- package/src/util/sort.ts +90 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-2PFAPQL4.mjs +0 -66
- package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +0 -7
- package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs +0 -67
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +0 -7
- package/dist/types/src/model/table-sorting.d.ts +0 -52
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/dist/types/src/types/schema.d.ts +0 -28
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/src/model/table-sorting.ts +0 -225
- package/src/types/schema.ts +0 -73
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-table",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.8360d9e660",
|
|
4
4
|
"description": "A table component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
10
14
|
"type": "module",
|
|
11
15
|
"exports": {
|
|
12
16
|
"./deprecated": {
|
|
@@ -56,71 +60,66 @@
|
|
|
56
60
|
"@codemirror/autocomplete": "^6.19.0",
|
|
57
61
|
"@codemirror/state": "^6.5.2",
|
|
58
62
|
"@codemirror/view": "^6.38.4",
|
|
59
|
-
"@
|
|
60
|
-
"@
|
|
61
|
-
"@preact/signals-core": "^1.12.1",
|
|
63
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
64
|
+
"@fluentui/react-tabster": "9.26.11",
|
|
62
65
|
"@radix-ui/react-context": "1.1.1",
|
|
63
66
|
"@radix-ui/react-primitive": "2.0.2",
|
|
64
67
|
"@radix-ui/react-slot": "1.1.2",
|
|
65
68
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
66
69
|
"date-fns": "^3.3.1",
|
|
67
70
|
"lodash.orderby": "^4.6.0",
|
|
68
|
-
"@dxos/async": "0.8.4-main.
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/keys": "0.8.4-main.
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/log": "0.8.4-main.
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/react-client": "0.8.4-main.
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/react-ui-
|
|
82
|
-
"@dxos/react-ui-
|
|
83
|
-
"@dxos/react-ui-
|
|
84
|
-
"@dxos/react-ui-
|
|
85
|
-
"@dxos/react-ui-
|
|
86
|
-
"@dxos/react-ui-menu": "0.8.4-main.
|
|
87
|
-
"@dxos/react-ui-
|
|
88
|
-
"@dxos/react-ui-
|
|
89
|
-
"@dxos/
|
|
90
|
-
"@dxos/
|
|
91
|
-
"@dxos/
|
|
92
|
-
"@dxos/
|
|
93
|
-
"@dxos/util": "0.8.4-main.72ec0f3",
|
|
94
|
-
"@dxos/schema": "0.8.4-main.72ec0f3"
|
|
71
|
+
"@dxos/async": "0.8.4-main.8360d9e660",
|
|
72
|
+
"@dxos/context": "0.8.4-main.8360d9e660",
|
|
73
|
+
"@dxos/debug": "0.8.4-main.8360d9e660",
|
|
74
|
+
"@dxos/echo": "0.8.4-main.8360d9e660",
|
|
75
|
+
"@dxos/echo-atom": "0.8.4-main.8360d9e660",
|
|
76
|
+
"@dxos/effect": "0.8.4-main.8360d9e660",
|
|
77
|
+
"@dxos/invariant": "0.8.4-main.8360d9e660",
|
|
78
|
+
"@dxos/keys": "0.8.4-main.8360d9e660",
|
|
79
|
+
"@dxos/lit-grid": "0.8.4-main.8360d9e660",
|
|
80
|
+
"@dxos/log": "0.8.4-main.8360d9e660",
|
|
81
|
+
"@dxos/random": "0.8.4-main.8360d9e660",
|
|
82
|
+
"@dxos/react-client": "0.8.4-main.8360d9e660",
|
|
83
|
+
"@dxos/react-ui-components": "0.8.4-main.8360d9e660",
|
|
84
|
+
"@dxos/react-ui-form": "0.8.4-main.8360d9e660",
|
|
85
|
+
"@dxos/react-ui-editor": "0.8.4-main.8360d9e660",
|
|
86
|
+
"@dxos/react-ui-grid": "0.8.4-main.8360d9e660",
|
|
87
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.8360d9e660",
|
|
88
|
+
"@dxos/react-ui-attention": "0.8.4-main.8360d9e660",
|
|
89
|
+
"@dxos/react-ui-menu": "0.8.4-main.8360d9e660",
|
|
90
|
+
"@dxos/react-ui-stack": "0.8.4-main.8360d9e660",
|
|
91
|
+
"@dxos/react-ui-syntax-highlighter": "0.8.4-main.8360d9e660",
|
|
92
|
+
"@dxos/types": "0.8.4-main.8360d9e660",
|
|
93
|
+
"@dxos/ui-types": "0.8.4-main.8360d9e660",
|
|
94
|
+
"@dxos/schema": "0.8.4-main.8360d9e660",
|
|
95
|
+
"@dxos/util": "0.8.4-main.8360d9e660"
|
|
95
96
|
},
|
|
96
97
|
"devDependencies": {
|
|
97
|
-
"@effect
|
|
98
|
-
"@effect/platform": "0.92.1",
|
|
98
|
+
"@effect/platform": "0.94.4",
|
|
99
99
|
"@types/lodash.orderby": "^4.6.9",
|
|
100
|
-
"@types/react": "~19.2.
|
|
101
|
-
"@types/react-dom": "~19.2.
|
|
102
|
-
"effect": "3.
|
|
100
|
+
"@types/react": "~19.2.7",
|
|
101
|
+
"@types/react-dom": "~19.2.3",
|
|
102
|
+
"effect": "3.19.16",
|
|
103
103
|
"fast-check": "^3.19.0",
|
|
104
|
-
"react": "~19.2.
|
|
105
|
-
"react-dom": "~19.2.
|
|
106
|
-
"vite": "7.1.
|
|
107
|
-
"@dxos/
|
|
108
|
-
"@dxos/lit-ui": "0.8.4-main.
|
|
109
|
-
"@dxos/
|
|
110
|
-
"@dxos/
|
|
111
|
-
"@dxos/
|
|
112
|
-
"@dxos/
|
|
113
|
-
"@dxos/
|
|
114
|
-
"@dxos/storybook-utils": "0.8.4-main.72ec0f3"
|
|
104
|
+
"react": "~19.2.3",
|
|
105
|
+
"react-dom": "~19.2.3",
|
|
106
|
+
"vite": "^7.1.11",
|
|
107
|
+
"@dxos/random": "0.8.4-main.8360d9e660",
|
|
108
|
+
"@dxos/lit-ui": "0.8.4-main.8360d9e660",
|
|
109
|
+
"@dxos/react-client": "0.8.4-main.8360d9e660",
|
|
110
|
+
"@dxos/react-ui": "0.8.4-main.8360d9e660",
|
|
111
|
+
"@dxos/storybook-utils": "0.8.4-main.8360d9e660",
|
|
112
|
+
"@dxos/test-utils": "0.8.4-main.8360d9e660",
|
|
113
|
+
"@dxos/ui-theme": "0.8.4-main.8360d9e660"
|
|
115
114
|
},
|
|
116
115
|
"peerDependencies": {
|
|
117
|
-
"@effect-atom/atom-react": "^0.
|
|
118
|
-
"@effect/platform": "
|
|
119
|
-
"effect": "
|
|
120
|
-
"react": "
|
|
121
|
-
"react-dom": "
|
|
122
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
123
|
-
"@dxos/
|
|
116
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
117
|
+
"@effect/platform": "0.94.4",
|
|
118
|
+
"effect": "3.19.16",
|
|
119
|
+
"react": "~19.2.3",
|
|
120
|
+
"react-dom": "~19.2.3",
|
|
121
|
+
"@dxos/react-ui": "0.8.4-main.8360d9e660",
|
|
122
|
+
"@dxos/ui-theme": "0.8.4-main.8360d9e660"
|
|
124
123
|
},
|
|
125
124
|
"publishConfig": {
|
|
126
125
|
"access": "public"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
5
6
|
import React from 'react';
|
|
6
7
|
|
|
7
8
|
import { DropdownMenu, useTranslation } from '@dxos/react-ui';
|
|
@@ -9,16 +10,19 @@ import { DropdownMenu, useTranslation } from '@dxos/react-ui';
|
|
|
9
10
|
import { type ModalController, type TableModel } from '../../model';
|
|
10
11
|
import { translationKey } from '../../translations';
|
|
11
12
|
|
|
12
|
-
type ColumnActionsMenuProps = {
|
|
13
|
+
export type ColumnActionsMenuProps = {
|
|
14
|
+
model: TableModel;
|
|
15
|
+
modals: ModalController;
|
|
16
|
+
};
|
|
13
17
|
|
|
14
18
|
export const ColumnActionsMenu = ({ model, modals }: ColumnActionsMenuProps) => {
|
|
15
19
|
const { t } = useTranslation(translationKey);
|
|
16
|
-
const state = modals.state
|
|
20
|
+
const state = useAtomValue(modals.state);
|
|
17
21
|
if (state?.type !== 'column') {
|
|
18
22
|
return null;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
const currentSort = model.
|
|
25
|
+
const currentSort = model.getSorting();
|
|
22
26
|
const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
|
|
23
27
|
|
|
24
28
|
return (
|
|
@@ -30,7 +34,7 @@ export const ColumnActionsMenu = ({ model, modals }: ColumnActionsMenuProps) =>
|
|
|
30
34
|
{(!isCurrentColumnSorted || currentSort?.direction === 'asc') && (
|
|
31
35
|
<DropdownMenu.Item
|
|
32
36
|
data-testid='column-sort-descending'
|
|
33
|
-
onClick={() => model.
|
|
37
|
+
onClick={() => model.setSort(state.fieldId, 'desc')}
|
|
34
38
|
>
|
|
35
39
|
{t('column action sort descending')}
|
|
36
40
|
</DropdownMenu.Item>
|
|
@@ -38,13 +42,13 @@ export const ColumnActionsMenu = ({ model, modals }: ColumnActionsMenuProps) =>
|
|
|
38
42
|
{(!isCurrentColumnSorted || currentSort?.direction === 'desc') && (
|
|
39
43
|
<DropdownMenu.Item
|
|
40
44
|
data-testid='column-sort-ascending'
|
|
41
|
-
onClick={() => model.
|
|
45
|
+
onClick={() => model.setSort(state.fieldId, 'asc')}
|
|
42
46
|
>
|
|
43
47
|
{t('column action sort ascending')}
|
|
44
48
|
</DropdownMenu.Item>
|
|
45
49
|
)}
|
|
46
50
|
{isCurrentColumnSorted && (
|
|
47
|
-
<DropdownMenu.Item data-testid='column-clear-sort' onClick={() => model.
|
|
51
|
+
<DropdownMenu.Item data-testid='column-clear-sort' onClick={() => model.clearSort()}>
|
|
48
52
|
{t('column action clear sorting')}
|
|
49
53
|
</DropdownMenu.Item>
|
|
50
54
|
)}
|
|
@@ -2,26 +2,25 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
5
6
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
import { type SchemaRegistry } from '@dxos/echo-db';
|
|
8
|
+
import { type SchemaRegistry, type View } from '@dxos/echo';
|
|
9
9
|
import { Popover } from '@dxos/react-ui';
|
|
10
10
|
import { FieldEditor } from '@dxos/react-ui-form';
|
|
11
|
-
import { type FieldType } from '@dxos/schema';
|
|
12
11
|
|
|
13
12
|
import { type ModalController, type TableModel } from '../../model';
|
|
14
13
|
|
|
15
14
|
type ColumnSettingsProps = {
|
|
16
|
-
registry?: SchemaRegistry;
|
|
15
|
+
registry?: SchemaRegistry.SchemaRegistry;
|
|
17
16
|
model?: TableModel;
|
|
18
17
|
modals: ModalController;
|
|
19
18
|
onNewColumn: () => void;
|
|
20
19
|
};
|
|
21
20
|
|
|
22
21
|
export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnSettingsProps) => {
|
|
23
|
-
const [newField, setNewField] = useState<FieldType>();
|
|
24
|
-
const state = modals.state
|
|
22
|
+
const [newField, setNewField] = useState<View.FieldType>();
|
|
23
|
+
const state = useAtomValue(modals.state);
|
|
25
24
|
|
|
26
25
|
useEffect(() => {
|
|
27
26
|
if (state?.type === 'columnSettings' && state.mode.type === 'create' && model?.projection) {
|
|
@@ -38,11 +37,11 @@ export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnS
|
|
|
38
37
|
if (state?.type === 'columnSettings') {
|
|
39
38
|
const { mode } = state;
|
|
40
39
|
if (mode.type === 'edit') {
|
|
41
|
-
return model?.projection?.
|
|
40
|
+
return model?.projection?.getFields().find((f) => f.id === mode.fieldId);
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
return undefined;
|
|
45
|
-
}, [model?.projection
|
|
44
|
+
}, [model?.projection, state]);
|
|
46
45
|
|
|
47
46
|
const field = existingField ?? newField;
|
|
48
47
|
|
|
@@ -64,7 +63,7 @@ export const ColumnSettings = ({ registry, model, modals, onNewColumn }: ColumnS
|
|
|
64
63
|
<Popover.Root modal={false} open={state?.type === 'columnSettings'}>
|
|
65
64
|
<Popover.VirtualTrigger virtualRef={modals.trigger} />
|
|
66
65
|
<Popover.Portal>
|
|
67
|
-
<Popover.Content classNames='md:
|
|
66
|
+
<Popover.Content classNames='md:w-64'>
|
|
68
67
|
<Popover.Viewport>
|
|
69
68
|
<FieldEditor
|
|
70
69
|
projection={model.projection}
|
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import type * as Types from 'effect/Types';
|
|
6
7
|
import React, { useMemo, useState } from 'react';
|
|
7
8
|
|
|
8
|
-
import { Obj } from '@dxos/echo';
|
|
9
|
-
import {
|
|
9
|
+
import { type JsonSchema, Obj } from '@dxos/echo';
|
|
10
|
+
import { Format } from '@dxos/echo/internal';
|
|
10
11
|
import { faker } from '@dxos/random';
|
|
11
|
-
import { useClient } from '@dxos/react-client';
|
|
12
12
|
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
13
|
-
import {
|
|
14
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
13
|
+
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
14
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
15
15
|
import { type SchemaPropertyDefinition } from '@dxos/schema';
|
|
16
|
-
import {
|
|
16
|
+
import { TestSchema } from '@dxos/schema/testing';
|
|
17
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
17
18
|
|
|
18
19
|
import { type TableFeatures } from '../../model';
|
|
19
20
|
import { translations } from '../../translations';
|
|
@@ -25,8 +26,8 @@ faker.seed(0);
|
|
|
25
26
|
const useTestPropertiesAndObjects = () => {
|
|
26
27
|
const properties = useMemo<SchemaPropertyDefinition[]>(
|
|
27
28
|
() => [
|
|
28
|
-
{ name: 'name', format:
|
|
29
|
-
{ name: 'age', format:
|
|
29
|
+
{ name: 'name', format: Format.TypeFormat.String },
|
|
30
|
+
{ name: 'age', format: Format.TypeFormat.Number },
|
|
30
31
|
],
|
|
31
32
|
[],
|
|
32
33
|
);
|
|
@@ -58,7 +59,7 @@ const DynamicTableStory = () => {
|
|
|
58
59
|
const meta = {
|
|
59
60
|
title: 'ui/react-ui-table/DynamicTable',
|
|
60
61
|
component: DynamicTable,
|
|
61
|
-
decorators: [withTheme],
|
|
62
|
+
decorators: [withTheme(), withLayout({ layout: 'fullscreen' }), withRegistry],
|
|
62
63
|
parameters: {
|
|
63
64
|
layout: 'fullscreen',
|
|
64
65
|
translations,
|
|
@@ -101,7 +102,7 @@ export const WithClickToSelect: StoryObj = {
|
|
|
101
102
|
|
|
102
103
|
export const WithJsonSchema: StoryObj = {
|
|
103
104
|
render: () => {
|
|
104
|
-
const schema = useMemo<
|
|
105
|
+
const schema = useMemo<Types.DeepMutable<JsonSchema.JsonSchema>>(
|
|
105
106
|
() => ({
|
|
106
107
|
type: 'object',
|
|
107
108
|
properties: {
|
|
@@ -130,10 +131,9 @@ export const WithJsonSchema: StoryObj = {
|
|
|
130
131
|
|
|
131
132
|
export const WithEchoSchema: StoryObj = {
|
|
132
133
|
render: () => {
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const objects = useQuery(space, schema ? Filter.type(schema) : Filter.nothing());
|
|
134
|
+
const { space } = useClientStory();
|
|
135
|
+
const schema = useSchema(space?.db, TestSchema.Person.typename);
|
|
136
|
+
const objects = useQuery(space?.db, schema ? Filter.type(schema) : Filter.nothing());
|
|
137
137
|
if (!schema) {
|
|
138
138
|
return <div>Loading schema...</div>;
|
|
139
139
|
}
|
|
@@ -142,13 +142,13 @@ export const WithEchoSchema: StoryObj = {
|
|
|
142
142
|
},
|
|
143
143
|
decorators: [
|
|
144
144
|
withClientProvider({
|
|
145
|
-
types: [
|
|
145
|
+
types: [TestSchema.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(
|
|
151
|
+
Obj.make(TestSchema.Person, {
|
|
152
152
|
name: faker.person.fullName(),
|
|
153
153
|
email: faker.internet.email(),
|
|
154
154
|
}),
|
|
@@ -2,25 +2,29 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
6
|
+
import type * as Types from 'effect/Types';
|
|
7
|
+
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
8
|
|
|
8
|
-
import { type
|
|
9
|
+
import { type Type } from '@dxos/echo';
|
|
10
|
+
import { type JsonSchema } from '@dxos/echo';
|
|
9
11
|
import { type ThemedClassName, useDefaultValue } from '@dxos/react-ui';
|
|
10
|
-
import {
|
|
12
|
+
import { type ProjectionModel } from '@dxos/schema';
|
|
13
|
+
import { mx } from '@dxos/ui-theme';
|
|
11
14
|
|
|
12
15
|
import { useTableModel } from '../../hooks';
|
|
13
16
|
import { type TableFeatures, TablePresentation, type TableRowAction } from '../../model';
|
|
17
|
+
import { type Table as TableType } from '../../types';
|
|
14
18
|
import { type TablePropertyDefinition, getBaseSchema, makeDynamicTable } from '../../util';
|
|
15
19
|
|
|
16
20
|
import { Table, type TableController } from './Table';
|
|
17
21
|
|
|
18
|
-
type DynamicTableProps = ThemedClassName<{
|
|
22
|
+
export type DynamicTableProps<T extends Type.AnyEntity = Type.AnyEntity> = ThemedClassName<{
|
|
23
|
+
schema?: T;
|
|
19
24
|
name?: string; // TODO(burdon): Remove?
|
|
20
25
|
rows: any[];
|
|
21
26
|
properties?: TablePropertyDefinition[];
|
|
22
|
-
jsonSchema?:
|
|
23
|
-
schema?: Schema.Schema.AnyNoContext;
|
|
27
|
+
jsonSchema?: Types.DeepMutable<JsonSchema.JsonSchema>;
|
|
24
28
|
features?: Partial<TableFeatures>;
|
|
25
29
|
rowActions?: TableRowAction[];
|
|
26
30
|
onRowClick?: (row: any) => void;
|
|
@@ -32,25 +36,30 @@ type DynamicTableProps = ThemedClassName<{
|
|
|
32
36
|
*/
|
|
33
37
|
// TODO(burdon): Instead of creating component variants, create helpers/hooks that normalize the props.
|
|
34
38
|
// TODO(burdon): Warning: Cannot update a component (`DynamicTable`) while rendering a different component (`DynamicTable`).
|
|
35
|
-
export const DynamicTable = ({
|
|
39
|
+
export const DynamicTable = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
36
40
|
classNames,
|
|
37
|
-
|
|
41
|
+
schema,
|
|
42
|
+
name = 'com.example.dynamic-table', // Remove default or make random; this will lead to type collisions.
|
|
38
43
|
rows,
|
|
39
44
|
properties,
|
|
40
|
-
jsonSchema:
|
|
41
|
-
schema,
|
|
45
|
+
jsonSchema: jsonSchemaProp,
|
|
42
46
|
rowActions,
|
|
43
47
|
onRowClick,
|
|
44
48
|
onRowAction,
|
|
45
49
|
...props
|
|
46
|
-
}: DynamicTableProps) => {
|
|
50
|
+
}: DynamicTableProps<T>) => {
|
|
51
|
+
const registry = useContext(RegistryContext);
|
|
52
|
+
const [dynamicTable, setDynamicTable] = useState<{ object: TableType.Table; projection: ProjectionModel }>();
|
|
53
|
+
|
|
47
54
|
// TODO(burdon): Remove variance from the props (should be normalized externally; possibly via hooks).
|
|
48
55
|
const { jsonSchema } = useMemo(
|
|
49
|
-
() => getBaseSchema({ typename: name, properties, jsonSchema:
|
|
50
|
-
[name, properties,
|
|
56
|
+
() => getBaseSchema({ typename: name, properties, jsonSchema: jsonSchemaProp, schema }),
|
|
57
|
+
[name, properties, jsonSchemaProp, schema],
|
|
51
58
|
);
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
setDynamicTable(makeDynamicTable({ registry, jsonSchema, properties }));
|
|
62
|
+
}, [registry, jsonSchema, properties]);
|
|
54
63
|
|
|
55
64
|
const tableRef = useRef<TableController>(null);
|
|
56
65
|
const handleCellUpdate = useCallback((cell: any) => {
|
|
@@ -72,9 +81,8 @@ export const DynamicTable = ({
|
|
|
72
81
|
|
|
73
82
|
const model = useTableModel({
|
|
74
83
|
rows,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
projection,
|
|
84
|
+
object: dynamicTable?.object,
|
|
85
|
+
projection: dynamicTable?.projection,
|
|
78
86
|
features,
|
|
79
87
|
rowActions,
|
|
80
88
|
onCellUpdate: handleCellUpdate,
|
|
@@ -84,14 +92,14 @@ export const DynamicTable = ({
|
|
|
84
92
|
|
|
85
93
|
const presentation = useMemo(() => {
|
|
86
94
|
if (model) {
|
|
87
|
-
return new TablePresentation(model);
|
|
95
|
+
return new TablePresentation(registry, model);
|
|
88
96
|
}
|
|
89
|
-
}, [model]);
|
|
97
|
+
}, [registry, model]);
|
|
90
98
|
|
|
91
99
|
// TODO(burdon): Do we need the outer divs?
|
|
92
100
|
return (
|
|
93
|
-
<div role='none' className={mx('
|
|
94
|
-
<div role='none' className='grid min-
|
|
101
|
+
<div role='none' className={mx('w-full h-full grow grid', classNames)}>
|
|
102
|
+
<div role='none' className='grid min-h-0 overflow-hidden'>
|
|
95
103
|
<Table.Root>
|
|
96
104
|
<Table.Main
|
|
97
105
|
ref={tableRef}
|
|
@@ -2,26 +2,27 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
5
6
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import
|
|
7
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
8
8
|
import { expect, userEvent, within } from 'storybook/test';
|
|
9
9
|
|
|
10
10
|
import { Obj, Type } from '@dxos/echo';
|
|
11
|
-
import {
|
|
11
|
+
import { View } from '@dxos/echo';
|
|
12
|
+
import { invariant } from '@dxos/invariant';
|
|
12
13
|
import { type DxGrid } from '@dxos/lit-grid';
|
|
13
14
|
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
14
15
|
import { faker } from '@dxos/random';
|
|
15
|
-
import {
|
|
16
|
-
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
16
|
+
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
17
17
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
18
18
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
19
19
|
import { translations as formTranslations } from '@dxos/react-ui-form';
|
|
20
|
-
import {
|
|
20
|
+
import { ViewModel } from '@dxos/schema';
|
|
21
21
|
import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
|
|
22
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
22
23
|
import { Organization, Person } from '@dxos/types';
|
|
23
24
|
|
|
24
|
-
import { useTableModel } from '../../hooks';
|
|
25
|
+
import { useProjectionModel, useTableModel } from '../../hooks';
|
|
25
26
|
import { type TableFeatures, TablePresentation, type TableRow } from '../../model';
|
|
26
27
|
import { translations } from '../../translations';
|
|
27
28
|
import { Table } from '../../types';
|
|
@@ -35,11 +36,10 @@ const generator: ValueGenerator = faker as any;
|
|
|
35
36
|
// TODO(burdon): Mutable and immutable views.
|
|
36
37
|
// TODO(burdon): Reconcile schemas types and utils (see API PR).
|
|
37
38
|
// TODO(burdon): Base type for T (with id); see ECHO API PR?
|
|
38
|
-
const useTestModel = <S extends Type.
|
|
39
|
-
const
|
|
40
|
-
const { space } =
|
|
41
|
-
const [
|
|
42
|
-
const [jsonSchema, setJsonSchema] = useState<JsonSchemaType>();
|
|
39
|
+
const useTestModel = <S extends Type.AnyObj>(schema: S, count: number) => {
|
|
40
|
+
const registry = useContext(RegistryContext);
|
|
41
|
+
const { space } = useClientStory();
|
|
42
|
+
const [object, setObject] = useState<Table.Table>();
|
|
43
43
|
|
|
44
44
|
const features = useMemo<TableFeatures>(
|
|
45
45
|
() => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
|
|
@@ -51,18 +51,14 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
setJsonSchema(jsonSchema);
|
|
60
|
-
setView(view);
|
|
61
|
-
space.db.add(view);
|
|
62
|
-
await space.db.schemaRegistry.register([effectSchema]);
|
|
63
|
-
}, [client, space, schema]);
|
|
54
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Type.getTypename(schema) });
|
|
55
|
+
const object = Table.make({ view, jsonSchema });
|
|
56
|
+
setObject(object);
|
|
57
|
+
space.db.add(object);
|
|
58
|
+
}, [space, schema]);
|
|
64
59
|
|
|
65
|
-
const
|
|
60
|
+
const projection = useProjectionModel(schema, object, registry);
|
|
61
|
+
const model = useTableModel<TableRow>({ object, projection, db: space?.db, features });
|
|
66
62
|
|
|
67
63
|
useEffect(() => {
|
|
68
64
|
if (!model || !space) {
|
|
@@ -80,34 +76,34 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
80
76
|
return;
|
|
81
77
|
}
|
|
82
78
|
|
|
83
|
-
return new TablePresentation(model);
|
|
84
|
-
}, [model]);
|
|
79
|
+
return new TablePresentation(registry, model);
|
|
80
|
+
}, [registry, model]);
|
|
85
81
|
|
|
86
82
|
return { model, presentation };
|
|
87
83
|
};
|
|
88
84
|
|
|
89
85
|
const DefaultStory = () => {
|
|
90
|
-
const client = useClient();
|
|
91
86
|
const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
|
|
92
87
|
const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
|
|
93
|
-
const { space } =
|
|
88
|
+
const { space } = useClientStory();
|
|
94
89
|
|
|
95
90
|
const handleCreate = useCallback(
|
|
96
|
-
(schema:
|
|
97
|
-
|
|
91
|
+
(schema: Type.AnyEntity, values: any) => {
|
|
92
|
+
invariant(Type.isObjectSchema(schema));
|
|
93
|
+
invariant(space);
|
|
94
|
+
return space.db.add(Obj.make(schema, values));
|
|
98
95
|
},
|
|
99
96
|
[space],
|
|
100
97
|
);
|
|
101
98
|
|
|
102
99
|
return (
|
|
103
|
-
<div className='
|
|
100
|
+
<div className='w-full h-full grid grid-cols-2 divide-x divide-separator'>
|
|
104
101
|
<TableComponent.Root>
|
|
105
102
|
<TableComponent.Main
|
|
106
103
|
model={orgModel}
|
|
107
104
|
schema={Organization.Organization}
|
|
108
105
|
presentation={orgPresentation}
|
|
109
106
|
onCreate={handleCreate}
|
|
110
|
-
client={client}
|
|
111
107
|
ignoreAttention
|
|
112
108
|
testId='relations-0'
|
|
113
109
|
/>
|
|
@@ -118,7 +114,6 @@ const DefaultStory = () => {
|
|
|
118
114
|
schema={Person.Person}
|
|
119
115
|
presentation={contactPresentation}
|
|
120
116
|
onCreate={handleCreate}
|
|
121
|
-
client={client}
|
|
122
117
|
ignoreAttention
|
|
123
118
|
testId='relations-1'
|
|
124
119
|
/>
|
|
@@ -131,8 +126,9 @@ const meta = {
|
|
|
131
126
|
title: 'ui/react-ui-table/Relations',
|
|
132
127
|
render: DefaultStory,
|
|
133
128
|
decorators: [
|
|
134
|
-
withTheme,
|
|
135
|
-
|
|
129
|
+
withTheme(),
|
|
130
|
+
withRegistry,
|
|
131
|
+
// TODO(thure): Shouldn't `layout: 'fullscreen'` below make this unnecessary?
|
|
136
132
|
withLayout({ classNames: 'fixed inset-0' }),
|
|
137
133
|
withClientProvider({
|
|
138
134
|
types: [View.View, Organization.Organization, Person.Person, Table.Table],
|
|
@@ -200,8 +196,9 @@ export const Default: Story = {
|
|
|
200
196
|
const saveButton = await body.findByTestId('save-button');
|
|
201
197
|
await userEvent.click(saveButton);
|
|
202
198
|
|
|
203
|
-
// Verify the relation was set (cell should now contain the org name)
|
|
204
|
-
|
|
199
|
+
// Verify the relation was set (cell should now contain the org name).
|
|
200
|
+
const updatedCell = within(secondGrid).getByTestId('grid.4.0');
|
|
201
|
+
await expect(updatedCell).toHaveTextContent(orgName.substring(0, 4));
|
|
205
202
|
|
|
206
203
|
// Test object creation (new relations) - equivalent to "new relations work as expected" test
|
|
207
204
|
// Find a different cell to test object creation (second row, relations column)
|
|
@@ -224,9 +221,9 @@ export const Default: Story = {
|
|
|
224
221
|
const newOrgName = 'Salieri LLC';
|
|
225
222
|
await userEvent.type(newSearchField, newOrgName);
|
|
226
223
|
|
|
227
|
-
//
|
|
228
|
-
const
|
|
229
|
-
await expect(
|
|
224
|
+
// Wait for the create option to appear (debounce is 200ms, allow time for render)
|
|
225
|
+
const createOption = await body.findByRole('option', undefined, { timeout: 500 });
|
|
226
|
+
await expect(createOption).toBeVisible();
|
|
230
227
|
|
|
231
228
|
// Press Enter to select/create
|
|
232
229
|
await userEvent.keyboard('{Enter}');
|
|
@@ -236,7 +233,8 @@ export const Default: Story = {
|
|
|
236
233
|
const saveObjectButton = await within(createReferencedObjectForm).findByTestId('save-button');
|
|
237
234
|
await userEvent.click(saveObjectButton);
|
|
238
235
|
|
|
239
|
-
// Verify the new object was created and relation was set
|
|
240
|
-
|
|
236
|
+
// Verify the new object was created and relation was set.
|
|
237
|
+
const updatedNewCell = within(secondGrid).getByTestId('grid.4.1');
|
|
238
|
+
await expect(updatedNewCell).toHaveTextContent(newOrgName);
|
|
241
239
|
},
|
|
242
240
|
} as any;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
5
6
|
import React from 'react';
|
|
6
7
|
|
|
7
8
|
import { DropdownMenu, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
@@ -13,8 +14,8 @@ type RowActionsMenuProps = { model: TableModel; modals: ModalController };
|
|
|
13
14
|
|
|
14
15
|
export const RowActionsMenu = ({ model, modals }: RowActionsMenuProps) => {
|
|
15
16
|
const { t } = useTranslation(translationKey);
|
|
16
|
-
const hasSelection = model.selection.hasSelection
|
|
17
|
-
const state = modals.state
|
|
17
|
+
const hasSelection = model.selection.hasSelection;
|
|
18
|
+
const state = useAtomValue(modals.state);
|
|
18
19
|
if (state?.type !== 'row') {
|
|
19
20
|
return null;
|
|
20
21
|
}
|