@dxos/react-ui-table 0.8.2-main.f11618f → 0.8.2-staging.7ac8446
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/index.mjs +116 -80
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +127 -91
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +116 -80
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Table/RefPanel.d.ts +9 -0
- package/dist/types/src/components/Table/RefPanel.d.ts.map +1 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +2 -2
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +3 -3
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/types/table.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +14 -0
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/components/Table/CreateRefPanel.tsx +2 -2
- package/src/components/Table/DynamicTable.stories.tsx +2 -2
- package/src/components/Table/RefPanel.tsx +52 -0
- package/src/components/Table/Table.stories.tsx +9 -9
- package/src/components/Table/Table.tsx +24 -18
- package/src/components/Table/relations.stories.tsx +21 -30
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +3 -3
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +3 -3
- package/src/hooks/useTableModel.ts +2 -2
- package/src/model/modal-controller.ts +3 -3
- package/src/model/table-model.test.ts +3 -3
- package/src/model/table-model.ts +2 -19
- package/src/model/table-presentation.test.ts +3 -3
- package/src/model/table-presentation.ts +5 -3
- package/src/util/dynamic-table.ts +2 -2
- package/src/util/table-buttons.ts +23 -0
- package/src/util/table.ts +7 -3
- package/dist/types/src/util/tag.d.ts +0 -11
- package/dist/types/src/util/tag.d.ts.map +0 -1
- package/src/util/tag.ts +0 -33
|
@@ -12,7 +12,7 @@ import { invariant } from '@dxos/invariant';
|
|
|
12
12
|
import { useGlobalFilteredObjects } from '@dxos/plugin-search';
|
|
13
13
|
import { faker } from '@dxos/random';
|
|
14
14
|
import { useClient } from '@dxos/react-client';
|
|
15
|
-
import { Filter, useQuery, useSchema,
|
|
15
|
+
import { Filter, useQuery, useSchema, create } from '@dxos/react-client/echo';
|
|
16
16
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
17
17
|
import { ViewEditor } from '@dxos/react-ui-form';
|
|
18
18
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
@@ -73,7 +73,7 @@ const useTestTableModel = () => {
|
|
|
73
73
|
|
|
74
74
|
const handleInsertRow = useCallback(() => {
|
|
75
75
|
if (space && schema) {
|
|
76
|
-
space.db.add(
|
|
76
|
+
space.db.add(create(schema, {}));
|
|
77
77
|
}
|
|
78
78
|
}, [space, schema]);
|
|
79
79
|
|
|
@@ -222,11 +222,11 @@ const meta: Meta<StoryProps> = {
|
|
|
222
222
|
createIdentity: true,
|
|
223
223
|
createSpace: true,
|
|
224
224
|
onSpaceCreated: async ({ client, space }) => {
|
|
225
|
-
const table = space.db.add(
|
|
225
|
+
const table = space.db.add(create(TableType, {}));
|
|
226
226
|
const schema = await initializeTable({ client, space, table, initialRow: false });
|
|
227
227
|
Array.from({ length: 10 }).map(() => {
|
|
228
228
|
return space.db.add(
|
|
229
|
-
|
|
229
|
+
create(schema, {
|
|
230
230
|
name: faker.person.fullName(),
|
|
231
231
|
}),
|
|
232
232
|
);
|
|
@@ -247,17 +247,17 @@ export const StaticSchema: StoryObj = {
|
|
|
247
247
|
parameters: { translations },
|
|
248
248
|
decorators: [
|
|
249
249
|
withClientProvider({
|
|
250
|
-
types: [TableType, ViewType, Testing.Contact, Testing.
|
|
250
|
+
types: [TableType, ViewType, Testing.Contact, Testing.Org],
|
|
251
251
|
createIdentity: true,
|
|
252
252
|
createSpace: true,
|
|
253
253
|
onSpaceCreated: async ({ client, space }) => {
|
|
254
|
-
const table = space.db.add(
|
|
254
|
+
const table = space.db.add(create(TableType, {}));
|
|
255
255
|
await initializeTable({ client, space, table, typename: Testing.Contact.typename });
|
|
256
256
|
|
|
257
257
|
const factory = createObjectFactory(space.db, faker as any);
|
|
258
258
|
await factory([
|
|
259
259
|
{ type: Testing.Contact, count: 10 },
|
|
260
|
-
{ type: Testing.
|
|
260
|
+
{ type: Testing.Org, count: 1 },
|
|
261
261
|
]);
|
|
262
262
|
},
|
|
263
263
|
}),
|
|
@@ -303,13 +303,13 @@ export const Tags: Meta<StoryProps> = {
|
|
|
303
303
|
const [storedSchema] = await space.db.schemaRegistry.register([schema]);
|
|
304
304
|
|
|
305
305
|
// Initialize table.
|
|
306
|
-
const table = space.db.add(
|
|
306
|
+
const table = space.db.add(create(TableType, {}));
|
|
307
307
|
await initializeTable({ client, space, table, initialRow: false, typename });
|
|
308
308
|
|
|
309
309
|
// Populate.
|
|
310
310
|
Array.from({ length: 10 }).map(() => {
|
|
311
311
|
return space.db.add(
|
|
312
|
-
|
|
312
|
+
create(storedSchema, {
|
|
313
313
|
single: faker.helpers.arrayElement([...selectOptionIds, undefined]),
|
|
314
314
|
multiple: faker.helpers.randomSubset(selectOptionIds),
|
|
315
315
|
}),
|
|
@@ -19,29 +19,27 @@ import { invariant } from '@dxos/invariant';
|
|
|
19
19
|
import { Filter } from '@dxos/react-client/echo';
|
|
20
20
|
import { useTranslation } from '@dxos/react-ui';
|
|
21
21
|
import { useAttention } from '@dxos/react-ui-attention';
|
|
22
|
-
import {
|
|
23
|
-
closestCell,
|
|
24
|
-
type DxGridElement,
|
|
25
|
-
type DxGridPosition,
|
|
26
|
-
type GridContentProps,
|
|
27
|
-
Grid,
|
|
28
|
-
type DxGridPlane,
|
|
29
|
-
type DxGridPlaneRange,
|
|
30
|
-
gridSeparatorInlineEnd,
|
|
31
|
-
gridSeparatorBlockEnd,
|
|
32
|
-
} from '@dxos/react-ui-grid';
|
|
22
|
+
import { closestCell, type DxGridElement, type DxGridPosition, type GridContentProps, Grid } from '@dxos/react-ui-grid';
|
|
33
23
|
import { mx } from '@dxos/react-ui-theme';
|
|
34
24
|
import { isNotFalsy, safeParseInt } from '@dxos/util';
|
|
35
25
|
|
|
36
26
|
import { ColumnActionsMenu } from './ColumnActionsMenu';
|
|
37
27
|
import { ColumnSettings } from './ColumnSettings';
|
|
38
28
|
import { CreateRefPanel } from './CreateRefPanel';
|
|
29
|
+
import { RefPanel } from './RefPanel';
|
|
39
30
|
import { RowActionsMenu } from './RowActionsMenu';
|
|
40
31
|
import { ModalController, type TableModel, type TablePresentation } from '../../model';
|
|
41
32
|
import { translationKey } from '../../translations';
|
|
42
33
|
import { tableButtons, tableControls } from '../../util';
|
|
43
34
|
import { createOption, TableCellEditor, type TableCellEditorProps } from '../TableCellEditor';
|
|
44
35
|
|
|
36
|
+
// NOTE(Zan): These fragments add border to inline-end and block-end of the grid using pseudo-elements.
|
|
37
|
+
// These are offset by 1px to avoid double borders in planks.
|
|
38
|
+
const inlineEndLine =
|
|
39
|
+
'[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-block-0 [&>.dx-grid]:after:-inline-end-px [&>.dx-grid]:after:is-px [&>.dx-grid]:after:bg-separator';
|
|
40
|
+
const blockEndLine =
|
|
41
|
+
'[&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-inline-0 [&>.dx-grid]:before:-block-end-px [&>.dx-grid]:before:bs-px [&>.dx-grid]:before:bg-separator';
|
|
42
|
+
|
|
45
43
|
//
|
|
46
44
|
// Table.Root
|
|
47
45
|
//
|
|
@@ -100,17 +98,20 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
100
98
|
};
|
|
101
99
|
}, [model]);
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
// TODO(burdon): Replace useEffect below.
|
|
102
|
+
// const getCells = useCallback<GridContentProps['getCells']>(
|
|
103
|
+
// (range: DxGridRange, plane: DxGridPlane) => presentation?.getCells(range, plane) ?? {},
|
|
104
|
+
// [presentation],
|
|
105
|
+
// );
|
|
107
106
|
|
|
108
107
|
useEffect(() => {
|
|
109
108
|
if (!presentation || !dxGrid) {
|
|
110
109
|
return;
|
|
111
110
|
}
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
|
|
112
|
+
// TODO(burdon): Pass to Grid.Content?
|
|
113
|
+
dxGrid.getCells = (range, plane) => presentation.getCells(range, plane);
|
|
114
|
+
}, [presentation, dxGrid]);
|
|
114
115
|
|
|
115
116
|
/**
|
|
116
117
|
* Provides an external controller that can be called to repaint the table.
|
|
@@ -180,6 +181,10 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
180
181
|
modals.showColumnCreator();
|
|
181
182
|
break;
|
|
182
183
|
}
|
|
184
|
+
case 'referencedCell': {
|
|
185
|
+
modals.showReferencePanel(data.targetId, data.schemaId);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
183
188
|
case 'sort': {
|
|
184
189
|
model?.sorting?.toggleSort(data.fieldId);
|
|
185
190
|
break;
|
|
@@ -351,7 +356,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
351
356
|
onQuery={handleQuery}
|
|
352
357
|
/>
|
|
353
358
|
<Grid.Content
|
|
354
|
-
className={mx('[--dx-grid-base:var(--surface-bg)]',
|
|
359
|
+
className={mx('[--dx-grid-base:var(--surface-bg)]', inlineEndLine, blockEndLine)}
|
|
355
360
|
frozen={frozen}
|
|
356
361
|
// getCells={getCells}
|
|
357
362
|
columns={model.columnMeta.value}
|
|
@@ -368,6 +373,7 @@ const TableMain = forwardRef<TableController, TableMainProps>(
|
|
|
368
373
|
<ColumnActionsMenu model={model} modals={modals} />
|
|
369
374
|
<ColumnSettings model={model} modals={modals} onNewColumn={handleNewColumn} />
|
|
370
375
|
<CreateRefPanel model={model} modals={modals} />
|
|
376
|
+
<RefPanel model={model} modals={modals} />
|
|
371
377
|
</Grid.Root>
|
|
372
378
|
);
|
|
373
379
|
},
|
|
@@ -10,15 +10,15 @@ import React, { useEffect, useMemo } from 'react';
|
|
|
10
10
|
import { AST, type BaseObject, ImmutableSchema, type BaseSchema, type HasId } from '@dxos/echo-schema';
|
|
11
11
|
import { getAnnotation } from '@dxos/effect';
|
|
12
12
|
import { faker } from '@dxos/random';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
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
17
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
18
18
|
|
|
19
19
|
import { Table } from './Table';
|
|
20
20
|
import { useTableModel } from '../../hooks';
|
|
21
|
-
import {
|
|
21
|
+
import { TablePresentation } from '../../model';
|
|
22
22
|
import translations from '../../translations';
|
|
23
23
|
import { TableType } from '../../types';
|
|
24
24
|
|
|
@@ -30,14 +30,12 @@ const generator: ValueGenerator = faker as any;
|
|
|
30
30
|
// TODO(burdon): Reconcile schemas types and utils (see API PR).
|
|
31
31
|
// TODO(burdon): Base type for T (with id); see ECHO API PR?
|
|
32
32
|
const useTestModel = <T extends BaseObject & HasId>(schema: BaseSchema<T>, count: number) => {
|
|
33
|
-
const { space } = useClientProvider();
|
|
34
|
-
|
|
35
33
|
const table = useMemo(() => {
|
|
36
34
|
// const { typename } = pipe(schema.ast, AST.getAnnotation<TypeAnnotation>(TypeAnnotationId), Option.getOrThrow);
|
|
37
35
|
const typename = schema.typename;
|
|
38
36
|
const name = getAnnotation<string>(AST.TitleAnnotationId)(schema.ast) ?? typename;
|
|
39
37
|
const view = createView({ name, typename, jsonSchema: schema.jsonSchema });
|
|
40
|
-
return
|
|
38
|
+
return create(TableType, { view: makeRef(view) });
|
|
41
39
|
}, [schema]);
|
|
42
40
|
|
|
43
41
|
const projection = useMemo(() => {
|
|
@@ -49,24 +47,16 @@ const useTestModel = <T extends BaseObject & HasId>(schema: BaseSchema<T>, count
|
|
|
49
47
|
return new ViewProjection(schema.jsonSchema, table.view.target);
|
|
50
48
|
}, [schema, table]);
|
|
51
49
|
|
|
52
|
-
const
|
|
53
|
-
() => ({ schemaEditable: false, dataEditable: true, selection: { enabled: false } }),
|
|
54
|
-
[],
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const model = useTableModel<TableRow>({ table, projection, rows: [], features });
|
|
50
|
+
const model = useTableModel({ table, projection, rows: [] });
|
|
58
51
|
useEffect(() => {
|
|
59
|
-
if (!model
|
|
52
|
+
if (!model) {
|
|
60
53
|
return;
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
const objectGenerator =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}, [model, space]);
|
|
68
|
-
|
|
69
|
-
console.log(projection?.getFieldProjections());
|
|
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]);
|
|
70
60
|
|
|
71
61
|
const presentation = useMemo(() => {
|
|
72
62
|
if (!model) {
|
|
@@ -81,20 +71,21 @@ const useTestModel = <T extends BaseObject & HasId>(schema: BaseSchema<T>, count
|
|
|
81
71
|
|
|
82
72
|
const DefaultStory = () => {
|
|
83
73
|
// TODO(burdon): Remove need for ImmutableSchema wrapper at API-level.
|
|
84
|
-
const orgSchema = useMemo(() => new ImmutableSchema(
|
|
85
|
-
const { model: orgModel, presentation: orgPresentation } = useTestModel<
|
|
74
|
+
const orgSchema = useMemo(() => new ImmutableSchema(Testing.Org), []);
|
|
75
|
+
const { model: orgModel, presentation: orgPresentation } = useTestModel<Testing.Org>(orgSchema, 50);
|
|
86
76
|
|
|
87
77
|
// TODO(burdon): Generate links with references.
|
|
88
|
-
const contactSchema = useMemo(() => new ImmutableSchema(Contact), []);
|
|
89
|
-
const { model: contactModel, presentation: contactPresentation } = useTestModel<Contact>(contactSchema, 50);
|
|
78
|
+
const contactSchema = useMemo(() => new ImmutableSchema(Testing.Contact), []);
|
|
79
|
+
const { model: contactModel, presentation: contactPresentation } = useTestModel<Testing.Contact>(contactSchema, 50);
|
|
90
80
|
|
|
81
|
+
// TODO(burdon): Scrolling isn't working.
|
|
91
82
|
return (
|
|
92
|
-
<div className='
|
|
83
|
+
<div className='grow grid grid-cols-2 divide-x divide-separator'>
|
|
93
84
|
<Table.Root>
|
|
94
|
-
<Table.Main model={orgModel} presentation={orgPresentation}
|
|
85
|
+
<Table.Main model={orgModel} presentation={orgPresentation} />
|
|
95
86
|
</Table.Root>
|
|
96
87
|
<Table.Root>
|
|
97
|
-
<Table.Main model={contactModel} presentation={contactPresentation}
|
|
88
|
+
<Table.Main model={contactModel} presentation={contactPresentation} />
|
|
98
89
|
</Table.Root>
|
|
99
90
|
</div>
|
|
100
91
|
);
|
|
@@ -106,7 +97,7 @@ const meta: Meta<typeof DefaultStory> = {
|
|
|
106
97
|
parameters: { translations },
|
|
107
98
|
decorators: [
|
|
108
99
|
withClientProvider({
|
|
109
|
-
types: [TableType, ViewType,
|
|
100
|
+
types: [TableType, ViewType, Testing.Org, Testing.Contact],
|
|
110
101
|
createIdentity: true,
|
|
111
102
|
createSpace: true,
|
|
112
103
|
}),
|
|
@@ -10,7 +10,7 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|
|
10
10
|
import { ImmutableSchema, type EchoSchema } from '@dxos/echo-schema';
|
|
11
11
|
import { invariant } from '@dxos/invariant';
|
|
12
12
|
import { faker } from '@dxos/random';
|
|
13
|
-
import { Filter, useQuery,
|
|
13
|
+
import { Filter, useQuery, create } from '@dxos/react-client/echo';
|
|
14
14
|
import { useClientProvider, withClientProvider } from '@dxos/react-client/testing';
|
|
15
15
|
import { defaultSizeRow, Grid, type GridEditing } from '@dxos/react-ui-grid';
|
|
16
16
|
import { ViewProjection, ViewType } from '@dxos/schema';
|
|
@@ -95,11 +95,11 @@ const meta: Meta<StoryProps> = {
|
|
|
95
95
|
createIdentity: true,
|
|
96
96
|
createSpace: true,
|
|
97
97
|
onSpaceCreated: async ({ client, space }) => {
|
|
98
|
-
const table = space.db.add(
|
|
98
|
+
const table = space.db.add(create(TableType, {}));
|
|
99
99
|
const schema = await initializeTable({ client, space, table });
|
|
100
100
|
Array.from({ length: 10 }).forEach(() => {
|
|
101
101
|
space.db.add(
|
|
102
|
-
|
|
102
|
+
create(schema, {
|
|
103
103
|
name: faker.person.fullName(),
|
|
104
104
|
}),
|
|
105
105
|
);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useCallback, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { create } from '@dxos/live-object';
|
|
8
8
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
9
9
|
import {
|
|
10
10
|
createGapSeparator,
|
|
@@ -60,7 +60,7 @@ const createTableToolbarActions = (state: TableToolbarState) => {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
export const TableToolbar = ({ classNames, viewDirty, attendableId, onAction }: TableToolbarProps) => {
|
|
63
|
-
const state = useMemo(() =>
|
|
63
|
+
const state = useMemo(() => create<TableToolbarState>({ viewDirty }), []);
|
|
64
64
|
const actionsCreator = useCallback(() => createTableToolbarActions(state), [state]);
|
|
65
65
|
const menu = useMenuActions(actionsCreator);
|
|
66
66
|
|
|
@@ -6,7 +6,7 @@ import { useEffect } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { setValue, toJsonSchema, S, TypeEnum, TypedObject, FormatEnum } from '@dxos/echo-schema';
|
|
8
8
|
import { faker } from '@dxos/random';
|
|
9
|
-
import {
|
|
9
|
+
import { create, makeRef } from '@dxos/react-client/echo';
|
|
10
10
|
import { createView, type ViewProjection } from '@dxos/schema';
|
|
11
11
|
|
|
12
12
|
import { TableType } from '../types';
|
|
@@ -20,7 +20,7 @@ export const TestSchema = TypedObject({ typename: 'example.com/type/Test', versi
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
export const createTable = (schema = TestSchema) => {
|
|
23
|
-
return
|
|
23
|
+
return create(TableType, {
|
|
24
24
|
view: makeRef(
|
|
25
25
|
createView({
|
|
26
26
|
name: 'Test',
|
|
@@ -32,7 +32,7 @@ export const createTable = (schema = TestSchema) => {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export const createItems = (n: number) => {
|
|
35
|
-
const { data } =
|
|
35
|
+
const { data } = create({
|
|
36
36
|
data: Array.from({ length: n }, () => ({
|
|
37
37
|
name: faker.person.fullName(),
|
|
38
38
|
age: faker.number.int({ min: 20, max: 70 }),
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { effect } from '@preact/signals-core';
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { fullyQualifiedId, getSpace, type
|
|
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';
|
|
@@ -16,7 +16,7 @@ import { type TableType } from '../types';
|
|
|
16
16
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
17
17
|
table?: TableType;
|
|
18
18
|
projection?: ViewProjection;
|
|
19
|
-
rows?:
|
|
19
|
+
rows?: ReactiveObject<T>[];
|
|
20
20
|
rowActions?: TableRowAction[];
|
|
21
21
|
onSelectionChanged?: (selection: string[]) => void;
|
|
22
22
|
onRowAction?: (actionId: string, data: T) => void;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
6
|
import { type RefObject, type MutableRefObject } from 'react';
|
|
7
7
|
|
|
8
|
-
import { type
|
|
8
|
+
import { type ReactiveObject } from '@dxos/live-object';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
|
|
11
11
|
export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
|
|
@@ -18,7 +18,7 @@ export type ModalState =
|
|
|
18
18
|
type: 'createRefPanel';
|
|
19
19
|
typename: string;
|
|
20
20
|
initialValues?: Record<string, string>;
|
|
21
|
-
onCreate?: (obj:
|
|
21
|
+
onCreate?: (obj: ReactiveObject<any>) => void;
|
|
22
22
|
}
|
|
23
23
|
| { type: 'columnSettings'; mode: ColumnSettingsMode }
|
|
24
24
|
| { type: 'closed' };
|
|
@@ -72,7 +72,7 @@ export class ModalController {
|
|
|
72
72
|
typename: string,
|
|
73
73
|
anchorCell: Element | null,
|
|
74
74
|
initialValues?: Record<string, string>,
|
|
75
|
-
onCreate?: (obj:
|
|
75
|
+
onCreate?: (obj: ReactiveObject<any>) => void,
|
|
76
76
|
) => {
|
|
77
77
|
if (anchorCell) {
|
|
78
78
|
this._triggerRef.current = anchorCell as HTMLElement;
|
|
@@ -9,7 +9,7 @@ import { S, TypedObject } from '@dxos/echo-schema';
|
|
|
9
9
|
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
10
10
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
11
11
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
12
|
-
import {
|
|
12
|
+
import { create, makeRef } from '@dxos/react-client/echo';
|
|
13
13
|
import { createView, ViewProjection } from '@dxos/schema';
|
|
14
14
|
|
|
15
15
|
import { TableModel, type TableModelProps } from './table-model';
|
|
@@ -69,7 +69,7 @@ describe('TableModel', () => {
|
|
|
69
69
|
|
|
70
70
|
describe('reactivity', () => {
|
|
71
71
|
it('pure signals should nest', () => {
|
|
72
|
-
const signal$ =
|
|
72
|
+
const signal$ = create({ arr: [{ thingInside: 1 }, { thingInside: 2 }] });
|
|
73
73
|
|
|
74
74
|
const computed$ = computed(() => {
|
|
75
75
|
return signal$.arr.map((row) =>
|
|
@@ -107,6 +107,6 @@ 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
109
|
const projection = new ViewProjection(schema.jsonSchema, view);
|
|
110
|
-
const table =
|
|
110
|
+
const table = create(TableType, { view: makeRef(view) });
|
|
111
111
|
return new TableModel({ id: table.id, space: undefined, view, projection, ...props });
|
|
112
112
|
};
|
package/src/model/table-model.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type Space } from '@dxos/client/echo';
|
|
|
8
8
|
import { Resource } from '@dxos/context';
|
|
9
9
|
import { type FieldSortType, FormatEnum, getValue, setValue, type JsonProp } from '@dxos/echo-schema';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
|
-
import {
|
|
11
|
+
import { isReactiveObject, makeRef } from '@dxos/live-object';
|
|
12
12
|
import { formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
13
13
|
import {
|
|
14
14
|
type DxGridAxisMeta,
|
|
@@ -21,7 +21,6 @@ import { type ViewType, type ViewProjection } from '@dxos/schema';
|
|
|
21
21
|
import { type SelectionMode, SelectionModel } from './selection-model';
|
|
22
22
|
import { TableSorting } from './table-sorting';
|
|
23
23
|
import { touch } from '../util';
|
|
24
|
-
import { extractTagIds } from '../util/tag';
|
|
25
24
|
|
|
26
25
|
// TODO(burdon): Use schema types.
|
|
27
26
|
export type TableRow = Record<JsonProp, any> & { id: string };
|
|
@@ -343,28 +342,12 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
343
342
|
case FormatEnum.Ref: {
|
|
344
343
|
// TODO(ZaymonFC): This get's called an additional time by the cell editor onBlur, but with the cell editors
|
|
345
344
|
// plain string value. Maybe onBlur should be called with the actual value?
|
|
346
|
-
if (
|
|
345
|
+
if (isReactiveObject(value)) {
|
|
347
346
|
setValue(this._rows.value[rowIdx], field.path, makeRef(value));
|
|
348
347
|
}
|
|
349
348
|
break;
|
|
350
349
|
}
|
|
351
350
|
|
|
352
|
-
case FormatEnum.SingleSelect: {
|
|
353
|
-
const ids = extractTagIds(value);
|
|
354
|
-
if (ids && ids.length > 0) {
|
|
355
|
-
setValue(this._rows.value[rowIdx], field.path, ids[0]);
|
|
356
|
-
}
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
case FormatEnum.MultiSelect: {
|
|
361
|
-
const ids = extractTagIds(value);
|
|
362
|
-
if (ids) {
|
|
363
|
-
setValue(this._rows.value[rowIdx], field.path, ids);
|
|
364
|
-
}
|
|
365
|
-
break;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
351
|
default: {
|
|
369
352
|
setValue(
|
|
370
353
|
this._rows.value[rowIdx],
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { describe, expect, it, beforeEach } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { S, TypedObject } from '@dxos/echo-schema';
|
|
8
|
-
import {
|
|
8
|
+
import { create, makeRef } from '@dxos/live-object';
|
|
9
9
|
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
10
10
|
import { createView, ViewProjection } from '@dxos/schema';
|
|
11
11
|
|
|
@@ -22,7 +22,7 @@ describe('TablePresentation', () => {
|
|
|
22
22
|
|
|
23
23
|
beforeEach(async () => {
|
|
24
24
|
updateCount = 0;
|
|
25
|
-
({ data } =
|
|
25
|
+
({ data } = create({
|
|
26
26
|
data: [
|
|
27
27
|
{ col1: 'A', col2: 1, col3: true },
|
|
28
28
|
{ col1: 'B', col2: 2, col3: false },
|
|
@@ -97,6 +97,6 @@ const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
|
97
97
|
const schema = createEchoSchema(Test);
|
|
98
98
|
const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
|
|
99
99
|
const projection = new ViewProjection(schema.jsonSchema, view);
|
|
100
|
-
const table =
|
|
100
|
+
const table = create(TableType, { view: makeRef(view) });
|
|
101
101
|
return new TableModel({ id: table.id, view, projection, ...props });
|
|
102
102
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
6
|
|
|
7
|
-
import { FormatEnum,
|
|
7
|
+
import { FormatEnum, getValue } from '@dxos/echo-schema';
|
|
8
8
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
9
9
|
import {
|
|
10
10
|
type DxGridPlane,
|
|
@@ -124,8 +124,10 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
124
124
|
if (props.format === FormatEnum.Ref && props.referenceSchema) {
|
|
125
125
|
const targetObj = getValue(obj, field.path)?.target;
|
|
126
126
|
if (targetObj) {
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
cell.accessoryHtml = tableButtons.referencedCell.render({
|
|
128
|
+
targetId: targetObj.id as any as string,
|
|
129
|
+
schemaId: props.referenceSchema,
|
|
130
|
+
});
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import type { BaseSchema, JsonSchemaType, SortDirectionType } from '@dxos/echo-schema';
|
|
6
|
-
import {
|
|
6
|
+
import { create, makeRef } from '@dxos/live-object';
|
|
7
7
|
import {
|
|
8
8
|
createView,
|
|
9
9
|
getSchemaFromPropertyDefinitions,
|
|
@@ -66,7 +66,7 @@ export const makeDynamicTable = ({
|
|
|
66
66
|
...(properties && { fields: properties.map((property) => property.name) }),
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
const table =
|
|
69
|
+
const table = create(TableType, { name: 'dynamic-table', view: makeRef(view) });
|
|
70
70
|
const projection = new ViewProjection(jsonSchema, view);
|
|
71
71
|
if (properties && view.fields) {
|
|
72
72
|
setProperties(view, projection, properties);
|
|
@@ -15,6 +15,7 @@ export const BUTTON_IDENTIFIERS: { [K in TableButton]: string } = {
|
|
|
15
15
|
type ButtonData =
|
|
16
16
|
| { type: 'columnSettings'; fieldId: string }
|
|
17
17
|
| { type: 'newColumn'; disabled?: boolean }
|
|
18
|
+
| { type: 'referencedCell'; schemaId: string; targetId: string }
|
|
18
19
|
| { type: 'rowMenu'; rowIndex: number }
|
|
19
20
|
| { type: 'sort'; fieldId: string; direction?: 'asc' | 'desc' };
|
|
20
21
|
|
|
@@ -75,6 +76,27 @@ const columnSettingsButton = {
|
|
|
75
76
|
}),
|
|
76
77
|
} as const;
|
|
77
78
|
|
|
79
|
+
const referencedCellButton = {
|
|
80
|
+
attr: BUTTON_IDENTIFIERS.referencedCell,
|
|
81
|
+
icon: 'ph--link-simple-horizontal--regular',
|
|
82
|
+
render: ({ targetId, schemaId }: Omit<Extract<ButtonData, { type: 'referencedCell' }>, 'type'>) => {
|
|
83
|
+
return createButton({
|
|
84
|
+
attr: BUTTON_IDENTIFIERS.referencedCell,
|
|
85
|
+
icon: referencedCellButton.icon,
|
|
86
|
+
data: {
|
|
87
|
+
'data-target-id': targetId,
|
|
88
|
+
'data-schema-id': schemaId,
|
|
89
|
+
},
|
|
90
|
+
testId: 'table-ref-cell-button',
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
getData: (el: HTMLElement): Extract<ButtonData, { type: 'referencedCell' }> => ({
|
|
94
|
+
type: 'referencedCell',
|
|
95
|
+
targetId: el.getAttribute('data-target-id')!,
|
|
96
|
+
schemaId: el.getAttribute('data-schema-id')!,
|
|
97
|
+
}),
|
|
98
|
+
} as const;
|
|
99
|
+
|
|
78
100
|
const rowMenuButton = {
|
|
79
101
|
attr: BUTTON_IDENTIFIERS.rowMenu,
|
|
80
102
|
icon: 'ph--dots-three--regular',
|
|
@@ -121,6 +143,7 @@ const sortButton = {
|
|
|
121
143
|
export const tableButtons = {
|
|
122
144
|
addColumn: addColumnButton,
|
|
123
145
|
columnSettings: columnSettingsButton,
|
|
146
|
+
referencedCell: referencedCellButton,
|
|
124
147
|
rowMenu: rowMenuButton,
|
|
125
148
|
sort: sortButton,
|
|
126
149
|
} as const;
|
package/src/util/table.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
TypeEnum,
|
|
15
15
|
} from '@dxos/echo-schema';
|
|
16
16
|
import { type Client, PublicKey } from '@dxos/react-client';
|
|
17
|
-
import {
|
|
17
|
+
import { create, makeRef, type Space } from '@dxos/react-client/echo';
|
|
18
18
|
import { createFieldId, createView, getSchemaProperties, ViewProjection, type ViewType } from '@dxos/schema';
|
|
19
19
|
|
|
20
20
|
import { type TableType } from '../types';
|
|
@@ -41,7 +41,11 @@ export const initializeTable = async ({
|
|
|
41
41
|
throw new Error(`Schema not found: ${typename}`);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// We need to get the schema properties here.
|
|
45
|
+
// For now, only simple types and refs, not compound types are going to be supported.
|
|
46
|
+
const fields = getSchemaProperties(schema.ast)
|
|
47
|
+
.filter((prop) => prop.format !== FormatEnum.Ref)
|
|
48
|
+
.map((prop) => prop.name);
|
|
45
49
|
|
|
46
50
|
table.view = makeRef(
|
|
47
51
|
createView({
|
|
@@ -71,7 +75,7 @@ export const initializeTable = async ({
|
|
|
71
75
|
|
|
72
76
|
if (initialRow) {
|
|
73
77
|
// TODO(burdon): Last (first) row should not be in db and should be managed by the model.
|
|
74
|
-
space.db.add(
|
|
78
|
+
space.db.add(create(schema, {}));
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
return schema;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Regex pattern for matching tags in the format [Label](id)
|
|
3
|
-
*/
|
|
4
|
-
export declare const TAG_PATTERN_REGEX: RegExp;
|
|
5
|
-
/**
|
|
6
|
-
* Extracts tag IDs from a string in the format [Label](id)
|
|
7
|
-
* @param value The string to extract IDs from
|
|
8
|
-
* @returns Array of extracted IDs or undefined if no matches or value is not a string
|
|
9
|
-
*/
|
|
10
|
-
export declare const extractTagIds: (value: unknown) => string[] | undefined;
|
|
11
|
-
//# sourceMappingURL=tag.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../../src/util/tag.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAA6B,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,aAAa,UAAW,OAAO,KAAG,MAAM,EAAE,GAAG,SAgBzD,CAAC"}
|
package/src/util/tag.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// TODO(ZaymonFC): Push this into the tag picker module.
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Regex pattern for matching tags in the format [Label](id)
|
|
9
|
-
*/
|
|
10
|
-
export const TAG_PATTERN_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Extracts tag IDs from a string in the format [Label](id)
|
|
14
|
-
* @param value The string to extract IDs from
|
|
15
|
-
* @returns Array of extracted IDs or undefined if no matches or value is not a string
|
|
16
|
-
*/
|
|
17
|
-
export const extractTagIds = (value: unknown): string[] | undefined => {
|
|
18
|
-
if (typeof value !== 'string') {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const ids: string[] = [];
|
|
23
|
-
let match;
|
|
24
|
-
|
|
25
|
-
// Use the global regex to find all matches
|
|
26
|
-
while ((match = TAG_PATTERN_REGEX.exec(value)) !== null) {
|
|
27
|
-
if (match[2]) {
|
|
28
|
-
ids.push(match[2]);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return ids.length > 0 ? ids : undefined;
|
|
33
|
-
};
|