@dxos/react-ui-table 0.8.4-main.e098934 → 0.8.4-main.e8ec1fe
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-63UUF4IG.mjs → chunk-2PFAPQL4.mjs} +7 -7
- package/dist/lib/browser/chunk-2PFAPQL4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-BBXDTWCX.mjs → chunk-GS2UNGJK.mjs} +443 -490
- package/dist/lib/browser/chunk-GS2UNGJK.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +161 -303
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +9 -7
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-K734DJB7.mjs → chunk-6POOFFYF.mjs} +7 -7
- package/dist/lib/node-esm/chunk-6POOFFYF.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-RNQDU7IQ.mjs → chunk-TRR3C5NX.mjs} +443 -490
- package/dist/lib/node-esm/chunk-TRR3C5NX.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +161 -303
- 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 +9 -7
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -3
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +70 -7
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +5 -2
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +5 -6
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -5
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -3
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
- package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +3 -3
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +0 -7
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +4 -4
- 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/model/table-sorting.d.ts +2 -2
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +3 -3
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +6 -6
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +5 -6
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +52 -51
- package/src/components/Table/DynamicTable.stories.tsx +11 -10
- package/src/components/Table/DynamicTable.tsx +4 -7
- package/src/components/Table/Relations.stories.tsx +127 -18
- package/src/components/Table/RowActionsMenu.tsx +21 -13
- package/src/components/Table/Table.stories.tsx +44 -43
- package/src/components/Table/Table.tsx +13 -9
- package/src/components/TableCellEditor/FormCellEditor.tsx +51 -41
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +19 -46
- package/src/components/TableCellEditor/TableCellEditor.tsx +7 -94
- package/src/components/TableCellEditor/index.ts +1 -0
- package/src/components/TableToolbar/TableToolbar.tsx +32 -22
- package/src/components/testing.ts +2 -2
- package/src/hooks/useAddRow.ts +1 -1
- package/src/hooks/useTableModel.ts +5 -5
- package/src/model/modal-controller.ts +0 -28
- package/src/model/table-model.test.ts +12 -7
- package/src/model/table-model.ts +12 -8
- package/src/model/table-presentation.test.ts +15 -18
- package/src/model/table-presentation.ts +4 -5
- package/src/model/table-sorting.ts +9 -9
- package/src/playwright/TableManager.ts +1 -1
- package/src/playwright/smoke.spec.ts +7 -5
- package/src/testing/useTestTableModel.ts +7 -5
- package/src/types/schema.ts +8 -8
- package/src/util/dynamic-table.ts +9 -11
- package/src/util/schema.ts +4 -4
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
- package/dist/lib/browser/chunk-BBXDTWCX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RNQDU7IQ.mjs.map +0 -7
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
- package/src/components/Table/CreateRefPanel.tsx +0 -103
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Completion } from '@codemirror/autocomplete';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
|
-
import
|
|
7
|
+
import type * as Schema from 'effect/Schema';
|
|
8
8
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
9
9
|
|
|
10
10
|
import { debounce } from '@dxos/async';
|
|
11
11
|
import type { Client } from '@dxos/client';
|
|
12
|
-
import { FormatEnum, TypeEnum } from '@dxos/echo
|
|
12
|
+
import { FormatEnum, TypeEnum } from '@dxos/echo/internal';
|
|
13
13
|
import { invariant } from '@dxos/invariant';
|
|
14
14
|
import { type DxGridAxis, type DxGridPosition } from '@dxos/lit-grid';
|
|
15
|
-
import { createMarkdownExtensions } from '@dxos/react-ui-editor';
|
|
16
15
|
import {
|
|
17
16
|
type EditorBlurHandler,
|
|
18
17
|
type EditorKeyEvent,
|
|
@@ -24,13 +23,12 @@ import {
|
|
|
24
23
|
parseCellIndex,
|
|
25
24
|
useGridContext,
|
|
26
25
|
} from '@dxos/react-ui-grid';
|
|
27
|
-
import { createLinks, tagPicker } from '@dxos/react-ui-tag-picker';
|
|
28
26
|
import { type FieldProjection } from '@dxos/schema';
|
|
29
27
|
|
|
30
28
|
import { type ModalController, type TableModel } from '../../model';
|
|
31
29
|
|
|
32
30
|
import { CellValidationMessage } from './CellValidationMessage';
|
|
33
|
-
import { FormCellEditor } from './FormCellEditor';
|
|
31
|
+
import { FormCellEditor, type OnCreateHandler } from './FormCellEditor';
|
|
34
32
|
|
|
35
33
|
/**
|
|
36
34
|
* Option to create new object/value.
|
|
@@ -40,8 +38,9 @@ export type QueryResult = Pick<Completion, 'label'> & { data: any };
|
|
|
40
38
|
export type TableCellEditorProps = {
|
|
41
39
|
model?: TableModel;
|
|
42
40
|
modals?: ModalController;
|
|
43
|
-
schema?: Schema.AnyNoContext;
|
|
41
|
+
schema?: Schema.Schema.AnyNoContext;
|
|
44
42
|
onFocus?: (axis?: DxGridAxis, delta?: -1 | 0 | 1, cell?: DxGridPosition) => void;
|
|
43
|
+
onCreate?: OnCreateHandler;
|
|
45
44
|
onSave?: () => void;
|
|
46
45
|
client?: Client;
|
|
47
46
|
};
|
|
@@ -55,6 +54,7 @@ export const TableValueEditor = ({
|
|
|
55
54
|
schema,
|
|
56
55
|
onFocus,
|
|
57
56
|
onSave,
|
|
57
|
+
onCreate,
|
|
58
58
|
client,
|
|
59
59
|
__gridScope,
|
|
60
60
|
}: GridScopedProps<TableCellEditorProps>) => {
|
|
@@ -85,6 +85,7 @@ export const TableValueEditor = ({
|
|
|
85
85
|
schema={schema}
|
|
86
86
|
__gridScope={__gridScope}
|
|
87
87
|
onSave={onSave}
|
|
88
|
+
onCreate={onCreate}
|
|
88
89
|
client={client}
|
|
89
90
|
modals={modals}
|
|
90
91
|
/>
|
|
@@ -213,48 +214,6 @@ export const TableCellEditor = ({
|
|
|
213
214
|
...(editing?.initialContent && { onNav: handleClose }),
|
|
214
215
|
}),
|
|
215
216
|
];
|
|
216
|
-
|
|
217
|
-
const format = fieldProjection.props.format;
|
|
218
|
-
if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
|
|
219
|
-
// TODO(ZaymonFC): Reconcile this with the TagPicker component?
|
|
220
|
-
const options = fieldProjection.props.options || [];
|
|
221
|
-
const mode = format === FormatEnum.SingleSelect ? ('single-select' as const) : ('multi-select' as const);
|
|
222
|
-
|
|
223
|
-
// Add markdown extensions needed by tag picker.
|
|
224
|
-
extensions.push(createMarkdownExtensions());
|
|
225
|
-
extensions.push(
|
|
226
|
-
tagPicker({
|
|
227
|
-
mode,
|
|
228
|
-
keymap: false,
|
|
229
|
-
onSearch: (text, selectedIds) => {
|
|
230
|
-
return options
|
|
231
|
-
.filter(
|
|
232
|
-
(option) =>
|
|
233
|
-
selectedIds.indexOf(option.id) === -1 &&
|
|
234
|
-
(text.length === 0 || option.title.toLowerCase().includes(text.toLowerCase())),
|
|
235
|
-
)
|
|
236
|
-
.map((option) => ({
|
|
237
|
-
id: option.id,
|
|
238
|
-
label: option.title,
|
|
239
|
-
hue: option.color as any,
|
|
240
|
-
}));
|
|
241
|
-
},
|
|
242
|
-
onUpdate: (ids) => {
|
|
243
|
-
if (model && editing) {
|
|
244
|
-
if (ids.length === 0) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
if (mode === 'single-select') {
|
|
248
|
-
void handleEnter(ids[0]);
|
|
249
|
-
} else {
|
|
250
|
-
void handleEnter(ids);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
}),
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
217
|
// Add validation extension to handle content changes.
|
|
259
218
|
if (model && editing) {
|
|
260
219
|
extensions.push(
|
|
@@ -283,52 +242,6 @@ export const TableCellEditor = ({
|
|
|
283
242
|
const getCellContent = useCallback<GridCellEditorProps['getCellContent']>(() => {
|
|
284
243
|
if (model && editing) {
|
|
285
244
|
const cell = parseCellIndex(editing.index);
|
|
286
|
-
const { col } = cell;
|
|
287
|
-
const field = model.projection.fields[col];
|
|
288
|
-
const fieldProjection = model.projection.getFieldProjection(field.id);
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
fieldProjection?.props.format === FormatEnum.SingleSelect ||
|
|
292
|
-
fieldProjection?.props.format === FormatEnum.MultiSelect
|
|
293
|
-
) {
|
|
294
|
-
const value = model.getCellData(cell);
|
|
295
|
-
if (value !== undefined) {
|
|
296
|
-
const options = fieldProjection.props.options || [];
|
|
297
|
-
if (fieldProjection.props.format === FormatEnum.MultiSelect) {
|
|
298
|
-
const tagItems = value
|
|
299
|
-
.split(',')
|
|
300
|
-
.map((id: string) => {
|
|
301
|
-
const option = options.find((o) => o.id === id);
|
|
302
|
-
if (option) {
|
|
303
|
-
return {
|
|
304
|
-
id,
|
|
305
|
-
label: option.title,
|
|
306
|
-
hue: option.color as any,
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
return undefined;
|
|
310
|
-
})
|
|
311
|
-
.filter((item: any): item is { id: any; label: string; hue: any } => item !== undefined);
|
|
312
|
-
|
|
313
|
-
return createLinks(tagItems);
|
|
314
|
-
} else {
|
|
315
|
-
const option = options.find((o) => o.id === value);
|
|
316
|
-
|
|
317
|
-
if (option) {
|
|
318
|
-
const tagItem = {
|
|
319
|
-
id: value,
|
|
320
|
-
label: option.title,
|
|
321
|
-
hue: option.color as any,
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
return createLinks([tagItem]);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return '';
|
|
330
|
-
}
|
|
331
|
-
|
|
332
245
|
const value = model.getCellData(cell);
|
|
333
246
|
return value !== undefined ? String(value) : '';
|
|
334
247
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
7
|
|
|
8
8
|
import { live } from '@dxos/live-object';
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
createMenuAction,
|
|
19
19
|
useMenuActions,
|
|
20
20
|
} from '@dxos/react-ui-menu';
|
|
21
|
-
import {
|
|
21
|
+
import { atomFromSignal } from '@dxos/react-ui-menu';
|
|
22
22
|
|
|
23
23
|
import { translationKey } from '../../translations';
|
|
24
24
|
|
|
@@ -30,12 +30,14 @@ export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
|
|
|
30
30
|
|
|
31
31
|
type TableToolbarState = Partial<{ viewDirty: boolean }>;
|
|
32
32
|
|
|
33
|
+
// TODO(burdon): Radix style layout.
|
|
34
|
+
|
|
33
35
|
export type TableToolbarProps = ThemedClassName<
|
|
34
36
|
TableToolbarState & {
|
|
35
37
|
onAdd: () => void;
|
|
36
38
|
onSave: () => void;
|
|
37
39
|
attendableId?: string;
|
|
38
|
-
customActions?:
|
|
40
|
+
customActions?: Atom.Atom<ActionGraphProps>;
|
|
39
41
|
}
|
|
40
42
|
>;
|
|
41
43
|
|
|
@@ -46,33 +48,41 @@ const createTableToolbarActions = ({
|
|
|
46
48
|
customActions,
|
|
47
49
|
}: {
|
|
48
50
|
state: TableToolbarState;
|
|
49
|
-
onAdd
|
|
50
|
-
onSave
|
|
51
|
-
customActions?:
|
|
51
|
+
onAdd?: () => void;
|
|
52
|
+
onSave?: () => void;
|
|
53
|
+
customActions?: Atom.Atom<ActionGraphProps>;
|
|
52
54
|
}) =>
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
Atom.make((get) => {
|
|
56
|
+
const nodes: ActionGraphNodes = [];
|
|
57
|
+
if (onAdd) {
|
|
58
|
+
const add = createMenuAction<TableToolbarActionProperties>('add-row', onAdd, {
|
|
59
|
+
type: 'add-row' as const,
|
|
60
|
+
icon: 'ph--plus--regular',
|
|
61
|
+
label: ['add row', { ns: translationKey }],
|
|
62
|
+
testId: 'table.toolbar.add-row',
|
|
63
|
+
});
|
|
64
|
+
nodes.push(add);
|
|
65
|
+
}
|
|
66
|
+
if (onSave) {
|
|
67
|
+
const save = createMenuAction<TableToolbarActionProperties>('save-view', onSave, {
|
|
68
|
+
type: 'save-view' as const,
|
|
69
|
+
icon: 'ph--floppy-disk--regular',
|
|
70
|
+
label: ['save view label', { ns: translationKey }],
|
|
71
|
+
testId: 'table.toolbar.save-view',
|
|
72
|
+
iconOnly: false,
|
|
73
|
+
hidden: get(atomFromSignal(() => !state.viewDirty)),
|
|
74
|
+
});
|
|
75
|
+
nodes.push(save);
|
|
76
|
+
}
|
|
68
77
|
const gap = createGapSeparator();
|
|
69
|
-
|
|
78
|
+
nodes.push(...gap.nodes);
|
|
70
79
|
const edges: ActionGraphEdges = nodes.map(({ id: target }) => ({ source: 'root', target }));
|
|
71
80
|
if (customActions) {
|
|
72
81
|
const custom = get(customActions);
|
|
73
82
|
nodes.push(...custom.nodes);
|
|
74
83
|
edges.push(...custom.edges);
|
|
75
84
|
}
|
|
85
|
+
|
|
76
86
|
return {
|
|
77
87
|
nodes,
|
|
78
88
|
edges,
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
7
|
|
|
8
|
-
import { FormatEnum, ObjectId, TypeEnum, TypedObject, setValue } from '@dxos/echo
|
|
8
|
+
import { FormatEnum, ObjectId, TypeEnum, TypedObject, setValue } from '@dxos/echo/internal';
|
|
9
9
|
import { live } from '@dxos/live-object';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
11
|
import { type ProjectionModel } from '@dxos/schema';
|
package/src/hooks/useAddRow.ts
CHANGED
|
@@ -6,17 +6,17 @@ import { effect } from '@preact/signals-core';
|
|
|
6
6
|
import orderBy from 'lodash.orderby';
|
|
7
7
|
import { useEffect, useMemo, useState } from 'react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { Obj } from '@dxos/echo';
|
|
10
|
+
import { type JsonSchemaType } from '@dxos/echo/internal';
|
|
10
11
|
import { type Live } from '@dxos/live-object';
|
|
11
|
-
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
12
12
|
import { useSelected, useSelectionActions } from '@dxos/react-ui-attention';
|
|
13
|
-
import { type
|
|
13
|
+
import { type ProjectionModel, type View } from '@dxos/schema';
|
|
14
14
|
import { isNonNullable } from '@dxos/util';
|
|
15
15
|
|
|
16
16
|
import { TableModel, type TableModelProps, type TableRow, type TableRowAction } from '../model';
|
|
17
17
|
|
|
18
18
|
export type UseTableModelParams<T extends TableRow = TableRow> = {
|
|
19
|
-
view?:
|
|
19
|
+
view?: View.View;
|
|
20
20
|
schema?: JsonSchemaType;
|
|
21
21
|
projection?: ProjectionModel;
|
|
22
22
|
rows?: Live<T>[];
|
|
@@ -39,7 +39,7 @@ export const useTableModel = <T extends TableRow = TableRow>({
|
|
|
39
39
|
onRowAction,
|
|
40
40
|
...props
|
|
41
41
|
}: UseTableModelParams<T>): TableModel<T> | undefined => {
|
|
42
|
-
const selected = useSelected(view &&
|
|
42
|
+
const selected = useSelected(view && Obj.getDXN(view).toString(), 'multi');
|
|
43
43
|
const initialSelection = useMemo(() => selected, [view]);
|
|
44
44
|
|
|
45
45
|
const [model, setModel] = useState<TableModel<T>>();
|
|
@@ -5,21 +5,12 @@
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
6
|
import { type MutableRefObject, type RefObject } from 'react';
|
|
7
7
|
|
|
8
|
-
import { type Live } from '@dxos/live-object';
|
|
9
|
-
import { log } from '@dxos/log';
|
|
10
|
-
|
|
11
8
|
export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
|
|
12
9
|
|
|
13
10
|
export type ModalState =
|
|
14
11
|
| { type: 'row'; rowIndex: number }
|
|
15
12
|
| { type: 'column'; fieldId: string }
|
|
16
13
|
| { type: 'refPanel'; targetId: string; typename: string }
|
|
17
|
-
| {
|
|
18
|
-
type: 'createRefPanel';
|
|
19
|
-
typename: string;
|
|
20
|
-
initialValues?: Record<string, string>;
|
|
21
|
-
onCreate?: (obj: Live<any>) => void;
|
|
22
|
-
}
|
|
23
14
|
| { type: 'columnSettings'; mode: ColumnSettingsMode }
|
|
24
15
|
| { type: 'closed' };
|
|
25
16
|
|
|
@@ -68,25 +59,6 @@ export class ModalController {
|
|
|
68
59
|
}
|
|
69
60
|
};
|
|
70
61
|
|
|
71
|
-
public openCreateRef = (
|
|
72
|
-
typename: string,
|
|
73
|
-
anchorCell: Element | null,
|
|
74
|
-
initialValues?: Record<string, string>,
|
|
75
|
-
onCreate?: (obj: Live<any>) => void,
|
|
76
|
-
) => {
|
|
77
|
-
if (anchorCell) {
|
|
78
|
-
this._triggerRef.current = anchorCell as HTMLElement;
|
|
79
|
-
this._state.value = {
|
|
80
|
-
type: 'createRefPanel',
|
|
81
|
-
typename,
|
|
82
|
-
initialValues,
|
|
83
|
-
onCreate,
|
|
84
|
-
};
|
|
85
|
-
} else {
|
|
86
|
-
log.warn('anchor cell not found while creating new ref');
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
62
|
public close = () => {
|
|
91
63
|
this._state.value = { type: 'closed' };
|
|
92
64
|
};
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { computed } from '@preact/signals-core';
|
|
6
|
-
import
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
9
|
import { Filter, Query } from '@dxos/echo';
|
|
10
|
-
import { TypedObject } from '@dxos/echo
|
|
11
|
-
import { updateCounter } from '@dxos/echo
|
|
10
|
+
import { TypedObject } from '@dxos/echo/internal';
|
|
11
|
+
import { updateCounter } from '@dxos/echo/testing';
|
|
12
|
+
import { createEchoSchema } from '@dxos/echo/testing';
|
|
12
13
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
13
14
|
import { live } from '@dxos/live-object';
|
|
14
|
-
import {
|
|
15
|
-
import { createView } from '@dxos/schema';
|
|
15
|
+
import { View } from '@dxos/schema';
|
|
16
16
|
|
|
17
17
|
import { Table } from '../types';
|
|
18
18
|
|
|
@@ -25,7 +25,7 @@ registerSignalsRuntime();
|
|
|
25
25
|
|
|
26
26
|
describe('TableModel', () => {
|
|
27
27
|
let updateCount = 0;
|
|
28
|
-
let model:
|
|
28
|
+
let model: any;
|
|
29
29
|
|
|
30
30
|
beforeEach(async () => {
|
|
31
31
|
updateCount = 0;
|
|
@@ -33,6 +33,11 @@ describe('TableModel', () => {
|
|
|
33
33
|
onCellUpdate: () => updateCount++,
|
|
34
34
|
});
|
|
35
35
|
await model.open();
|
|
36
|
+
model.setRows([
|
|
37
|
+
{ id: '1', title: 'Test', completed: false },
|
|
38
|
+
{ id: '2', title: 'Test 2', completed: true },
|
|
39
|
+
{ id: '3', title: 'Test 3', completed: false },
|
|
40
|
+
]);
|
|
36
41
|
});
|
|
37
42
|
|
|
38
43
|
afterEach(async () => {
|
|
@@ -108,7 +113,7 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
108
113
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
109
114
|
const schema = createEchoSchema(Test);
|
|
110
115
|
const table = Table.make();
|
|
111
|
-
const view =
|
|
116
|
+
const view = View.make({
|
|
112
117
|
query: Query.select(Filter.type(schema)),
|
|
113
118
|
jsonSchema: schema.jsonSchema,
|
|
114
119
|
presentation: table,
|
package/src/model/table-model.ts
CHANGED
|
@@ -14,11 +14,10 @@ import {
|
|
|
14
14
|
getValue,
|
|
15
15
|
setValue,
|
|
16
16
|
toEffectSchema,
|
|
17
|
-
} from '@dxos/echo
|
|
17
|
+
} from '@dxos/echo/internal';
|
|
18
18
|
import { invariant } from '@dxos/invariant';
|
|
19
19
|
import { ObjectId } from '@dxos/keys';
|
|
20
20
|
import { getSnapshot, isLiveObject } from '@dxos/live-object';
|
|
21
|
-
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
22
21
|
import { type Label } from '@dxos/react-ui';
|
|
23
22
|
import { formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
24
23
|
import {
|
|
@@ -28,11 +27,11 @@ import {
|
|
|
28
27
|
type DxGridPosition,
|
|
29
28
|
} from '@dxos/react-ui-grid';
|
|
30
29
|
import {
|
|
31
|
-
type DataType,
|
|
32
30
|
type FieldSortType,
|
|
33
31
|
ProjectionModel,
|
|
34
32
|
type PropertyType,
|
|
35
33
|
type ValidationError,
|
|
34
|
+
type View,
|
|
36
35
|
validateSchema,
|
|
37
36
|
} from '@dxos/schema';
|
|
38
37
|
|
|
@@ -78,7 +77,7 @@ const defaultFeatures: TableFeatures = {
|
|
|
78
77
|
export type InsertRowResult = 'draft' | 'final';
|
|
79
78
|
|
|
80
79
|
export type TableModelProps<T extends TableRow = TableRow> = {
|
|
81
|
-
view:
|
|
80
|
+
view: View.View;
|
|
82
81
|
schema: JsonSchemaType;
|
|
83
82
|
projection?: ProjectionModel;
|
|
84
83
|
features?: Partial<TableFeatures>;
|
|
@@ -96,7 +95,7 @@ export type TableModelProps<T extends TableRow = TableRow> = {
|
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
99
|
-
private readonly _view:
|
|
98
|
+
private readonly _view: View.View;
|
|
100
99
|
private readonly _projection: ProjectionModel;
|
|
101
100
|
private _table?: Table.Table;
|
|
102
101
|
|
|
@@ -141,6 +140,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
141
140
|
super();
|
|
142
141
|
this._view = view;
|
|
143
142
|
this._projection = projection ?? new ProjectionModel(schema, view.projection);
|
|
143
|
+
this._projection.normalizeView();
|
|
144
144
|
|
|
145
145
|
// TODO(ZaymonFC): Use our more robust config merging module?
|
|
146
146
|
this._features = { ...defaultFeatures, ...features };
|
|
@@ -175,10 +175,10 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
public get id(): string {
|
|
178
|
-
return
|
|
178
|
+
return Obj.getDXN(this._view).toString();
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
public get view():
|
|
181
|
+
public get view(): View.View {
|
|
182
182
|
return this._view;
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -479,7 +479,11 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
default: {
|
|
482
|
-
return formatForEditing({
|
|
482
|
+
return formatForEditing({
|
|
483
|
+
type: props.type,
|
|
484
|
+
format: props.format,
|
|
485
|
+
value,
|
|
486
|
+
});
|
|
483
487
|
}
|
|
484
488
|
}
|
|
485
489
|
};
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { Filter, Query } from '@dxos/echo';
|
|
9
|
-
import { TypedObject } from '@dxos/echo
|
|
9
|
+
import { TypedObject } from '@dxos/echo/internal';
|
|
10
|
+
import { createEchoSchema } from '@dxos/echo/testing';
|
|
10
11
|
import { live } from '@dxos/live-object';
|
|
11
|
-
import {
|
|
12
|
-
import { createView } from '@dxos/schema';
|
|
12
|
+
import { View } from '@dxos/schema';
|
|
13
13
|
|
|
14
14
|
import { Table } from '../types';
|
|
15
15
|
|
|
@@ -27,8 +27,8 @@ describe('TablePresentation', () => {
|
|
|
27
27
|
updateCount = 0;
|
|
28
28
|
({ data } = live({
|
|
29
29
|
data: [
|
|
30
|
-
{
|
|
31
|
-
{
|
|
30
|
+
{ title: 'A', count: 1 },
|
|
31
|
+
{ title: 'B', count: 2 },
|
|
32
32
|
],
|
|
33
33
|
}));
|
|
34
34
|
|
|
@@ -45,8 +45,8 @@ describe('TablePresentation', () => {
|
|
|
45
45
|
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
46
46
|
|
|
47
47
|
// Trigger a row update.
|
|
48
|
-
data[0].
|
|
49
|
-
expect(updateCount).toBe(
|
|
48
|
+
data[0].title = 'New Value';
|
|
49
|
+
expect(updateCount).toBe(2);
|
|
50
50
|
|
|
51
51
|
// Verify the new value through getCells.
|
|
52
52
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 0 } }, 'grid');
|
|
@@ -57,15 +57,14 @@ describe('TablePresentation', () => {
|
|
|
57
57
|
// Set up visible range to include our test data.
|
|
58
58
|
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
59
59
|
|
|
60
|
-
data.push({
|
|
61
|
-
expect(updateCount).toBe(
|
|
60
|
+
data.push({ title: 'C', count: 3 });
|
|
61
|
+
expect(updateCount).toBe(2);
|
|
62
62
|
|
|
63
63
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
64
64
|
|
|
65
65
|
// Verify the new row's data.
|
|
66
66
|
expect(cells['0,2']?.value).toBe('C');
|
|
67
67
|
expect(cells['1,2']?.value).toBe('3');
|
|
68
|
-
expect(cells['2,2']?.value).toBe('true');
|
|
69
68
|
});
|
|
70
69
|
|
|
71
70
|
it('should handle combined operations reactively', () => {
|
|
@@ -73,33 +72,31 @@ describe('TablePresentation', () => {
|
|
|
73
72
|
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
74
73
|
|
|
75
74
|
// Multiple operations.
|
|
76
|
-
data[0].
|
|
77
|
-
data.push({
|
|
75
|
+
data[0].title = 'Updated A';
|
|
76
|
+
data.push({ title: 'C', count: 3, completed: true });
|
|
78
77
|
data.splice(1, 1);
|
|
79
78
|
|
|
80
79
|
// We expect one update per operation affecting visible rows.
|
|
81
|
-
expect(updateCount).toBe(
|
|
80
|
+
expect(updateCount).toBe(4);
|
|
82
81
|
|
|
83
82
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
84
83
|
|
|
85
84
|
// Verify final state.
|
|
86
85
|
expect(cells['0,0']?.value).toBe('Updated A');
|
|
87
86
|
expect(cells['0,1']?.value).toBe('C');
|
|
88
|
-
expect(cells['1,1']?.value).toBe('3');
|
|
89
|
-
expect(cells['2,1']?.value).toBe('true');
|
|
90
87
|
});
|
|
91
88
|
});
|
|
92
89
|
});
|
|
93
90
|
|
|
94
91
|
class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
|
|
95
92
|
title: Schema.String,
|
|
96
|
-
|
|
93
|
+
count: Schema.Number,
|
|
97
94
|
}) {}
|
|
98
95
|
|
|
99
96
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
100
97
|
const schema = createEchoSchema(Test);
|
|
101
98
|
const table = Table.make();
|
|
102
|
-
const view =
|
|
99
|
+
const view = View.make({
|
|
103
100
|
query: Query.select(Filter.type(schema)),
|
|
104
101
|
jsonSchema: schema.jsonSchema,
|
|
105
102
|
presentation: table,
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
|
-
import
|
|
6
|
+
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo
|
|
9
|
+
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
10
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
11
|
import {
|
|
12
12
|
type DxGridCellValue,
|
|
@@ -190,7 +190,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
190
190
|
|
|
191
191
|
const tags = targetArray
|
|
192
192
|
.map(getLabel)
|
|
193
|
-
.filter(isNotNullable)
|
|
193
|
+
.filter(Predicate.isNotNullable)
|
|
194
194
|
.map((title) => {
|
|
195
195
|
return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
|
|
196
196
|
})
|
|
@@ -205,7 +205,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
205
205
|
const targetObj = getValue(obj, field.path)?.target;
|
|
206
206
|
if (targetObj) {
|
|
207
207
|
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
208
|
-
cell.accessoryHtml = `<dx-anchor refId=${dxn} class="dx-button is-6
|
|
208
|
+
cell.accessoryHtml = `<div role="none" class="absolute inline-end-0 inset-block-0 p-[--dx-grid-cell-content-padding-block]"><dx-anchor refId=${dxn} class="dx-button is-6 aspect-square min-bs-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -468,7 +468,6 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
468
468
|
|
|
469
469
|
switch (selectionMode) {
|
|
470
470
|
case 'single':
|
|
471
|
-
// TODO(ZaymonFC): @thure, do we need a grid version of 'currentRelated'?
|
|
472
471
|
return ['!bg-currentRelated dx-grid__cell--no-focus-unfurl hover:bg-hoverSurface !cursor-pointer'];
|
|
473
472
|
case 'multiple':
|
|
474
473
|
return ['!bg-gridCellSelected'];
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
import { type ReadonlySignal, type Signal, computed, signal } from '@preact/signals-core';
|
|
6
6
|
import orderBy from 'lodash.orderby';
|
|
7
7
|
|
|
8
|
-
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo
|
|
8
|
+
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
9
9
|
import { formatForDisplay } from '@dxos/react-ui-form';
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
import {
|
|
11
|
+
type FieldSortType,
|
|
12
|
+
type FieldType,
|
|
13
|
+
type ProjectionModel,
|
|
14
|
+
type PropertyType,
|
|
15
|
+
type SortDirectionType,
|
|
16
|
+
type View,
|
|
17
17
|
} from '@dxos/schema';
|
|
18
18
|
|
|
19
19
|
import { type TableRow } from './table-model';
|
|
@@ -58,7 +58,7 @@ export class TableSorting<T extends TableRow> {
|
|
|
58
58
|
|
|
59
59
|
constructor(
|
|
60
60
|
rows: Signal<T[]>,
|
|
61
|
-
private readonly _view:
|
|
61
|
+
private readonly _view: View.View,
|
|
62
62
|
private readonly _projection: ProjectionModel,
|
|
63
63
|
) {
|
|
64
64
|
this._rows = rows;
|
|
@@ -59,6 +59,6 @@ export class TableManager {
|
|
|
59
59
|
await this.page.getByLabel(`format ${format}`).click();
|
|
60
60
|
await this.page.getByPlaceholder('Property label.').click();
|
|
61
61
|
await this.page.getByPlaceholder('Property label.').fill(label);
|
|
62
|
-
await this.page.getByRole('button', { name: 'button
|
|
62
|
+
await this.page.getByRole('button', { name: 'save button' }).click();
|
|
63
63
|
}
|
|
64
64
|
}
|