@dxos/react-ui-table 0.8.4-main.7ace549 → 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-5XWSB3JP.mjs → chunk-BF54SSNL.mjs} +27 -28
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-KNC7XYT6.mjs → chunk-PTHMYAAM.mjs} +620 -652
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1154 -1036
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +20 -19
- 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-CWSGS3Y2.mjs → chunk-GI72MQSG.mjs} +620 -651
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-MKTMOY46.mjs → chunk-SIRDIPHZ.mjs} +27 -28
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1154 -1036
- 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 +20 -19
- 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/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 +2 -16
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +6 -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/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 +6 -3
- 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 +2 -2
- 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 -13
- package/dist/types/src/components/testing.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 +2 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +6 -5
- 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 +100 -18
- 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 +5 -8
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +13 -15
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +12 -10
- 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 +11 -11
- package/src/components/Table/DynamicTable.tsx +30 -30
- package/src/components/Table/Relations.stories.tsx +34 -31
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +80 -67
- 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 +10 -7
- package/src/components/TableCellEditor/TableCellEditor.tsx +27 -27
- package/src/components/TableToolbar/TableToolbar.tsx +24 -18
- package/src/components/testing.ts +37 -11
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +29 -9
- package/src/hooks/useTableModel.ts +27 -23
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +81 -53
- package/src/model/table-model.ts +410 -138
- package/src/model/table-presentation.test.ts +58 -48
- package/src/model/table-presentation.ts +34 -28
- package/src/playwright/TableManager.ts +12 -5
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +48 -4
- package/src/testing/useTestTableModel.ts +24 -25
- package/src/translations.ts +1 -1
- package/src/types/Table.ts +28 -38
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +36 -19
- 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-5XWSB3JP.mjs.map +0 -7
- package/dist/lib/browser/chunk-KNC7XYT6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CWSGS3Y2.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MKTMOY46.mjs.map +0 -7
- package/dist/types/src/model/table-sorting.d.ts +0 -56
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/src/model/table-sorting.ts +0 -234
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/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/debug": "0.8.4-main.
|
|
71
|
-
"@dxos/echo": "0.8.4-main.
|
|
72
|
-
"@dxos/echo-
|
|
73
|
-
"@dxos/effect": "0.8.4-main.
|
|
74
|
-
"@dxos/invariant": "0.8.4-main.
|
|
75
|
-
"@dxos/keys": "0.8.4-main.
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/log": "0.8.4-main.
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/react-
|
|
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/
|
|
90
|
-
"@dxos/
|
|
91
|
-
"@dxos/
|
|
92
|
-
"@dxos/
|
|
93
|
-
"@dxos/types": "0.8.4-main.7ace549",
|
|
94
|
-
"@dxos/util": "0.8.4-main.7ace549"
|
|
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/
|
|
109
|
-
"@dxos/
|
|
110
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
111
|
-
"@dxos/
|
|
112
|
-
"@dxos/
|
|
113
|
-
"@dxos/
|
|
114
|
-
"@dxos/echo-signals": "0.8.4-main.7ace549"
|
|
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 { Format
|
|
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
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';
|
|
@@ -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
|
}
|
|
@@ -2,26 +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 {
|
|
11
|
-
import {
|
|
12
|
+
import { type ProjectionModel } from '@dxos/schema';
|
|
13
|
+
import { mx } from '@dxos/ui-theme';
|
|
12
14
|
|
|
13
15
|
import { useTableModel } from '../../hooks';
|
|
14
16
|
import { type TableFeatures, TablePresentation, type TableRowAction } from '../../model';
|
|
17
|
+
import { type Table as TableType } from '../../types';
|
|
15
18
|
import { type TablePropertyDefinition, getBaseSchema, makeDynamicTable } from '../../util';
|
|
16
19
|
|
|
17
20
|
import { Table, type TableController } from './Table';
|
|
18
21
|
|
|
19
|
-
type DynamicTableProps = ThemedClassName<{
|
|
22
|
+
export type DynamicTableProps<T extends Type.AnyEntity = Type.AnyEntity> = ThemedClassName<{
|
|
23
|
+
schema?: T;
|
|
20
24
|
name?: string; // TODO(burdon): Remove?
|
|
21
25
|
rows: any[];
|
|
22
26
|
properties?: TablePropertyDefinition[];
|
|
23
|
-
jsonSchema?:
|
|
24
|
-
schema?: Schema.Schema.AnyNoContext;
|
|
27
|
+
jsonSchema?: Types.DeepMutable<JsonSchema.JsonSchema>;
|
|
25
28
|
features?: Partial<TableFeatures>;
|
|
26
29
|
rowActions?: TableRowAction[];
|
|
27
30
|
onRowClick?: (row: any) => void;
|
|
@@ -33,25 +36,30 @@ type DynamicTableProps = ThemedClassName<{
|
|
|
33
36
|
*/
|
|
34
37
|
// TODO(burdon): Instead of creating component variants, create helpers/hooks that normalize the props.
|
|
35
38
|
// TODO(burdon): Warning: Cannot update a component (`DynamicTable`) while rendering a different component (`DynamicTable`).
|
|
36
|
-
export const DynamicTable = ({
|
|
39
|
+
export const DynamicTable = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
37
40
|
classNames,
|
|
38
|
-
|
|
41
|
+
schema,
|
|
42
|
+
name = 'com.example.dynamic-table', // Remove default or make random; this will lead to type collisions.
|
|
39
43
|
rows,
|
|
40
44
|
properties,
|
|
41
|
-
jsonSchema:
|
|
42
|
-
schema,
|
|
45
|
+
jsonSchema: jsonSchemaProp,
|
|
43
46
|
rowActions,
|
|
44
47
|
onRowClick,
|
|
45
48
|
onRowAction,
|
|
46
49
|
...props
|
|
47
|
-
}: DynamicTableProps) => {
|
|
50
|
+
}: DynamicTableProps<T>) => {
|
|
51
|
+
const registry = useContext(RegistryContext);
|
|
52
|
+
const [dynamicTable, setDynamicTable] = useState<{ object: TableType.Table; projection: ProjectionModel }>();
|
|
53
|
+
|
|
48
54
|
// TODO(burdon): Remove variance from the props (should be normalized externally; possibly via hooks).
|
|
49
55
|
const { jsonSchema } = useMemo(
|
|
50
|
-
() => getBaseSchema({ typename: name, properties, jsonSchema:
|
|
51
|
-
[name, properties,
|
|
56
|
+
() => getBaseSchema({ typename: name, properties, jsonSchema: jsonSchemaProp, schema }),
|
|
57
|
+
[name, properties, jsonSchemaProp, schema],
|
|
52
58
|
);
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
setDynamicTable(makeDynamicTable({ registry, jsonSchema, properties }));
|
|
62
|
+
}, [registry, jsonSchema, properties]);
|
|
55
63
|
|
|
56
64
|
const tableRef = useRef<TableController>(null);
|
|
57
65
|
const handleCellUpdate = useCallback((cell: any) => {
|
|
@@ -71,18 +79,10 @@ export const DynamicTable = ({
|
|
|
71
79
|
}) as const,
|
|
72
80
|
);
|
|
73
81
|
|
|
74
|
-
const projectionModel = useMemo(() => {
|
|
75
|
-
if (jsonSchema && projection) {
|
|
76
|
-
const model = new ProjectionModel(jsonSchema, projection);
|
|
77
|
-
model.normalizeView();
|
|
78
|
-
return model;
|
|
79
|
-
}
|
|
80
|
-
}, [jsonSchema, projection]);
|
|
81
|
-
|
|
82
82
|
const model = useTableModel({
|
|
83
83
|
rows,
|
|
84
|
-
object,
|
|
85
|
-
projection:
|
|
84
|
+
object: dynamicTable?.object,
|
|
85
|
+
projection: dynamicTable?.projection,
|
|
86
86
|
features,
|
|
87
87
|
rowActions,
|
|
88
88
|
onCellUpdate: handleCellUpdate,
|
|
@@ -92,14 +92,14 @@ export const DynamicTable = ({
|
|
|
92
92
|
|
|
93
93
|
const presentation = useMemo(() => {
|
|
94
94
|
if (model) {
|
|
95
|
-
return new TablePresentation(model);
|
|
95
|
+
return new TablePresentation(registry, model);
|
|
96
96
|
}
|
|
97
|
-
}, [model]);
|
|
97
|
+
}, [registry, model]);
|
|
98
98
|
|
|
99
99
|
// TODO(burdon): Do we need the outer divs?
|
|
100
100
|
return (
|
|
101
|
-
<div role='none' className={mx('
|
|
102
|
-
<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'>
|
|
103
103
|
<Table.Root>
|
|
104
104
|
<Table.Main
|
|
105
105
|
ref={tableRef}
|
|
@@ -2,22 +2,24 @@
|
|
|
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 { View } from '@dxos/echo';
|
|
12
|
+
import { invariant } from '@dxos/invariant';
|
|
11
13
|
import { type DxGrid } from '@dxos/lit-grid';
|
|
12
14
|
import '@dxos/lit-ui/dx-tag-picker.pcss';
|
|
13
15
|
import { faker } from '@dxos/random';
|
|
14
|
-
import {
|
|
15
|
-
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
16
|
+
import { useClientStory, withClientProvider } from '@dxos/react-client/testing';
|
|
16
17
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
17
18
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
18
19
|
import { translations as formTranslations } from '@dxos/react-ui-form';
|
|
19
|
-
import {
|
|
20
|
+
import { ViewModel } from '@dxos/schema';
|
|
20
21
|
import { type ValueGenerator, createAsyncGenerator } from '@dxos/schema/testing';
|
|
22
|
+
import { withRegistry } from '@dxos/storybook-utils';
|
|
21
23
|
import { Organization, Person } from '@dxos/types';
|
|
22
24
|
|
|
23
25
|
import { useProjectionModel, useTableModel } from '../../hooks';
|
|
@@ -34,9 +36,9 @@ const generator: ValueGenerator = faker as any;
|
|
|
34
36
|
// TODO(burdon): Mutable and immutable views.
|
|
35
37
|
// TODO(burdon): Reconcile schemas types and utils (see API PR).
|
|
36
38
|
// TODO(burdon): Base type for T (with id); see ECHO API PR?
|
|
37
|
-
const useTestModel = <S extends Type.
|
|
38
|
-
const
|
|
39
|
-
const { space } =
|
|
39
|
+
const useTestModel = <S extends Type.AnyObj>(schema: S, count: number) => {
|
|
40
|
+
const registry = useContext(RegistryContext);
|
|
41
|
+
const { space } = useClientStory();
|
|
40
42
|
const [object, setObject] = useState<Table.Table>();
|
|
41
43
|
|
|
42
44
|
const features = useMemo<TableFeatures>(
|
|
@@ -49,15 +51,14 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
const { view, jsonSchema } = await
|
|
54
|
+
const { view, jsonSchema } = await ViewModel.makeFromDatabase({ db: space.db, typename: Type.getTypename(schema) });
|
|
53
55
|
const object = Table.make({ view, jsonSchema });
|
|
54
56
|
setObject(object);
|
|
55
57
|
space.db.add(object);
|
|
56
|
-
|
|
57
|
-
}, [client, space, schema]);
|
|
58
|
+
}, [space, schema]);
|
|
58
59
|
|
|
59
|
-
const projection = useProjectionModel(schema, object);
|
|
60
|
-
const model = useTableModel<TableRow>({ object, projection, features });
|
|
60
|
+
const projection = useProjectionModel(schema, object, registry);
|
|
61
|
+
const model = useTableModel<TableRow>({ object, projection, db: space?.db, features });
|
|
61
62
|
|
|
62
63
|
useEffect(() => {
|
|
63
64
|
if (!model || !space) {
|
|
@@ -75,34 +76,34 @@ const useTestModel = <S extends Type.Obj.Any>(schema: S, count: number) => {
|
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
return new TablePresentation(model);
|
|
79
|
-
}, [model]);
|
|
79
|
+
return new TablePresentation(registry, model);
|
|
80
|
+
}, [registry, model]);
|
|
80
81
|
|
|
81
82
|
return { model, presentation };
|
|
82
83
|
};
|
|
83
84
|
|
|
84
85
|
const DefaultStory = () => {
|
|
85
|
-
const client = useClient();
|
|
86
86
|
const { model: orgModel, presentation: orgPresentation } = useTestModel(Organization.Organization, 50);
|
|
87
87
|
const { model: contactModel, presentation: contactPresentation } = useTestModel(Person.Person, 50);
|
|
88
|
-
const { space } =
|
|
88
|
+
const { space } = useClientStory();
|
|
89
89
|
|
|
90
90
|
const handleCreate = useCallback(
|
|
91
|
-
(schema:
|
|
92
|
-
|
|
91
|
+
(schema: Type.AnyEntity, values: any) => {
|
|
92
|
+
invariant(Type.isObjectSchema(schema));
|
|
93
|
+
invariant(space);
|
|
94
|
+
return space.db.add(Obj.make(schema, values));
|
|
93
95
|
},
|
|
94
96
|
[space],
|
|
95
97
|
);
|
|
96
98
|
|
|
97
99
|
return (
|
|
98
|
-
<div className='
|
|
100
|
+
<div className='w-full h-full grid grid-cols-2 divide-x divide-separator'>
|
|
99
101
|
<TableComponent.Root>
|
|
100
102
|
<TableComponent.Main
|
|
101
103
|
model={orgModel}
|
|
102
104
|
schema={Organization.Organization}
|
|
103
105
|
presentation={orgPresentation}
|
|
104
106
|
onCreate={handleCreate}
|
|
105
|
-
client={client}
|
|
106
107
|
ignoreAttention
|
|
107
108
|
testId='relations-0'
|
|
108
109
|
/>
|
|
@@ -113,7 +114,6 @@ const DefaultStory = () => {
|
|
|
113
114
|
schema={Person.Person}
|
|
114
115
|
presentation={contactPresentation}
|
|
115
116
|
onCreate={handleCreate}
|
|
116
|
-
client={client}
|
|
117
117
|
ignoreAttention
|
|
118
118
|
testId='relations-1'
|
|
119
119
|
/>
|
|
@@ -126,8 +126,9 @@ const meta = {
|
|
|
126
126
|
title: 'ui/react-ui-table/Relations',
|
|
127
127
|
render: DefaultStory,
|
|
128
128
|
decorators: [
|
|
129
|
-
withTheme,
|
|
130
|
-
|
|
129
|
+
withTheme(),
|
|
130
|
+
withRegistry,
|
|
131
|
+
// TODO(thure): Shouldn't `layout: 'fullscreen'` below make this unnecessary?
|
|
131
132
|
withLayout({ classNames: 'fixed inset-0' }),
|
|
132
133
|
withClientProvider({
|
|
133
134
|
types: [View.View, Organization.Organization, Person.Person, Table.Table],
|
|
@@ -195,8 +196,9 @@ export const Default: Story = {
|
|
|
195
196
|
const saveButton = await body.findByTestId('save-button');
|
|
196
197
|
await userEvent.click(saveButton);
|
|
197
198
|
|
|
198
|
-
// Verify the relation was set (cell should now contain the org name)
|
|
199
|
-
|
|
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));
|
|
200
202
|
|
|
201
203
|
// Test object creation (new relations) - equivalent to "new relations work as expected" test
|
|
202
204
|
// Find a different cell to test object creation (second row, relations column)
|
|
@@ -219,9 +221,9 @@ export const Default: Story = {
|
|
|
219
221
|
const newOrgName = 'Salieri LLC';
|
|
220
222
|
await userEvent.type(newSearchField, newOrgName);
|
|
221
223
|
|
|
222
|
-
//
|
|
223
|
-
const
|
|
224
|
-
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();
|
|
225
227
|
|
|
226
228
|
// Press Enter to select/create
|
|
227
229
|
await userEvent.keyboard('{Enter}');
|
|
@@ -231,7 +233,8 @@ export const Default: Story = {
|
|
|
231
233
|
const saveObjectButton = await within(createReferencedObjectForm).findByTestId('save-button');
|
|
232
234
|
await userEvent.click(saveObjectButton);
|
|
233
235
|
|
|
234
|
-
// Verify the new object was created and relation was set
|
|
235
|
-
|
|
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);
|
|
236
239
|
},
|
|
237
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
|
}
|