@dxos/react-ui-table 0.8.4-main.f9ba587 → 0.8.4-main.fd6878d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +18 -0
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs +1814 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +658 -2359
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +123 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +3 -5
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +20 -0
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs +1815 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +658 -2359
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +124 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +3 -5
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +4 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/{relations.stories.d.ts → Relations.stories.d.ts} +2 -2
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +1 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +8 -7
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +2 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +17 -16
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +2 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +4 -4
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/useTestTableModel.d.ts +41 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +8 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util/dynamic-table.d.ts +3 -4
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +9 -11
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +50 -36
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +15 -11
- package/src/components/Table/CreateRefPanel.tsx +6 -4
- package/src/components/Table/DynamicTable.stories.tsx +3 -2
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +36 -35
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +99 -144
- package/src/components/Table/Table.tsx +87 -71
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +30 -21
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -27
- package/src/components/TableCellEditor/TableCellEditor.tsx +42 -27
- package/src/components/TableCellEditor/extension.ts +2 -2
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +7 -24
- package/src/hooks/useTableModel.ts +21 -17
- package/src/index.ts +0 -1
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +6 -3
- package/src/model/table-model.test.ts +8 -8
- package/src/model/table-model.ts +68 -62
- package/src/model/table-presentation.test.ts +8 -8
- package/src/model/table-presentation.ts +35 -13
- package/src/model/table-sorting.ts +11 -11
- package/src/playwright/smoke.spec.ts +105 -14
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +123 -0
- package/src/translations.ts +2 -2
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +16 -0
- package/src/util/dynamic-table.ts +20 -18
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +2 -1
- package/src/util/table-controls.ts +4 -1
- package/src/util/table.ts +37 -111
- package/dist/lib/browser/chunk-Q5QQXJUO.mjs +0 -27
- package/dist/lib/browser/chunk-Q5QQXJUO.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs +0 -29
- package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs.map +0 -7
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/types/table.d.ts +0 -56
- package/dist/types/src/types/table.d.ts.map +0 -1
- package/src/types/table.ts +0 -27
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Schema } from 'effect';
|
|
6
|
-
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
6
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { getSnapshot } from '@dxos/echo-schema';
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
@@ -20,18 +20,26 @@ type FormCellEditorProps = {
|
|
|
20
20
|
fieldProjection: FieldProjection;
|
|
21
21
|
model?: TableModel;
|
|
22
22
|
schema?: Schema.Schema.AnyNoContext;
|
|
23
|
+
onSave?: () => void;
|
|
23
24
|
__gridScope: any;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
export const FormCellEditor = ({ fieldProjection, model, schema, __gridScope }: FormCellEditorProps) => {
|
|
27
|
-
const { editing, setEditing } = useGridContext('ArrayEditor', __gridScope);
|
|
28
|
-
const
|
|
27
|
+
export const FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }: FormCellEditorProps) => {
|
|
28
|
+
const { editing: contextEditing, setEditing } = useGridContext('ArrayEditor', __gridScope);
|
|
29
|
+
const [editing, setLocalEditing] = useState(false);
|
|
30
|
+
const anchorRef = useRef<HTMLButtonElement | null>(null);
|
|
29
31
|
|
|
30
32
|
useEffect(() => {
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
+
if (contextEditing && contextEditing.cellElement) {
|
|
34
|
+
anchorRef.current = (contextEditing.cellElement as HTMLElement).querySelector(
|
|
35
|
+
'.dx-grid__cell__content',
|
|
36
|
+
) as HTMLButtonElement;
|
|
37
|
+
setLocalEditing(true);
|
|
38
|
+
} else {
|
|
39
|
+
anchorRef.current = null;
|
|
40
|
+
setLocalEditing(false);
|
|
33
41
|
}
|
|
34
|
-
}, [
|
|
42
|
+
}, [contextEditing]);
|
|
35
43
|
|
|
36
44
|
const narrowedSchema = useMemo(() => {
|
|
37
45
|
if (!schema) {
|
|
@@ -41,8 +49,8 @@ export const FormCellEditor = ({ fieldProjection, model, schema, __gridScope }:
|
|
|
41
49
|
}, [JSON.stringify(schema), fieldProjection.field.path]); // TODO(burdon): Avoid stringify.
|
|
42
50
|
|
|
43
51
|
const originalRow = useMemo(() => {
|
|
44
|
-
if (model &&
|
|
45
|
-
const cell = parseCellIndex(
|
|
52
|
+
if (model && contextEditing) {
|
|
53
|
+
const cell = parseCellIndex(contextEditing.index);
|
|
46
54
|
const row = model.getRowAt(cell.row);
|
|
47
55
|
invariant(row);
|
|
48
56
|
|
|
@@ -50,7 +58,7 @@ export const FormCellEditor = ({ fieldProjection, model, schema, __gridScope }:
|
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
return undefined;
|
|
53
|
-
}, [model,
|
|
61
|
+
}, [model, contextEditing]);
|
|
54
62
|
|
|
55
63
|
const formValues = useMemo(() => {
|
|
56
64
|
if (originalRow) {
|
|
@@ -65,27 +73,28 @@ export const FormCellEditor = ({ fieldProjection, model, schema, __gridScope }:
|
|
|
65
73
|
const value = getDeep(values, [path]);
|
|
66
74
|
setDeep(originalRow, [path], value);
|
|
67
75
|
setEditing(null);
|
|
76
|
+
setLocalEditing(false);
|
|
77
|
+
onSave?.();
|
|
68
78
|
},
|
|
69
79
|
[fieldProjection.field.path, originalRow],
|
|
70
80
|
);
|
|
71
81
|
|
|
72
|
-
const handleOpenChange = useCallback(
|
|
73
|
-
(nextOpen
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
);
|
|
82
|
+
const handleOpenChange = useCallback((nextOpen: boolean) => {
|
|
83
|
+
if (nextOpen === false) {
|
|
84
|
+
setEditing(null);
|
|
85
|
+
onSave?.();
|
|
86
|
+
}
|
|
87
|
+
setLocalEditing(nextOpen);
|
|
88
|
+
}, []);
|
|
80
89
|
|
|
81
90
|
if (!editing) {
|
|
82
91
|
return null;
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
return (
|
|
86
|
-
<Popover.Root open={editing
|
|
87
|
-
<Popover.VirtualTrigger virtualRef={
|
|
88
|
-
<Popover.Content tabIndex={-1} classNames='popover-card-width'>
|
|
95
|
+
<Popover.Root open={editing} onOpenChange={handleOpenChange}>
|
|
96
|
+
<Popover.VirtualTrigger virtualRef={anchorRef} />
|
|
97
|
+
<Popover.Content tabIndex={-1} classNames='popover-card-width density-fine'>
|
|
89
98
|
<Popover.Arrow />
|
|
90
99
|
<Popover.Viewport>
|
|
91
100
|
<Form values={formValues} schema={narrowedSchema as any} onSave={handleSave} />
|
|
@@ -11,18 +11,19 @@ import { Obj } from '@dxos/echo';
|
|
|
11
11
|
import { type EchoSchema, isMutable } from '@dxos/echo-schema';
|
|
12
12
|
import { invariant } from '@dxos/invariant';
|
|
13
13
|
import { faker } from '@dxos/random';
|
|
14
|
-
import { Filter, useQuery
|
|
14
|
+
import { Filter, useQuery } from '@dxos/react-client/echo';
|
|
15
15
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { Grid, type GridEditing, defaultRowSize } from '@dxos/react-ui-grid';
|
|
17
|
+
import { DataType, createDefaultSchema } from '@dxos/schema';
|
|
18
18
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
19
19
|
|
|
20
|
-
import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
|
|
21
20
|
import { useTableModel } from '../../hooks';
|
|
22
21
|
import { type TableFeatures } from '../../model';
|
|
23
22
|
import { translations } from '../../translations';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
23
|
+
import { TableView } from '../../types';
|
|
24
|
+
import { createTable } from '../../util';
|
|
25
|
+
|
|
26
|
+
import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
|
|
26
27
|
|
|
27
28
|
type StoryProps = {
|
|
28
29
|
editing: GridEditing;
|
|
@@ -32,23 +33,16 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
32
33
|
const { space } = useClientProvider();
|
|
33
34
|
invariant(space);
|
|
34
35
|
|
|
35
|
-
const
|
|
36
|
-
const [
|
|
36
|
+
const views = useQuery(space, Filter.type(DataType.View));
|
|
37
|
+
const [view, setView] = useState<DataType.View>();
|
|
37
38
|
const [schema, setSchema] = useState<EchoSchema>();
|
|
38
39
|
useEffect(() => {
|
|
39
|
-
if (space &&
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
setSchema(space.db.schemaRegistry.getSchema(table.view.target!.query.typename!));
|
|
44
|
-
}
|
|
45
|
-
}, [space, tables]);
|
|
46
|
-
|
|
47
|
-
const projection = useMemo(() => {
|
|
48
|
-
if (schema && table?.view) {
|
|
49
|
-
return new ViewProjection(schema.jsonSchema, table.view.target!);
|
|
40
|
+
if (space && views.length && !view) {
|
|
41
|
+
const view = views[0];
|
|
42
|
+
setView(view);
|
|
43
|
+
setSchema(space.db.schemaRegistry.getSchema(view.query.typename!));
|
|
50
44
|
}
|
|
51
|
-
}, [
|
|
45
|
+
}, [space, views, view]);
|
|
52
46
|
|
|
53
47
|
const features: Partial<TableFeatures> = useMemo(
|
|
54
48
|
() => ({
|
|
@@ -59,7 +53,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
59
53
|
[schema],
|
|
60
54
|
);
|
|
61
55
|
|
|
62
|
-
const model = useTableModel({
|
|
56
|
+
const model = useTableModel({ view, schema: schema?.jsonSchema, features });
|
|
63
57
|
|
|
64
58
|
const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
|
|
65
59
|
// TODO(dmaretskyi): If no schema query nothing
|
|
@@ -73,7 +67,7 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
73
67
|
});
|
|
74
68
|
};
|
|
75
69
|
|
|
76
|
-
if (!model || !schema || !
|
|
70
|
+
if (!model || !schema || !view) {
|
|
77
71
|
return <div />;
|
|
78
72
|
}
|
|
79
73
|
|
|
@@ -93,16 +87,19 @@ const meta: Meta<StoryProps> = {
|
|
|
93
87
|
parameters: { translations, layout: 'centered' },
|
|
94
88
|
decorators: [
|
|
95
89
|
withClientProvider({
|
|
96
|
-
types: [
|
|
90
|
+
types: [DataType.View, TableView],
|
|
97
91
|
createIdentity: true,
|
|
98
92
|
createSpace: true,
|
|
99
93
|
onSpaceCreated: async ({ client, space }) => {
|
|
100
|
-
const
|
|
101
|
-
const
|
|
94
|
+
const schema = createDefaultSchema();
|
|
95
|
+
const { view } = await createTable({ client, space, typename: schema.typename });
|
|
96
|
+
space.db.add(view);
|
|
102
97
|
Array.from({ length: 10 }).forEach(() => {
|
|
103
98
|
space.db.add(
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
Obj.make(schema, {
|
|
100
|
+
title: faker.person.fullName(),
|
|
101
|
+
status: faker.helpers.arrayElement(['todo', 'in-progress', 'done'] as const),
|
|
102
|
+
description: faker.lorem.sentence(),
|
|
106
103
|
}),
|
|
107
104
|
);
|
|
108
105
|
});
|
|
@@ -8,29 +8,29 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
|
8
8
|
|
|
9
9
|
import { FormatEnum, TypeEnum } from '@dxos/echo-schema';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import { type
|
|
11
|
+
import { type DxGridAxis, type DxGridPosition } from '@dxos/lit-grid';
|
|
12
12
|
import { useThemeContext } from '@dxos/react-ui';
|
|
13
13
|
import { createMarkdownExtensions } from '@dxos/react-ui-editor';
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
editorKeys,
|
|
17
|
-
parseCellIndex,
|
|
18
|
-
useGridContext,
|
|
19
|
-
type DxGridPlanePosition,
|
|
15
|
+
type EditorBlurHandler,
|
|
20
16
|
type EditorKeyEvent,
|
|
21
17
|
type EditorKeyOrBlurHandler,
|
|
22
|
-
type EditorBlurHandler,
|
|
23
18
|
GridCellEditor,
|
|
24
19
|
type GridCellEditorProps,
|
|
25
20
|
type GridScopedProps,
|
|
21
|
+
cellQuery,
|
|
22
|
+
editorKeys,
|
|
23
|
+
parseCellIndex,
|
|
24
|
+
useGridContext,
|
|
26
25
|
} from '@dxos/react-ui-grid';
|
|
27
|
-
import {
|
|
26
|
+
import { createLinks, tagPicker } from '@dxos/react-ui-tag-picker';
|
|
28
27
|
import { type FieldProjection } from '@dxos/schema';
|
|
29
28
|
|
|
29
|
+
import { type ModalController, type TableModel } from '../../model';
|
|
30
|
+
|
|
30
31
|
import { CellValidationMessage } from './CellValidationMessage';
|
|
31
|
-
import { FormCellEditor } from './FormCellEditor';
|
|
32
32
|
import { completion } from './extension';
|
|
33
|
-
import {
|
|
33
|
+
import { FormCellEditor } from './FormCellEditor';
|
|
34
34
|
|
|
35
35
|
const newValue = Symbol.for('newValue');
|
|
36
36
|
|
|
@@ -47,8 +47,9 @@ export type TableCellEditorProps = {
|
|
|
47
47
|
model?: TableModel;
|
|
48
48
|
modals?: ModalController;
|
|
49
49
|
schema?: Schema.AnyNoContext;
|
|
50
|
-
onEnter?: (cell:
|
|
51
|
-
onFocus?:
|
|
50
|
+
onEnter?: (cell: DxGridPosition) => void;
|
|
51
|
+
onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
|
|
52
|
+
onSave?: () => void;
|
|
52
53
|
onQuery?: (field: FieldProjection, text: string) => Promise<QueryResult[]>;
|
|
53
54
|
};
|
|
54
55
|
|
|
@@ -58,6 +59,7 @@ export const TableValueEditor = ({
|
|
|
58
59
|
schema,
|
|
59
60
|
onEnter,
|
|
60
61
|
onFocus,
|
|
62
|
+
onSave,
|
|
61
63
|
onQuery,
|
|
62
64
|
__gridScope,
|
|
63
65
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
@@ -69,14 +71,26 @@ export const TableValueEditor = ({
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const { col } = parseCellIndex(editing.index);
|
|
72
|
-
const field = model.projection.
|
|
74
|
+
const field = model.projection.fields[col];
|
|
73
75
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
74
76
|
invariant(fieldProjection);
|
|
75
77
|
return fieldProjection;
|
|
76
78
|
}, [model, editing]);
|
|
77
79
|
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
+
if (
|
|
81
|
+
fieldProjection?.props.type === TypeEnum.Array ||
|
|
82
|
+
fieldProjection?.props.format === FormatEnum.SingleSelect
|
|
83
|
+
// TODO(thure): Support `FormatEnum.MultiSelect`
|
|
84
|
+
) {
|
|
85
|
+
return (
|
|
86
|
+
<FormCellEditor
|
|
87
|
+
fieldProjection={fieldProjection}
|
|
88
|
+
model={model}
|
|
89
|
+
schema={schema}
|
|
90
|
+
__gridScope={__gridScope}
|
|
91
|
+
onSave={onSave}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
// For all other types, use the existing cell editor
|
|
@@ -87,6 +101,7 @@ export const TableValueEditor = ({
|
|
|
87
101
|
onEnter={onEnter}
|
|
88
102
|
onFocus={onFocus}
|
|
89
103
|
onQuery={onQuery}
|
|
104
|
+
onSave={onSave}
|
|
90
105
|
__gridScope={__gridScope}
|
|
91
106
|
/>
|
|
92
107
|
);
|
|
@@ -100,13 +115,14 @@ export const TableCellEditor = ({
|
|
|
100
115
|
onEnter,
|
|
101
116
|
onFocus,
|
|
102
117
|
onQuery,
|
|
118
|
+
onSave,
|
|
103
119
|
__gridScope,
|
|
104
120
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
105
121
|
const { id: gridId, editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
|
|
106
122
|
const suppressNextBlur = useRef(false);
|
|
107
123
|
const { themeMode } = useThemeContext();
|
|
108
|
-
const [
|
|
109
|
-
const [
|
|
124
|
+
const [validationError, setValidationError] = useState<string | null>(null);
|
|
125
|
+
const [validationVariant, setValidationVariant] = useState<'error' | 'warning'>('error');
|
|
110
126
|
|
|
111
127
|
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
112
128
|
if (!model || !editing) {
|
|
@@ -114,7 +130,7 @@ export const TableCellEditor = ({
|
|
|
114
130
|
}
|
|
115
131
|
|
|
116
132
|
const { col } = parseCellIndex(editing.index);
|
|
117
|
-
const field = model.projection.
|
|
133
|
+
const field = model.projection.fields[col];
|
|
118
134
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
119
135
|
invariant(fieldProjection);
|
|
120
136
|
return fieldProjection;
|
|
@@ -131,7 +147,7 @@ export const TableCellEditor = ({
|
|
|
131
147
|
const { row, col } = cell;
|
|
132
148
|
|
|
133
149
|
if (model.isDraftCell(cell)) {
|
|
134
|
-
const field = model.projection.
|
|
150
|
+
const field = model.projection.fields[col];
|
|
135
151
|
const hasValidationError = model.hasDraftRowValidationError(row, field.path);
|
|
136
152
|
|
|
137
153
|
if (hasValidationError) {
|
|
@@ -167,6 +183,7 @@ export const TableCellEditor = ({
|
|
|
167
183
|
onEnter?.(cell);
|
|
168
184
|
onFocus?.();
|
|
169
185
|
setEditing(null);
|
|
186
|
+
onSave?.();
|
|
170
187
|
} else {
|
|
171
188
|
setValidationError(validationResult.error);
|
|
172
189
|
setValidationVariant('error');
|
|
@@ -205,9 +222,10 @@ export const TableCellEditor = ({
|
|
|
205
222
|
setValidationError(null);
|
|
206
223
|
model.setCellData(cell, value);
|
|
207
224
|
setEditing(null);
|
|
225
|
+
onSave?.();
|
|
208
226
|
onEnter?.(cell);
|
|
209
227
|
if (event && onFocus) {
|
|
210
|
-
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
228
|
+
onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
|
|
211
229
|
}
|
|
212
230
|
} else {
|
|
213
231
|
setValidationError(result.error);
|
|
@@ -216,12 +234,13 @@ export const TableCellEditor = ({
|
|
|
216
234
|
} else {
|
|
217
235
|
setValidationError(null);
|
|
218
236
|
setEditing(null);
|
|
237
|
+
onSave?.();
|
|
219
238
|
if (event && onFocus) {
|
|
220
239
|
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
221
240
|
}
|
|
222
241
|
}
|
|
223
242
|
},
|
|
224
|
-
[model, editing, onFocus, onEnter, fieldProjection, setEditing],
|
|
243
|
+
[model, editing, onFocus, onEnter, fieldProjection, setEditing, onSave],
|
|
225
244
|
);
|
|
226
245
|
|
|
227
246
|
const extension = useMemo(() => {
|
|
@@ -322,7 +341,7 @@ export const TableCellEditor = ({
|
|
|
322
341
|
if (model && editing) {
|
|
323
342
|
const cell = parseCellIndex(editing.index);
|
|
324
343
|
const { col } = cell;
|
|
325
|
-
const field = model.projection.
|
|
344
|
+
const field = model.projection.fields[col];
|
|
326
345
|
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
327
346
|
|
|
328
347
|
if (
|
|
@@ -376,11 +395,7 @@ export const TableCellEditor = ({
|
|
|
376
395
|
|
|
377
396
|
return (
|
|
378
397
|
<>
|
|
379
|
-
<CellValidationMessage
|
|
380
|
-
validationError={_validationError}
|
|
381
|
-
variant={_validationVariant}
|
|
382
|
-
__gridScope={__gridScope}
|
|
383
|
-
/>
|
|
398
|
+
<CellValidationMessage validationError={validationError} variant={validationVariant} __gridScope={__gridScope} />
|
|
384
399
|
<GridCellEditor extension={extension} getCellContent={getCellContent} onBlur={handleBlur} slots={editorSlots} />
|
|
385
400
|
</>
|
|
386
401
|
);
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
autocompletion,
|
|
7
|
-
pickedCompletion,
|
|
8
6
|
type CompletionContext,
|
|
9
7
|
type CompletionResult,
|
|
8
|
+
autocompletion,
|
|
9
|
+
pickedCompletion,
|
|
10
10
|
} from '@codemirror/autocomplete';
|
|
11
11
|
import { type Extension } from '@codemirror/state';
|
|
12
12
|
import { EditorView } from '@codemirror/view';
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
type ActionGraphEdges,
|
|
12
12
|
type ActionGraphNodes,
|
|
13
13
|
type ActionGraphProps,
|
|
14
|
-
createGapSeparator,
|
|
15
|
-
createMenuAction,
|
|
16
14
|
type MenuAction,
|
|
17
15
|
MenuProvider,
|
|
18
16
|
ToolbarMenu,
|
|
17
|
+
createGapSeparator,
|
|
18
|
+
createMenuAction,
|
|
19
19
|
useMenuActions,
|
|
20
20
|
} from '@dxos/react-ui-menu';
|
|
21
21
|
import { rxFromSignal } from '@dxos/react-ui-menu';
|
|
@@ -5,13 +5,10 @@
|
|
|
5
5
|
import { Schema } from 'effect';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { FormatEnum, ObjectId, TypeEnum, TypedObject, setValue } from '@dxos/echo-schema';
|
|
9
|
+
import { live } from '@dxos/live-object';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
|
-
import {
|
|
12
|
-
import { createView, type ViewProjection } from '@dxos/schema';
|
|
13
|
-
|
|
14
|
-
import { TableType } from '../types';
|
|
11
|
+
import { type ProjectionModel } from '@dxos/schema';
|
|
15
12
|
|
|
16
13
|
export const TestSchema = TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
17
14
|
id: ObjectId,
|
|
@@ -21,18 +18,6 @@ export const TestSchema = TypedObject({ typename: 'example.com/type/Test', versi
|
|
|
21
18
|
netWorth: Schema.optional(Schema.Number),
|
|
22
19
|
});
|
|
23
20
|
|
|
24
|
-
export const createTable = (schema = TestSchema) => {
|
|
25
|
-
return Obj.make(TableType, {
|
|
26
|
-
view: Ref.make(
|
|
27
|
-
createView({
|
|
28
|
-
name: 'Test',
|
|
29
|
-
typename: schema.typename,
|
|
30
|
-
jsonSchema: toJsonSchema(schema),
|
|
31
|
-
}),
|
|
32
|
-
),
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
|
|
36
21
|
export const createItems = (n: number) => {
|
|
37
22
|
const { data } = live({
|
|
38
23
|
data: Array.from({ length: n }, () => ({
|
|
@@ -47,13 +32,13 @@ export const createItems = (n: number) => {
|
|
|
47
32
|
};
|
|
48
33
|
|
|
49
34
|
export type SimulatorProps = {
|
|
50
|
-
|
|
35
|
+
projection: ProjectionModel;
|
|
51
36
|
items: any[];
|
|
52
37
|
insertInterval?: number;
|
|
53
38
|
updateInterval?: number;
|
|
54
39
|
};
|
|
55
40
|
|
|
56
|
-
export const useSimulator = ({ items,
|
|
41
|
+
export const useSimulator = ({ items, projection, insertInterval, updateInterval }: SimulatorProps) => {
|
|
57
42
|
useEffect(() => {
|
|
58
43
|
if (!insertInterval) {
|
|
59
44
|
return;
|
|
@@ -74,10 +59,8 @@ export const useSimulator = ({ items, table, insertInterval, updateInterval }: S
|
|
|
74
59
|
|
|
75
60
|
const i = setInterval(() => {
|
|
76
61
|
const rowIdx = Math.floor(Math.random() * items.length);
|
|
77
|
-
const fields =
|
|
62
|
+
const fields = projection.fields ?? [];
|
|
78
63
|
const columnIdx = Math.floor(Math.random() * fields.length);
|
|
79
|
-
// TODO(ZaymonFC): ... This is borked.
|
|
80
|
-
const projection: ViewProjection = (table as any)._projection;
|
|
81
64
|
const field = fields[columnIdx];
|
|
82
65
|
const item = items[rowIdx];
|
|
83
66
|
|
|
@@ -115,5 +98,5 @@ export const useSimulator = ({ items, table, insertInterval, updateInterval }: S
|
|
|
115
98
|
}, updateInterval);
|
|
116
99
|
|
|
117
100
|
return () => clearInterval(i);
|
|
118
|
-
}, [items,
|
|
101
|
+
}, [items, projection.fields, updateInterval]);
|
|
119
102
|
};
|
|
@@ -4,19 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
import { effect } from '@preact/signals-core';
|
|
6
6
|
import orderBy from 'lodash.orderby';
|
|
7
|
-
import { useEffect, useState } from 'react';
|
|
7
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { type JsonSchemaType } from '@dxos/echo-schema';
|
|
10
|
+
import { type Live } from '@dxos/live-object';
|
|
11
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
12
|
+
import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
|
|
13
|
+
import { type DataType, type ProjectionModel } from '@dxos/schema';
|
|
12
14
|
import { isNonNullable } from '@dxos/util';
|
|
13
15
|
|
|
14
|
-
import {
|
|
15
|
-
import { type TableType } from '../types';
|
|
16
|
+
import { TableModel, type TableModelProps, type TableRow, type TableRowAction } from '../model';
|
|
16
17
|
|
|
17
18
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
view?: DataType.View;
|
|
20
|
+
schema?: JsonSchemaType;
|
|
21
|
+
projection?: ProjectionModel;
|
|
20
22
|
rows?: Live<T>[];
|
|
21
23
|
rowActions?: TableRowAction[];
|
|
22
24
|
onSelectionChanged?: (selection: string[]) => void;
|
|
@@ -27,7 +29,8 @@ export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
|
27
29
|
>;
|
|
28
30
|
|
|
29
31
|
export const useTableModel = <T extends TableRow = TableRow>({
|
|
30
|
-
|
|
32
|
+
view,
|
|
33
|
+
schema,
|
|
31
34
|
projection,
|
|
32
35
|
rows,
|
|
33
36
|
rowActions,
|
|
@@ -36,21 +39,24 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
36
39
|
onRowAction,
|
|
37
40
|
...props
|
|
38
41
|
}: UseTableModelParams<T>): TableModel<T> | undefined => {
|
|
42
|
+
const selected = useSelected(view && fullyQualifiedId(view), 'multi');
|
|
43
|
+
const initialSelection = useMemo(() => selected, [view]);
|
|
44
|
+
|
|
39
45
|
const [model, setModel] = useState<TableModel<T>>();
|
|
40
46
|
useEffect(() => {
|
|
41
|
-
if (!
|
|
47
|
+
if (!view || !schema) {
|
|
42
48
|
return;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
let model: TableModel<T> | undefined;
|
|
46
52
|
const t = setTimeout(async () => {
|
|
47
53
|
model = new TableModel<T>({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
view: table.view?.target,
|
|
54
|
+
view,
|
|
55
|
+
schema,
|
|
51
56
|
projection,
|
|
52
57
|
features,
|
|
53
58
|
rowActions,
|
|
59
|
+
initialSelection,
|
|
54
60
|
onRowAction,
|
|
55
61
|
...props,
|
|
56
62
|
});
|
|
@@ -62,7 +68,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
62
68
|
clearTimeout(t);
|
|
63
69
|
void model?.close();
|
|
64
70
|
};
|
|
65
|
-
}, [
|
|
71
|
+
}, [view, schema, projection, features, rowActions, initialSelection]); // TODO(burdon): Trigger if callbacks change?
|
|
66
72
|
|
|
67
73
|
// Update data.
|
|
68
74
|
useEffect(() => {
|
|
@@ -77,9 +83,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
77
83
|
}
|
|
78
84
|
}, [model, rows]);
|
|
79
85
|
|
|
80
|
-
const { multiSelect, clear } = useSelectionActions(
|
|
81
|
-
[table?.id, table?.view?.target?.query.typename].filter(isNonNullable),
|
|
82
|
-
);
|
|
86
|
+
const { multiSelect, clear } = useSelectionActions([model?.id].filter(isNonNullable));
|
|
83
87
|
|
|
84
88
|
useEffect(() => {
|
|
85
89
|
if (!model) {
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
|
-
import { type
|
|
6
|
+
import { type MutableRefObject, type RefObject } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type Live } from '@dxos/live-object';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { computed, effect, signal
|
|
5
|
+
import { type ReadonlySignal, type Signal, computed, effect, signal } from '@preact/signals-core';
|
|
6
6
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
|
|
9
|
-
import { type TableRow } from './table-model';
|
|
10
9
|
import { touch } from '../util';
|
|
11
10
|
|
|
11
|
+
import { type TableRow } from './table-model';
|
|
12
|
+
|
|
12
13
|
export type SelectionMode = 'single' | 'multiple';
|
|
13
14
|
|
|
14
15
|
export class SelectionModel<T extends TableRow> extends Resource {
|
|
15
|
-
private readonly _selection
|
|
16
|
+
private readonly _selection: Signal<Set<string>>;
|
|
16
17
|
|
|
17
18
|
private readonly _validSelectedIds = computed<Set<string>>(() => {
|
|
18
19
|
const rows = this._rows.value;
|
|
@@ -38,9 +39,11 @@ export class SelectionModel<T extends TableRow> extends Resource {
|
|
|
38
39
|
constructor(
|
|
39
40
|
private readonly _rows: ReadonlySignal<T[]>,
|
|
40
41
|
private readonly _selectionMode: SelectionMode,
|
|
42
|
+
private readonly _initialSelection: string[],
|
|
41
43
|
private readonly _onSelectionChanged?: () => void,
|
|
42
44
|
) {
|
|
43
45
|
super();
|
|
46
|
+
this._selection = signal(new Set(this._initialSelection));
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
protected override async _open(): Promise<void> {
|
|
@@ -6,16 +6,17 @@ import { computed } from '@preact/signals-core';
|
|
|
6
6
|
import { Schema } from 'effect';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Obj
|
|
9
|
+
import { Obj } from '@dxos/echo';
|
|
10
10
|
import { TypedObject } from '@dxos/echo-schema';
|
|
11
11
|
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
12
12
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
13
|
+
import { live } from '@dxos/live-object';
|
|
13
14
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
14
|
-
import {
|
|
15
|
-
|
|
15
|
+
import { createView } from '@dxos/schema';
|
|
16
|
+
|
|
17
|
+
import { TableView } from '../types';
|
|
16
18
|
|
|
17
19
|
import { TableModel, type TableModelProps } from './table-model';
|
|
18
|
-
import { TableType } from '../types';
|
|
19
20
|
|
|
20
21
|
// TODO(burdon): Tests are disabled in project.json since they bring in plugin deps.
|
|
21
22
|
// Restore once factored out into react-ui-table.
|
|
@@ -107,8 +108,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
107
108
|
|
|
108
109
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
109
110
|
const schema = createEchoSchema(Test);
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
return new TableModel({ id: table.id, space: undefined, view, projection, ...props });
|
|
111
|
+
const table = Obj.make(TableView, { sizes: {} });
|
|
112
|
+
const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
|
|
113
|
+
return new TableModel({ view, schema: schema.jsonSchema, ...props });
|
|
114
114
|
};
|