@dxos/react-ui-table 0.8.1-staging.391c573 → 0.8.1-staging.9eaf14f
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-3FR6NHPD.mjs → chunk-YWNRUF4J.mjs} +8 -6
- package/dist/lib/browser/chunk-YWNRUF4J.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +625 -225
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node/{chunk-NB3SFHGA.cjs → chunk-TGG6OZ54.cjs} +10 -8
- package/dist/lib/node/chunk-TGG6OZ54.cjs.map +7 -0
- package/dist/lib/node/index.cjs +618 -220
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +3 -3
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{chunk-RLVVOZW5.mjs → chunk-KKLP54L3.mjs} +8 -6
- package/dist/lib/node-esm/chunk-KKLP54L3.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +625 -225
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +24 -0
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -0
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +10 -0
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -0
- 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 +4 -3
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/index.d.ts +1 -0
- package/dist/types/src/components/Table/index.d.ts.map +1 -1
- package/dist/types/src/components/Table/relations.stories.d.ts +9 -0
- package/dist/types/src/components/Table/relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +7 -7
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +8 -9
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/index.d.ts +1 -0
- package/dist/types/src/model/index.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +6 -3
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +42 -16
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +4 -4
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +2 -2
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/types/table.d.ts +7 -32
- package/dist/types/src/types/table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +31 -0
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +2 -2
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +5 -3
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/package.json +37 -36
- package/src/components/Table/ColumnActionsMenu.tsx +6 -4
- package/src/components/Table/ColumnSettings.tsx +5 -6
- package/src/components/Table/CreateRefPanel.tsx +3 -3
- package/src/components/Table/DynamicTable.stories.tsx +155 -0
- package/src/components/Table/DynamicTable.tsx +104 -0
- package/src/components/Table/RefPanel.tsx +3 -3
- package/src/components/Table/RowActionsMenu.tsx +18 -3
- package/src/components/Table/Table.stories.tsx +170 -115
- package/src/components/Table/Table.tsx +71 -26
- package/src/components/Table/index.ts +1 -0
- package/src/components/Table/relations.stories.tsx +113 -0
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +15 -5
- package/src/components/TableCellEditor/TableCellEditor.tsx +102 -4
- package/src/components/testing.ts +0 -1
- package/src/hooks/useTableModel.ts +35 -14
- package/src/model/index.ts +1 -0
- package/src/model/selection-model.ts +12 -2
- package/src/model/table-model.test.ts +2 -2
- package/src/model/table-model.ts +117 -32
- package/src/model/table-presentation.test.ts +2 -2
- package/src/model/table-presentation.ts +94 -22
- package/src/model/table-sorting.ts +3 -2
- package/src/playwright/smoke.spec.ts +2 -3
- package/src/types/table.ts +9 -10
- package/src/util/dynamic-table.ts +103 -0
- package/src/util/index.ts +1 -0
- package/src/util/table-controls.ts +4 -4
- package/src/util/table.ts +68 -65
- package/dist/lib/browser/_deprecated/index.mjs +0 -1483
- package/dist/lib/browser/_deprecated/index.mjs.map +0 -7
- package/dist/lib/browser/chunk-3FR6NHPD.mjs.map +0 -7
- package/dist/lib/node/_deprecated/index.cjs +0 -1511
- package/dist/lib/node/_deprecated/index.cjs.map +0 -7
- package/dist/lib/node/chunk-NB3SFHGA.cjs.map +0 -7
- package/dist/lib/node-esm/_deprecated/index.mjs +0 -1485
- package/dist/lib/node-esm/_deprecated/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RLVVOZW5.mjs.map +0 -7
- package/dist/types/src/_deprecated/components/Cell/Cell.d.ts +0 -12
- package/dist/types/src/_deprecated/components/Cell/Cell.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts +0 -13
- package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Cell/index.d.ts +0 -2
- package/dist/types/src/_deprecated/components/Cell/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts +0 -23
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts +0 -36
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts +0 -15
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts +0 -84
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/index.d.ts +0 -3
- package/dist/types/src/_deprecated/components/ColumnMenu/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/Table.d.ts +0 -20
- package/dist/types/src/_deprecated/components/Table/Table.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts +0 -116
- package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/TableBody.d.ts +0 -12
- package/dist/types/src/_deprecated/components/Table/TableBody.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/TableContext.d.ts +0 -13
- package/dist/types/src/_deprecated/components/Table/TableContext.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/TableHead.d.ts +0 -9
- package/dist/types/src/_deprecated/components/Table/TableHead.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/TableRootContext.d.ts +0 -17
- package/dist/types/src/_deprecated/components/Table/TableRootContext.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/index.d.ts +0 -5
- package/dist/types/src/_deprecated/components/Table/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/Table/props.d.ts +0 -42
- package/dist/types/src/_deprecated/components/Table/props.d.ts.map +0 -1
- package/dist/types/src/_deprecated/components/index.d.ts +0 -4
- package/dist/types/src/_deprecated/components/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/helpers.d.ts +0 -91
- package/dist/types/src/_deprecated/helpers.d.ts.map +0 -1
- package/dist/types/src/_deprecated/hooks/index.d.ts +0 -5
- package/dist/types/src/_deprecated/hooks/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/hooks/useColumnSizing.d.ts +0 -12
- package/dist/types/src/_deprecated/hooks/useColumnSizing.d.ts.map +0 -1
- package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts +0 -15
- package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts.map +0 -1
- package/dist/types/src/_deprecated/hooks/usePinLastRow.d.ts +0 -3
- package/dist/types/src/_deprecated/hooks/usePinLastRow.d.ts.map +0 -1
- package/dist/types/src/_deprecated/hooks/useRowSelection.d.ts +0 -7
- package/dist/types/src/_deprecated/hooks/useRowSelection.d.ts.map +0 -1
- package/dist/types/src/_deprecated/index.d.ts +0 -8
- package/dist/types/src/_deprecated/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/schema/Schema.stories.d.ts +0 -11
- package/dist/types/src/_deprecated/schema/Schema.stories.d.ts.map +0 -1
- package/dist/types/src/_deprecated/schema/column-utils.d.ts +0 -7
- package/dist/types/src/_deprecated/schema/column-utils.d.ts.map +0 -1
- package/dist/types/src/_deprecated/schema/index.d.ts +0 -3
- package/dist/types/src/_deprecated/schema/index.d.ts.map +0 -1
- package/dist/types/src/_deprecated/schema/types.d.ts +0 -27
- package/dist/types/src/_deprecated/schema/types.d.ts.map +0 -1
- package/dist/types/src/_deprecated/theme.d.ts +0 -29
- package/dist/types/src/_deprecated/theme.d.ts.map +0 -1
- package/dist/types/src/_deprecated/translations.d.ts +0 -21
- package/dist/types/src/_deprecated/translations.d.ts.map +0 -1
- package/dist/types/src/_deprecated/types.d.ts +0 -26
- package/dist/types/src/_deprecated/types.d.ts.map +0 -1
- package/src/_deprecated/components/Cell/Cell.tsx +0 -88
- package/src/_deprecated/components/Cell/CellCombobox.tsx +0 -103
- package/src/_deprecated/components/Cell/index.ts +0 -5
- package/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.tsx +0 -129
- package/src/_deprecated/components/ColumnMenu/ColumnMenu.tsx +0 -182
- package/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.tsx +0 -108
- package/src/_deprecated/components/ColumnMenu/ColumnSettings.tsx +0 -207
- package/src/_deprecated/components/ColumnMenu/index.ts +0 -6
- package/src/_deprecated/components/Table/Table.stories.tsx +0 -508
- package/src/_deprecated/components/Table/Table.tsx +0 -301
- package/src/_deprecated/components/Table/TableBody.tsx +0 -82
- package/src/_deprecated/components/Table/TableContext.tsx +0 -34
- package/src/_deprecated/components/Table/TableHead.tsx +0 -78
- package/src/_deprecated/components/Table/TableRootContext.ts +0 -39
- package/src/_deprecated/components/Table/index.ts +0 -8
- package/src/_deprecated/components/Table/props.ts +0 -60
- package/src/_deprecated/components/index.ts +0 -7
- package/src/_deprecated/helpers.tsx +0 -526
- package/src/_deprecated/hooks/index.ts +0 -8
- package/src/_deprecated/hooks/useColumnSizing.ts +0 -46
- package/src/_deprecated/hooks/useColumnSorting.ts +0 -64
- package/src/_deprecated/hooks/usePinLastRow.ts +0 -47
- package/src/_deprecated/hooks/useRowSelection.ts +0 -40
- package/src/_deprecated/index.ts +0 -12
- package/src/_deprecated/schema/Schema.stories.tsx +0 -59
- package/src/_deprecated/schema/column-utils.ts +0 -54
- package/src/_deprecated/schema/index.ts +0 -6
- package/src/_deprecated/schema/types.ts +0 -40
- package/src/_deprecated/theme.ts +0 -82
- package/src/_deprecated/translations.ts +0 -26
- package/src/_deprecated/types.ts +0 -38
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import { type StoryObj, type Meta } from '@storybook/react';
|
|
8
|
+
import React, { useEffect, useMemo } from 'react';
|
|
9
|
+
|
|
10
|
+
import { AST, type BaseObject, ImmutableSchema, type BaseSchema, type HasId } from '@dxos/echo-schema';
|
|
11
|
+
import { getAnnotation } from '@dxos/effect';
|
|
12
|
+
import { faker } from '@dxos/random';
|
|
13
|
+
import { create, makeRef, type ReactiveObject } from '@dxos/react-client/echo';
|
|
14
|
+
import { withClientProvider } from '@dxos/react-client/testing';
|
|
15
|
+
import { createView, ViewProjection, ViewType } from '@dxos/schema';
|
|
16
|
+
import { createGenerator, Testing, type ValueGenerator } from '@dxos/schema/testing';
|
|
17
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
18
|
+
|
|
19
|
+
import { Table } from './Table';
|
|
20
|
+
import { useTableModel } from '../../hooks';
|
|
21
|
+
import { TablePresentation } from '../../model';
|
|
22
|
+
import translations from '../../translations';
|
|
23
|
+
import { TableType } from '../../types';
|
|
24
|
+
|
|
25
|
+
faker.seed(1);
|
|
26
|
+
const generator: ValueGenerator = faker as any;
|
|
27
|
+
|
|
28
|
+
// TODO(burdon): Many-to-many relations.
|
|
29
|
+
// TODO(burdon): Mutable and immutable views.
|
|
30
|
+
// TODO(burdon): Reconcile schemas types and utils (see API PR).
|
|
31
|
+
// TODO(burdon): Base type for T (with id); see ECHO API PR?
|
|
32
|
+
const useTestModel = <T extends BaseObject & HasId>(schema: BaseSchema<T>, count: number) => {
|
|
33
|
+
const table = useMemo(() => {
|
|
34
|
+
// const { typename } = pipe(schema.ast, AST.getAnnotation<TypeAnnotation>(TypeAnnotationId), Option.getOrThrow);
|
|
35
|
+
const typename = schema.typename;
|
|
36
|
+
const name = getAnnotation<string>(AST.TitleAnnotationId)(schema.ast) ?? typename;
|
|
37
|
+
const view = createView({ name, typename, jsonSchema: schema.jsonSchema });
|
|
38
|
+
return create(TableType, { view: makeRef(view) });
|
|
39
|
+
}, [schema]);
|
|
40
|
+
|
|
41
|
+
const projection = useMemo(() => {
|
|
42
|
+
if (!table.view?.target) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// TODO(burdon): Just pass in view? Reuse same jsonSchema instance? View determines if mutable, etc.
|
|
47
|
+
return new ViewProjection(schema.jsonSchema, table.view.target);
|
|
48
|
+
}, [schema, table]);
|
|
49
|
+
|
|
50
|
+
const model = useTableModel({ table, projection, rows: [] });
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!model) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const objectGenerator = createGenerator(generator, schema, { optional: true });
|
|
57
|
+
const objects: ReactiveObject<T>[] = Array.from({ length: count }).map(() => objectGenerator.createObject());
|
|
58
|
+
model.setRows(objects);
|
|
59
|
+
}, [model]);
|
|
60
|
+
|
|
61
|
+
const presentation = useMemo(() => {
|
|
62
|
+
if (!model) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return new TablePresentation(model);
|
|
67
|
+
}, [model]);
|
|
68
|
+
|
|
69
|
+
return { model, presentation };
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const DefaultStory = () => {
|
|
73
|
+
// TODO(burdon): Remove need for ImmutableSchema wrapper at API-level.
|
|
74
|
+
const orgSchema = useMemo(() => new ImmutableSchema(Testing.Org), []);
|
|
75
|
+
const { model: orgModel, presentation: orgPresentation } = useTestModel<Testing.Org>(orgSchema, 50);
|
|
76
|
+
|
|
77
|
+
// TODO(burdon): Generate links with references.
|
|
78
|
+
const contactSchema = useMemo(() => new ImmutableSchema(Testing.Contact), []);
|
|
79
|
+
const { model: contactModel, presentation: contactPresentation } = useTestModel<Testing.Contact>(contactSchema, 50);
|
|
80
|
+
|
|
81
|
+
// TODO(burdon): Scrolling isn't working.
|
|
82
|
+
return (
|
|
83
|
+
<div className='grow grid grid-cols-2 divide-x divide-separator'>
|
|
84
|
+
<Table.Root>
|
|
85
|
+
<Table.Main model={orgModel} presentation={orgPresentation} />
|
|
86
|
+
</Table.Root>
|
|
87
|
+
<Table.Root>
|
|
88
|
+
<Table.Main model={contactModel} presentation={contactPresentation} />
|
|
89
|
+
</Table.Root>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const meta: Meta<typeof DefaultStory> = {
|
|
95
|
+
title: 'ui/react-ui-table/relations',
|
|
96
|
+
render: DefaultStory,
|
|
97
|
+
parameters: { translations },
|
|
98
|
+
decorators: [
|
|
99
|
+
withClientProvider({
|
|
100
|
+
types: [TableType, ViewType, Testing.Org, Testing.Contact],
|
|
101
|
+
createIdentity: true,
|
|
102
|
+
createSpace: true,
|
|
103
|
+
}),
|
|
104
|
+
withTheme,
|
|
105
|
+
withLayout({ fullscreen: true, tooltips: true }),
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default meta;
|
|
110
|
+
|
|
111
|
+
type Story = StoryObj<typeof DefaultStory>;
|
|
112
|
+
|
|
113
|
+
export const Default: Story = {};
|
|
@@ -7,7 +7,7 @@ import '@dxos-theme';
|
|
|
7
7
|
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
8
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
9
9
|
|
|
10
|
-
import { type EchoSchema } from '@dxos/echo-schema';
|
|
10
|
+
import { ImmutableSchema, type EchoSchema } from '@dxos/echo-schema';
|
|
11
11
|
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { faker } from '@dxos/random';
|
|
13
13
|
import { Filter, useQuery, create } from '@dxos/react-client/echo';
|
|
@@ -18,6 +18,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
|
18
18
|
|
|
19
19
|
import { TableCellEditor, type TableCellEditorProps } from './TableCellEditor';
|
|
20
20
|
import { useTableModel } from '../../hooks';
|
|
21
|
+
import { type TableFeatures } from '../../model';
|
|
21
22
|
import translations from '../../translations';
|
|
22
23
|
import { TableType } from '../../types';
|
|
23
24
|
import { initializeTable } from '../../util';
|
|
@@ -44,11 +45,20 @@ const DefaultStory = ({ editing }: StoryProps) => {
|
|
|
44
45
|
|
|
45
46
|
const projection = useMemo(() => {
|
|
46
47
|
if (schema && table?.view) {
|
|
47
|
-
return new ViewProjection(schema, table.view.target!);
|
|
48
|
+
return new ViewProjection(schema.jsonSchema, table.view.target!);
|
|
48
49
|
}
|
|
49
50
|
}, [schema, table?.view]);
|
|
50
51
|
|
|
51
|
-
const
|
|
52
|
+
const features: Partial<TableFeatures> = useMemo(
|
|
53
|
+
() => ({
|
|
54
|
+
selection: { enabled: true, mode: 'multiple' },
|
|
55
|
+
dataEditable: true,
|
|
56
|
+
schemaEditable: !(schema instanceof ImmutableSchema),
|
|
57
|
+
}),
|
|
58
|
+
[],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const model = useTableModel({ table, projection, features });
|
|
52
62
|
|
|
53
63
|
const handleQuery: TableCellEditorProps['onQuery'] = async ({ field }) => {
|
|
54
64
|
const { objects } = await space.db.query(schema).run();
|
|
@@ -84,9 +94,9 @@ const meta: Meta<StoryProps> = {
|
|
|
84
94
|
types: [TableType, ViewType],
|
|
85
95
|
createIdentity: true,
|
|
86
96
|
createSpace: true,
|
|
87
|
-
onSpaceCreated: async ({ space }) => {
|
|
97
|
+
onSpaceCreated: async ({ client, space }) => {
|
|
88
98
|
const table = space.db.add(create(TableType, {}));
|
|
89
|
-
const schema = await initializeTable({ space, table });
|
|
99
|
+
const schema = await initializeTable({ client, space, table });
|
|
90
100
|
Array.from({ length: 10 }).forEach(() => {
|
|
91
101
|
space.db.add(
|
|
92
102
|
create(schema, {
|
|
@@ -8,6 +8,8 @@ import React, { useCallback, useMemo, useRef } from 'react';
|
|
|
8
8
|
import { FormatEnum } from '@dxos/echo-schema';
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
10
|
import { type DxGrid } from '@dxos/lit-grid';
|
|
11
|
+
import { useThemeContext } from '@dxos/react-ui';
|
|
12
|
+
import { createMarkdownExtensions } from '@dxos/react-ui-editor';
|
|
11
13
|
import {
|
|
12
14
|
cellQuery,
|
|
13
15
|
editorKeys,
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
type GridCellEditorProps,
|
|
22
24
|
type GridScopedProps,
|
|
23
25
|
} from '@dxos/react-ui-grid';
|
|
26
|
+
import { tagPickerExtension, createLinks } from '@dxos/react-ui-tag-picker';
|
|
24
27
|
import { type FieldProjection } from '@dxos/schema';
|
|
25
28
|
|
|
26
29
|
import { completion } from './extension';
|
|
@@ -55,6 +58,7 @@ export const TableCellEditor = ({
|
|
|
55
58
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
56
59
|
const { id: gridId, editing, setEditing } = useGridContext('TableCellEditor', __gridScope);
|
|
57
60
|
const suppressNextBlur = useRef(false);
|
|
61
|
+
const { themeMode } = useThemeContext();
|
|
58
62
|
|
|
59
63
|
const fieldProjection = useMemo<FieldProjection | undefined>(() => {
|
|
60
64
|
if (!model || !editing) {
|
|
@@ -80,7 +84,7 @@ export const TableCellEditor = ({
|
|
|
80
84
|
onFocus?.();
|
|
81
85
|
setEditing(null);
|
|
82
86
|
},
|
|
83
|
-
[model, editing],
|
|
87
|
+
[model, editing, onEnter, onFocus, setEditing],
|
|
84
88
|
);
|
|
85
89
|
|
|
86
90
|
const handleBlur = useCallback<EditorBlurHandler>(
|
|
@@ -113,7 +117,7 @@ export const TableCellEditor = ({
|
|
|
113
117
|
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
114
118
|
}
|
|
115
119
|
},
|
|
116
|
-
[model, editing, onFocus, onEnter, fieldProjection
|
|
120
|
+
[model, editing, onFocus, onEnter, fieldProjection],
|
|
117
121
|
);
|
|
118
122
|
|
|
119
123
|
const extension = useMemo(() => {
|
|
@@ -128,6 +132,50 @@ export const TableCellEditor = ({
|
|
|
128
132
|
}),
|
|
129
133
|
];
|
|
130
134
|
|
|
135
|
+
const format = fieldProjection.props.format;
|
|
136
|
+
|
|
137
|
+
if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
|
|
138
|
+
// TODO(ZaymonFC): Reconcile this with the TagPicker component?
|
|
139
|
+
// Add markdown extensions needed by tag picker.
|
|
140
|
+
extension.push(createMarkdownExtensions({ themeMode }));
|
|
141
|
+
|
|
142
|
+
const options = fieldProjection.props.options || [];
|
|
143
|
+
|
|
144
|
+
const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
|
|
145
|
+
|
|
146
|
+
extension.push(
|
|
147
|
+
tagPickerExtension({
|
|
148
|
+
mode,
|
|
149
|
+
inGrid: true,
|
|
150
|
+
onSearch: (text, selectedIds) => {
|
|
151
|
+
return options
|
|
152
|
+
.filter(
|
|
153
|
+
(option) =>
|
|
154
|
+
selectedIds.indexOf(option.id) === -1 &&
|
|
155
|
+
(text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase())),
|
|
156
|
+
)
|
|
157
|
+
.map((option) => ({
|
|
158
|
+
id: option.id,
|
|
159
|
+
label: option.title,
|
|
160
|
+
hue: option.color as any,
|
|
161
|
+
}));
|
|
162
|
+
},
|
|
163
|
+
onUpdate: (ids) => {
|
|
164
|
+
if (model && editing) {
|
|
165
|
+
if (ids.length === 0) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (mode === 'single-select') {
|
|
169
|
+
handleEnter(ids[0]);
|
|
170
|
+
} else {
|
|
171
|
+
handleEnter(ids);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
131
179
|
if (onQuery) {
|
|
132
180
|
switch (fieldProjection.props.format) {
|
|
133
181
|
case FormatEnum.Ref: {
|
|
@@ -164,11 +212,61 @@ export const TableCellEditor = ({
|
|
|
164
212
|
}
|
|
165
213
|
|
|
166
214
|
return extension;
|
|
167
|
-
}, [model, modals, editing, fieldProjection, handleClose]);
|
|
215
|
+
}, [model, modals, editing, fieldProjection, handleClose, themeMode]);
|
|
168
216
|
|
|
169
217
|
const getCellContent = useCallback<GridCellEditorProps['getCellContent']>(() => {
|
|
170
218
|
if (model && editing) {
|
|
171
|
-
const
|
|
219
|
+
const cell = parseCellIndex(editing.index);
|
|
220
|
+
const { col } = cell;
|
|
221
|
+
const field = model.projection.view.fields[col];
|
|
222
|
+
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
223
|
+
|
|
224
|
+
if (
|
|
225
|
+
fieldProjection?.props.format === FormatEnum.SingleSelect ||
|
|
226
|
+
fieldProjection?.props.format === FormatEnum.MultiSelect
|
|
227
|
+
) {
|
|
228
|
+
const value = model.getCellData(cell);
|
|
229
|
+
|
|
230
|
+
if (value !== undefined) {
|
|
231
|
+
const options = fieldProjection.props.options || [];
|
|
232
|
+
|
|
233
|
+
if (fieldProjection.props.format === FormatEnum.MultiSelect && Array.isArray(value)) {
|
|
234
|
+
const tagItems = value
|
|
235
|
+
.map((id) => {
|
|
236
|
+
const option = options.find((o) => o.id === id);
|
|
237
|
+
if (option) {
|
|
238
|
+
return {
|
|
239
|
+
id,
|
|
240
|
+
label: option.title,
|
|
241
|
+
hue: option.color as any,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
})
|
|
246
|
+
.filter((item): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
247
|
+
|
|
248
|
+
return createLinks(tagItems);
|
|
249
|
+
} else {
|
|
250
|
+
const option = options.find((o) => o.id === value);
|
|
251
|
+
|
|
252
|
+
if (option) {
|
|
253
|
+
const tagItem = {
|
|
254
|
+
id: value,
|
|
255
|
+
label: option.title,
|
|
256
|
+
hue: option.color as any,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return createLinks([tagItem]);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Return empty string if no value
|
|
265
|
+
return '';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Default behavior for other formats
|
|
269
|
+
const value = model.getCellData(cell);
|
|
172
270
|
return value !== undefined ? String(value) : '';
|
|
173
271
|
}
|
|
174
272
|
}, [model, editing]);
|
|
@@ -8,7 +8,6 @@ import { setValue, toJsonSchema, S, TypeEnum, TypedObject, FormatEnum } from '@d
|
|
|
8
8
|
import { faker } from '@dxos/random';
|
|
9
9
|
import { create, makeRef } from '@dxos/react-client/echo';
|
|
10
10
|
import { createView, type ViewProjection } from '@dxos/schema';
|
|
11
|
-
import {} from '@dxos/schema';
|
|
12
11
|
|
|
13
12
|
import { TableType } from '../types';
|
|
14
13
|
|
|
@@ -5,24 +5,34 @@
|
|
|
5
5
|
import { effect } from '@preact/signals-core';
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { type ReactiveObject } from '@dxos/react-client/echo';
|
|
8
|
+
import { fullyQualifiedId, getSpace, type ReactiveObject } from '@dxos/react-client/echo';
|
|
9
9
|
import { useSelectionActions } from '@dxos/react-ui-attention';
|
|
10
10
|
import { type ViewProjection } from '@dxos/schema';
|
|
11
11
|
import { isNonNullable } from '@dxos/util';
|
|
12
12
|
|
|
13
|
-
import { type
|
|
13
|
+
import { type TableRow, TableModel, type TableModelProps, type TableRowAction } from '../model';
|
|
14
14
|
import { type TableType } from '../types';
|
|
15
15
|
|
|
16
|
-
export type UseTableModelParams<T extends
|
|
16
|
+
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
17
17
|
table?: TableType;
|
|
18
18
|
projection?: ViewProjection;
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
rows?: ReactiveObject<T>[];
|
|
20
|
+
rowActions?: TableRowAction[];
|
|
21
|
+
onSelectionChanged?: (selection: string[]) => void;
|
|
22
|
+
onRowAction?: (actionId: string, data: T) => void;
|
|
23
|
+
} & Pick<
|
|
24
|
+
TableModelProps<T>,
|
|
25
|
+
'features' | 'onInsertRow' | 'onDeleteRows' | 'onDeleteColumn' | 'onCellUpdate' | 'onRowOrderChange'
|
|
26
|
+
>;
|
|
21
27
|
|
|
22
|
-
export const useTableModel = <T extends
|
|
23
|
-
objects,
|
|
28
|
+
export const useTableModel = <T extends TableRow = TableRow>({
|
|
24
29
|
table,
|
|
25
30
|
projection,
|
|
31
|
+
rows,
|
|
32
|
+
rowActions,
|
|
33
|
+
features,
|
|
34
|
+
onSelectionChanged,
|
|
35
|
+
onRowAction,
|
|
26
36
|
...props
|
|
27
37
|
}: UseTableModelParams<T>): TableModel<T> | undefined => {
|
|
28
38
|
const [model, setModel] = useState<TableModel<T>>();
|
|
@@ -33,7 +43,16 @@ export const useTableModel = <T extends BaseTableRow = { id: string }>({
|
|
|
33
43
|
|
|
34
44
|
let model: TableModel<T> | undefined;
|
|
35
45
|
const t = setTimeout(async () => {
|
|
36
|
-
model = new TableModel<T>({
|
|
46
|
+
model = new TableModel<T>({
|
|
47
|
+
id: fullyQualifiedId(table),
|
|
48
|
+
space: getSpace(table),
|
|
49
|
+
view: table.view?.target,
|
|
50
|
+
projection,
|
|
51
|
+
features,
|
|
52
|
+
rowActions,
|
|
53
|
+
onRowAction,
|
|
54
|
+
...props,
|
|
55
|
+
});
|
|
37
56
|
await model.open();
|
|
38
57
|
setModel(model);
|
|
39
58
|
});
|
|
@@ -42,14 +61,14 @@ export const useTableModel = <T extends BaseTableRow = { id: string }>({
|
|
|
42
61
|
clearTimeout(t);
|
|
43
62
|
void model?.close();
|
|
44
63
|
};
|
|
45
|
-
}, [table, projection]); // TODO(burdon): Trigger if callbacks change?
|
|
64
|
+
}, [table, projection, table?.view?.target, features, rowActions]); // TODO(burdon): Trigger if callbacks change?
|
|
46
65
|
|
|
47
66
|
// Update data.
|
|
48
67
|
useEffect(() => {
|
|
49
|
-
if (
|
|
50
|
-
model?.setRows(
|
|
68
|
+
if (rows) {
|
|
69
|
+
model?.setRows(rows);
|
|
51
70
|
}
|
|
52
|
-
}, [model,
|
|
71
|
+
}, [model, rows]);
|
|
53
72
|
|
|
54
73
|
const { select, clear } = useSelectionActions([table?.id, table?.view?.target?.query.typename].filter(isNonNullable));
|
|
55
74
|
|
|
@@ -59,7 +78,9 @@ export const useTableModel = <T extends BaseTableRow = { id: string }>({
|
|
|
59
78
|
}
|
|
60
79
|
|
|
61
80
|
const unsubscribe = effect(() => {
|
|
62
|
-
|
|
81
|
+
const selectedItems = [...model.selection.selection.value];
|
|
82
|
+
select(selectedItems);
|
|
83
|
+
onSelectionChanged?.(selectedItems);
|
|
63
84
|
});
|
|
64
85
|
|
|
65
86
|
// Maybe clear the selection here?
|
|
@@ -67,7 +88,7 @@ export const useTableModel = <T extends BaseTableRow = { id: string }>({
|
|
|
67
88
|
clear();
|
|
68
89
|
unsubscribe();
|
|
69
90
|
};
|
|
70
|
-
}, [model]);
|
|
91
|
+
}, [model, onSelectionChanged]);
|
|
71
92
|
|
|
72
93
|
return model;
|
|
73
94
|
};
|
package/src/model/index.ts
CHANGED
|
@@ -6,10 +6,12 @@ import { computed, effect, signal, type ReadonlySignal } from '@preact/signals-c
|
|
|
6
6
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
|
|
9
|
-
import { type
|
|
9
|
+
import { type TableRow } from './table-model';
|
|
10
10
|
import { touch } from '../util';
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export type SelectionMode = 'single' | 'multiple';
|
|
13
|
+
|
|
14
|
+
export class SelectionModel<T extends TableRow> extends Resource {
|
|
13
15
|
private readonly _selection = signal<Set<string>>(new Set());
|
|
14
16
|
|
|
15
17
|
private readonly _validSelectedIds = computed<Set<string>>(() => {
|
|
@@ -35,6 +37,7 @@ export class SelectionModel<T extends BaseTableRow> extends Resource {
|
|
|
35
37
|
|
|
36
38
|
constructor(
|
|
37
39
|
private readonly _rows: ReadonlySignal<T[]>,
|
|
40
|
+
private readonly _selectionMode: SelectionMode,
|
|
38
41
|
private readonly _onSelectionChanged?: () => void,
|
|
39
42
|
) {
|
|
40
43
|
super();
|
|
@@ -64,6 +67,10 @@ export class SelectionModel<T extends BaseTableRow> extends Resource {
|
|
|
64
67
|
return this._allSelected;
|
|
65
68
|
}
|
|
66
69
|
|
|
70
|
+
public get selectionMode(): SelectionMode {
|
|
71
|
+
return this._selectionMode;
|
|
72
|
+
}
|
|
73
|
+
|
|
67
74
|
public getSelectedRows = (): T[] => {
|
|
68
75
|
const selectedIds = this._validSelectedIds.value;
|
|
69
76
|
return this._rows.value.filter((row) => selectedIds.has(row.id));
|
|
@@ -89,6 +96,9 @@ export class SelectionModel<T extends BaseTableRow> extends Resource {
|
|
|
89
96
|
if (newSelection.has(row.id)) {
|
|
90
97
|
newSelection.delete(row.id);
|
|
91
98
|
} else {
|
|
99
|
+
if (this._selectionMode === 'single') {
|
|
100
|
+
newSelection.clear();
|
|
101
|
+
}
|
|
92
102
|
newSelection.add(row.id);
|
|
93
103
|
}
|
|
94
104
|
|
|
@@ -106,7 +106,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
106
106
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
107
107
|
const schema = createEchoSchema(Test);
|
|
108
108
|
const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
|
|
109
|
-
const projection = new ViewProjection(schema, view);
|
|
109
|
+
const projection = new ViewProjection(schema.jsonSchema, view);
|
|
110
110
|
const table = create(TableType, { view: makeRef(view) });
|
|
111
|
-
return new TableModel({ table, projection, ...props });
|
|
111
|
+
return new TableModel({ id: table.id, space: undefined, view, projection, ...props });
|
|
112
112
|
};
|