@dxos/react-ui-table 0.8.4-main.c1de068 → 0.8.4-main.c351d160a8
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-BF54SSNL.mjs +70 -0
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/chunk-PTHMYAAM.mjs +1751 -0
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1188 -1139
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +31 -38
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs +1753 -0
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs +71 -0
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1188 -1139
- 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 +31 -38
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +2 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +3 -3
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +37 -3
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +111 -0
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +16 -12
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +108 -4
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +13 -9
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +11 -18
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +39 -4
- 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 +5 -5
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +23 -16
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +6 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +6 -0
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +10 -10
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -8
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +17 -8
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +112 -30
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +5 -2
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +18 -34
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +4 -4
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +26 -0
- package/dist/types/src/types/Table.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +14 -12
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/index.d.ts +2 -1
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +61 -56
- package/src/components/Table/ColumnActionsMenu.tsx +11 -7
- package/src/components/Table/ColumnSettings.tsx +9 -10
- package/src/components/Table/DynamicTable.stories.tsx +28 -24
- package/src/components/Table/DynamicTable.tsx +34 -28
- package/src/components/Table/Relations.stories.tsx +240 -0
- package/src/components/Table/RowActionsMenu.tsx +26 -17
- package/src/components/Table/Table.stories.tsx +148 -118
- package/src/components/Table/Table.tsx +351 -296
- package/src/components/TableCellEditor/CellValidationMessage.tsx +6 -6
- package/src/components/TableCellEditor/FormCellEditor.tsx +134 -40
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +31 -66
- package/src/components/TableCellEditor/TableCellEditor.tsx +88 -238
- package/src/components/TableCellEditor/index.ts +1 -0
- package/src/components/TableToolbar/TableToolbar.tsx +54 -38
- package/src/components/testing.ts +39 -13
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAddRow.ts +14 -14
- package/src/hooks/useProjectionModel.ts +39 -0
- package/src/hooks/useTableModel.ts +36 -34
- package/src/model/modal-controller.ts +24 -41
- package/src/model/selection-model.ts +75 -43
- package/src/model/table-model.test.ts +92 -55
- package/src/model/table-model.ts +453 -175
- package/src/model/table-presentation.test.ts +70 -58
- package/src/model/table-presentation.ts +111 -64
- package/src/playwright/TableManager.ts +13 -6
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +171 -33
- package/src/testing/useTestTableModel.ts +46 -35
- package/src/translations.ts +3 -3
- package/src/types/Table.ts +79 -0
- package/src/types/index.ts +1 -1
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +46 -33
- package/src/util/index.ts +2 -1
- package/src/util/query-ast.ts +22 -0
- package/src/util/schema.ts +4 -4
- package/src/util/sort.ts +90 -0
- package/src/util/table-buttons.ts +4 -3
- package/src/util/table-controls.ts +6 -3
- package/dist/lib/browser/chunk-5XLSHLOG.mjs +0 -1751
- package/dist/lib/browser/chunk-5XLSHLOG.mjs.map +0 -7
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +0 -18
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +0 -20
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Y66YLSTC.mjs +0 -1752
- package/dist/lib/node-esm/chunk-Y66YLSTC.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/dist/types/src/components/Table/relations.stories.d.ts +0 -9
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts +0 -13
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +0 -1
- package/dist/types/src/model/table-sorting.d.ts +0 -53
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/dist/types/src/types/schema.d.ts +0 -8
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/dist/types/src/util/table.d.ts +0 -11
- package/dist/types/src/util/table.d.ts.map +0 -1
- package/src/components/Table/CreateRefPanel.tsx +0 -86
- package/src/components/Table/relations.stories.tsx +0 -116
- package/src/components/TableCellEditor/extension.ts +0 -61
- package/src/model/table-sorting.ts +0 -216
- package/src/types/schema.ts +0 -16
- package/src/util/table.ts +0 -49
|
@@ -2,102 +2,114 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
import {
|
|
9
|
+
import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
|
+
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
12
|
+
|
|
13
|
+
import { Table } from '../types';
|
|
13
14
|
|
|
14
15
|
import { TableModel, type TableModelProps } from './table-model';
|
|
15
16
|
import { TablePresentation } from './table-presentation';
|
|
16
|
-
import { TableView } from '../types';
|
|
17
17
|
|
|
18
18
|
describe('TablePresentation', () => {
|
|
19
|
-
describe('
|
|
19
|
+
describe('getCells', () => {
|
|
20
|
+
let registry: Registry.Registry;
|
|
20
21
|
let data: any[];
|
|
21
|
-
let updateCount: number;
|
|
22
22
|
let model: any;
|
|
23
23
|
let presentation: TablePresentation;
|
|
24
24
|
|
|
25
25
|
beforeEach(async () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
model = createTableModel({
|
|
35
|
-
onCellUpdate: () => updateCount++,
|
|
36
|
-
});
|
|
37
|
-
model.setRows(data);
|
|
26
|
+
registry = Registry.make();
|
|
27
|
+
data = [
|
|
28
|
+
{ id: '1', title: 'A', count: 1 },
|
|
29
|
+
{ id: '2', title: 'B', count: 2 },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
model = createTableModel(registry, {});
|
|
38
33
|
await model.open();
|
|
39
|
-
|
|
34
|
+
model.setRows(data);
|
|
35
|
+
presentation = new TablePresentation(registry, model);
|
|
40
36
|
});
|
|
41
37
|
|
|
42
|
-
it('should
|
|
43
|
-
|
|
44
|
-
presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
38
|
+
it('should return cells for the visible range', () => {
|
|
39
|
+
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
45
40
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
expect(
|
|
41
|
+
// Verify that cells are returned for the range. The exact column indices depend on the projection.
|
|
42
|
+
const cellKeys = Object.keys(cells);
|
|
43
|
+
expect(cellKeys.length).toBeGreaterThan(0);
|
|
49
44
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
expect(
|
|
45
|
+
// Check that values from the data are present in the cells.
|
|
46
|
+
const values = Object.values(cells).map((c) => c.value);
|
|
47
|
+
expect(values).toContain('A');
|
|
48
|
+
expect(values).toContain('B');
|
|
53
49
|
});
|
|
54
50
|
|
|
55
|
-
it('should
|
|
56
|
-
//
|
|
57
|
-
|
|
51
|
+
it('should reflect row data changes', () => {
|
|
52
|
+
// Update the row data and refresh.
|
|
53
|
+
data[0].title = 'New Value';
|
|
54
|
+
model.setRows(data);
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
// Verify the new value through getCells.
|
|
57
|
+
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 2 } }, 'grid');
|
|
58
|
+
const values = Object.values(cells).map((c) => c.value);
|
|
59
|
+
expect(values).toContain('New Value');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should reflect new rows', () => {
|
|
63
|
+
// Add a new row.
|
|
64
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
65
|
+
model.setRows(data);
|
|
61
66
|
|
|
62
67
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
63
68
|
|
|
64
69
|
// Verify the new row's data.
|
|
65
70
|
expect(cells['0,2']?.value).toBe('C');
|
|
66
71
|
expect(cells['1,2']?.value).toBe('3');
|
|
67
|
-
expect(cells['2,2']?.value).toBe('true');
|
|
68
72
|
});
|
|
69
73
|
|
|
70
|
-
it('should handle
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Multiple operations.
|
|
75
|
-
data[0].col1 = 'Updated A';
|
|
76
|
-
data.push({ col1: 'C', col2: 3, col3: true });
|
|
74
|
+
it('should handle row modifications', () => {
|
|
75
|
+
// Modify the data.
|
|
76
|
+
data[0].title = 'Updated A';
|
|
77
|
+
data.push({ id: '3', title: 'C', count: 3 });
|
|
77
78
|
data.splice(1, 1);
|
|
78
|
-
|
|
79
|
-
// We expect one update per operation affecting visible rows.
|
|
80
|
-
expect(updateCount).toBe(3);
|
|
79
|
+
model.setRows(data);
|
|
81
80
|
|
|
82
81
|
const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
83
82
|
|
|
84
83
|
// Verify final state.
|
|
85
84
|
expect(cells['0,0']?.value).toBe('Updated A');
|
|
86
85
|
expect(cells['0,1']?.value).toBe('C');
|
|
87
|
-
expect(cells['1,1']?.value).toBe('3');
|
|
88
|
-
expect(cells['2,1']?.value).toBe('true');
|
|
89
86
|
});
|
|
90
87
|
});
|
|
91
88
|
});
|
|
92
89
|
|
|
93
|
-
|
|
90
|
+
const Test = Schema.Struct({
|
|
94
91
|
title: Schema.String,
|
|
95
|
-
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
count: Schema.Number,
|
|
93
|
+
}).pipe(
|
|
94
|
+
Type.object({
|
|
95
|
+
typename: 'com.example.type.test',
|
|
96
|
+
version: '0.1.0',
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
99
101
|
const schema = createEchoSchema(Test);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
const view = ViewModel.make({
|
|
103
|
+
query: Query.select(Filter.type(schema)),
|
|
104
|
+
jsonSchema: schema.jsonSchema,
|
|
105
|
+
});
|
|
106
|
+
const object = Table.make({ view });
|
|
107
|
+
const projection = new ProjectionModel({
|
|
108
|
+
registry,
|
|
109
|
+
view,
|
|
110
|
+
baseSchema: schema.jsonSchema,
|
|
111
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
|
|
112
|
+
});
|
|
113
|
+
projection.normalizeView();
|
|
114
|
+
return new TableModel({ registry, object, projection, ...props });
|
|
103
115
|
};
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import {
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
10
|
+
import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
11
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
12
|
import {
|
|
12
13
|
type DxGridCellValue,
|
|
@@ -15,28 +16,34 @@ import {
|
|
|
15
16
|
type DxGridPlaneRange,
|
|
16
17
|
toPlaneCellIndex,
|
|
17
18
|
} from '@dxos/react-ui-grid';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
19
|
+
import { VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
20
|
+
import { mx } from '@dxos/ui-theme';
|
|
20
21
|
|
|
21
|
-
import { type SelectionMode } from './selection-model';
|
|
22
|
-
import { type TableRow, type TableModel } from './table-model';
|
|
23
22
|
import { tableButtons, tableControls } from '../util';
|
|
24
23
|
|
|
24
|
+
import { type SelectionMode } from './selection-model';
|
|
25
|
+
import { type TableModel, type TableRow } from './table-model';
|
|
26
|
+
|
|
25
27
|
/**
|
|
26
28
|
* Presentation layer for a table component, handling cell rendering and grid display logic.
|
|
27
29
|
* Manages rendering of table cells, headers, selection columns, and action columns across
|
|
28
30
|
* different grid planes.
|
|
29
31
|
*/
|
|
30
32
|
export class TablePresentation<T extends TableRow = TableRow> {
|
|
33
|
+
private readonly _registry: Registry.Registry;
|
|
34
|
+
private readonly _visibleRange: Atom.Writable<DxGridPlaneRange>;
|
|
31
35
|
private fieldProjectionCache = new Map<string, ReturnType<typeof this.model.projection.getFieldProjection>>();
|
|
32
36
|
|
|
33
37
|
constructor(
|
|
38
|
+
registry: Registry.Registry,
|
|
34
39
|
private readonly model: TableModel<T>,
|
|
35
|
-
|
|
40
|
+
) {
|
|
41
|
+
this._registry = registry;
|
|
42
|
+
this._visibleRange = Atom.make<DxGridPlaneRange>({
|
|
36
43
|
start: { row: 0, col: 0 },
|
|
37
44
|
end: { row: 0, col: 0 },
|
|
38
|
-
})
|
|
39
|
-
|
|
45
|
+
});
|
|
46
|
+
}
|
|
40
47
|
|
|
41
48
|
public getCells(range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells {
|
|
42
49
|
// Clear cache at the start of each render pass
|
|
@@ -46,7 +53,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
46
53
|
|
|
47
54
|
switch (plane) {
|
|
48
55
|
case 'grid':
|
|
49
|
-
this._visibleRange
|
|
56
|
+
this._registry.set(this._visibleRange, range);
|
|
50
57
|
cells = this.getMainGridCells(range);
|
|
51
58
|
break;
|
|
52
59
|
case 'frozenRowsStart':
|
|
@@ -67,6 +74,9 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
67
74
|
case 'frozenRowsEnd':
|
|
68
75
|
cells = this.getDraftRowCells(range);
|
|
69
76
|
break;
|
|
77
|
+
case 'fixedEndStart':
|
|
78
|
+
cells = this.getDraftIconCells(range);
|
|
79
|
+
break;
|
|
70
80
|
case 'fixedEndEnd':
|
|
71
81
|
cells = this.getDraftActionCells(range);
|
|
72
82
|
break;
|
|
@@ -98,7 +108,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
98
108
|
private createDataCell(
|
|
99
109
|
cells: DxGridPlaneCells,
|
|
100
110
|
obj: T,
|
|
101
|
-
field: FieldType,
|
|
111
|
+
field: View.FieldType,
|
|
102
112
|
colIndex: number,
|
|
103
113
|
displayIndex: number,
|
|
104
114
|
): void {
|
|
@@ -116,12 +126,12 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
switch (props.format) {
|
|
119
|
-
case
|
|
120
|
-
case
|
|
121
|
-
case
|
|
129
|
+
case Format.TypeFormat.Boolean:
|
|
130
|
+
case Format.TypeFormat.SingleSelect:
|
|
131
|
+
case Format.TypeFormat.MultiSelect: {
|
|
122
132
|
return '';
|
|
123
133
|
}
|
|
124
|
-
case
|
|
134
|
+
case Format.TypeFormat.Ref: {
|
|
125
135
|
if (!field.referencePath) {
|
|
126
136
|
return ''; // TODO(burdon): Show error.
|
|
127
137
|
}
|
|
@@ -137,20 +147,22 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
137
147
|
};
|
|
138
148
|
|
|
139
149
|
const classes = [];
|
|
140
|
-
const formatClasses = cellClassesForFieldType(
|
|
150
|
+
const formatClasses = cellClassesForFieldType(props);
|
|
151
|
+
|
|
141
152
|
if (formatClasses) {
|
|
142
153
|
classes.push(formatClasses);
|
|
143
154
|
}
|
|
155
|
+
|
|
144
156
|
const rowSelectionClasses = cellClassesForRowSelection(
|
|
145
157
|
this.model.selection.isObjectSelected(obj),
|
|
146
158
|
this.model.selection.selectionMode,
|
|
147
159
|
);
|
|
160
|
+
|
|
148
161
|
if (rowSelectionClasses) {
|
|
149
162
|
classes.push(rowSelectionClasses);
|
|
150
163
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
164
|
+
|
|
165
|
+
cell.className = mx(classes);
|
|
154
166
|
|
|
155
167
|
// Arrays.
|
|
156
168
|
if (props.type === TypeEnum.Array) {
|
|
@@ -184,7 +196,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
184
196
|
|
|
185
197
|
const tags = targetArray
|
|
186
198
|
.map(getLabel)
|
|
187
|
-
.filter(isNotNullable)
|
|
199
|
+
.filter(Predicate.isNotNullable)
|
|
188
200
|
.map((title) => {
|
|
189
201
|
return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
|
|
190
202
|
})
|
|
@@ -195,16 +207,16 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
195
207
|
}
|
|
196
208
|
|
|
197
209
|
// References.
|
|
198
|
-
if (props.format ===
|
|
210
|
+
if (props.format === Format.TypeFormat.Ref && props.referenceSchema) {
|
|
199
211
|
const targetObj = getValue(obj, field.path)?.target;
|
|
200
212
|
if (targetObj) {
|
|
201
213
|
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
202
|
-
cell.accessoryHtml = `<dx-
|
|
214
|
+
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><dx-anchor dxn=${dxn} class="dx-button w-6 aspect-square min-h-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
|
|
203
215
|
}
|
|
204
216
|
}
|
|
205
217
|
|
|
206
218
|
// Booleans.
|
|
207
|
-
if (props.format ===
|
|
219
|
+
if (props.format === Format.TypeFormat.Boolean) {
|
|
208
220
|
const value = getValue(obj, field.path);
|
|
209
221
|
cell.accessoryHtml = tableControls.switch.render({
|
|
210
222
|
colIndex,
|
|
@@ -215,7 +227,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
// Single-Selects.
|
|
218
|
-
if (props.format ===
|
|
230
|
+
if (props.format === Format.TypeFormat.SingleSelect) {
|
|
219
231
|
const value = getValue(obj, field.path);
|
|
220
232
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
221
233
|
if (options) {
|
|
@@ -227,7 +239,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
227
239
|
}
|
|
228
240
|
|
|
229
241
|
// Multi-Selects.
|
|
230
|
-
if (props.format ===
|
|
242
|
+
if (props.format === Format.TypeFormat.MultiSelect) {
|
|
231
243
|
const values = getValue(obj, field.path) as string[] | undefined;
|
|
232
244
|
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
233
245
|
if (options && values && values.length > 0) {
|
|
@@ -254,7 +266,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
254
266
|
|
|
255
267
|
private getMainGridCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
256
268
|
const cells: DxGridPlaneCells = {};
|
|
257
|
-
const fields = this.model.projection?.
|
|
269
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
258
270
|
|
|
259
271
|
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
260
272
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
@@ -263,7 +275,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
263
275
|
continue;
|
|
264
276
|
}
|
|
265
277
|
|
|
266
|
-
this.createDataCell(cells, this.model.
|
|
278
|
+
this.createDataCell(cells, this.model.getRows()[row], field, col, row);
|
|
267
279
|
}
|
|
268
280
|
}
|
|
269
281
|
|
|
@@ -272,26 +284,37 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
272
284
|
|
|
273
285
|
private getDraftRowCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
274
286
|
const cells: DxGridPlaneCells = {};
|
|
275
|
-
const fields = this.model.projection?.
|
|
276
|
-
const draftRows = this.model.
|
|
287
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
288
|
+
const draftRows = this.model.getDraftRows();
|
|
277
289
|
|
|
278
|
-
|
|
279
|
-
|
|
290
|
+
// Return cells of the CTA row if no draft row is active
|
|
291
|
+
if (draftRows.length === 0) {
|
|
280
292
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
293
|
+
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
294
|
+
value: '',
|
|
295
|
+
readonly: true,
|
|
296
|
+
className: 'dx-grid__row--cta__cell',
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
301
|
+
const draftRow = draftRows[row];
|
|
302
|
+
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
303
|
+
const cellIndex = toPlaneCellIndex({ col, row });
|
|
304
|
+
const field = fields[col];
|
|
305
|
+
if (!field) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
285
308
|
|
|
286
|
-
|
|
309
|
+
this.createDataCell(cells, draftRow.data, field, col, row);
|
|
287
310
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
311
|
+
if (this.model.hasDraftRowValidationError(row, field.path)) {
|
|
312
|
+
const cellValue = cells[cellIndex];
|
|
313
|
+
if (cellValue) {
|
|
314
|
+
const existingClasses = cellValue.className || '';
|
|
315
|
+
const draftClasses = 'dx-grid__cell--flagged';
|
|
316
|
+
cellValue.className = existingClasses ? `${existingClasses} ${draftClasses}` : draftClasses;
|
|
317
|
+
}
|
|
295
318
|
}
|
|
296
319
|
}
|
|
297
320
|
}
|
|
@@ -302,22 +325,22 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
302
325
|
|
|
303
326
|
private getHeaderCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
304
327
|
const cells: DxGridPlaneCells = {};
|
|
305
|
-
const fields = this.model.projection?.
|
|
328
|
+
const fields = this.model.projection?.getFields() ?? [];
|
|
329
|
+
const currentSort = this.model.getSorting();
|
|
306
330
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
307
331
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
308
|
-
const
|
|
309
|
-
const direction = sorting?.fieldId === field.id ? sorting.direction : undefined;
|
|
332
|
+
const direction = currentSort?.fieldId === field.id ? currentSort.direction : undefined;
|
|
310
333
|
|
|
311
334
|
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
312
335
|
// TODO(burdon): Use same logic as form for fallback title.
|
|
313
|
-
value:
|
|
314
|
-
readonly: true,
|
|
336
|
+
value: '',
|
|
315
337
|
resizeHandle: 'col',
|
|
316
338
|
accessoryHtml: `
|
|
339
|
+
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
317
340
|
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
318
341
|
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
319
342
|
`,
|
|
320
|
-
className: '!bg-
|
|
343
|
+
className: '!bg-toolbar-surface text-description! [&>div]:flex [&>div]:items-stretch',
|
|
321
344
|
};
|
|
322
345
|
}
|
|
323
346
|
|
|
@@ -360,7 +383,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
360
383
|
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === 'single') {
|
|
361
384
|
return {
|
|
362
385
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
363
|
-
className: '!bg-
|
|
386
|
+
className: '!bg-toolbar-surface',
|
|
364
387
|
readonly: true,
|
|
365
388
|
value: '',
|
|
366
389
|
},
|
|
@@ -372,9 +395,9 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
372
395
|
accessoryHtml: tableControls.checkbox.render({
|
|
373
396
|
rowIndex: 0,
|
|
374
397
|
header: true,
|
|
375
|
-
checked: this.model.selection.
|
|
398
|
+
checked: this.model.selection.allRowsSelected,
|
|
376
399
|
}),
|
|
377
|
-
className: '!bg-
|
|
400
|
+
className: '!bg-toolbar-surface',
|
|
378
401
|
readonly: true,
|
|
379
402
|
value: '',
|
|
380
403
|
},
|
|
@@ -386,10 +409,10 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
386
409
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
387
410
|
accessoryHtml: this.model.features.schemaEditable
|
|
388
411
|
? tableButtons.addColumn.render({
|
|
389
|
-
disabled: (this.model.projection?.
|
|
412
|
+
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
390
413
|
})
|
|
391
414
|
: undefined,
|
|
392
|
-
className: '!bg-
|
|
415
|
+
className: '!bg-toolbar-surface',
|
|
393
416
|
readonly: true,
|
|
394
417
|
value: '',
|
|
395
418
|
},
|
|
@@ -398,16 +421,41 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
398
421
|
|
|
399
422
|
private getDraftActionCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
400
423
|
const cells: DxGridPlaneCells = {};
|
|
401
|
-
const draftRows = this.model.
|
|
424
|
+
const draftRows = this.model.getDraftRows();
|
|
425
|
+
|
|
426
|
+
// Return cells of the CTA row if no draft row is active
|
|
427
|
+
if (draftRows.length === 0) {
|
|
428
|
+
cells[toPlaneCellIndex({ col: 0, row: 0 })] = {
|
|
429
|
+
value: '',
|
|
430
|
+
className: 'dx-grid__row--cta__cell',
|
|
431
|
+
readonly: true,
|
|
432
|
+
};
|
|
433
|
+
} else {
|
|
434
|
+
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
435
|
+
const draftRow = draftRows[row];
|
|
436
|
+
const disabled = !draftRow.valid;
|
|
402
437
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
438
|
+
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
439
|
+
value: '',
|
|
440
|
+
readonly: true,
|
|
441
|
+
accessoryHtml: tableButtons.saveDraftRow.render({ rowIndex: row, disabled }),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return cells;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private getDraftIconCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
450
|
+
const cells: DxGridPlaneCells = {};
|
|
451
|
+
const draftRows = this.model.getDraftRows();
|
|
406
452
|
|
|
453
|
+
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
407
454
|
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
408
455
|
value: '',
|
|
409
456
|
readonly: true,
|
|
410
|
-
accessoryHtml:
|
|
457
|
+
accessoryHtml: '<dx-icon icon="ph--plus--regular" class="contents"></dx-icon>',
|
|
458
|
+
className: mx('[&>div]:grid [&>div]:place-content-center', draftRows.length < 1 && 'dx-grid__row--cta__cell'),
|
|
411
459
|
};
|
|
412
460
|
}
|
|
413
461
|
|
|
@@ -418,7 +466,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
418
466
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
419
467
|
if (!selected) {
|
|
420
468
|
if (selectionMode === 'single') {
|
|
421
|
-
return ['
|
|
469
|
+
return ['cursor-pointer!'];
|
|
422
470
|
} else {
|
|
423
471
|
return undefined;
|
|
424
472
|
}
|
|
@@ -426,9 +474,8 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
426
474
|
|
|
427
475
|
switch (selectionMode) {
|
|
428
476
|
case 'single':
|
|
429
|
-
|
|
430
|
-
return ['!bg-currentRelated dx-grid__cell--no-focus-unfurl hover:bg-hoverSurface !cursor-pointer'];
|
|
477
|
+
return ['dx-grid__cell--no-focus-unfurl !bg-current-surface hover:bg-hover-surface cursor-pointer!'];
|
|
431
478
|
case 'multiple':
|
|
432
|
-
return ['!bg-
|
|
479
|
+
return ['!bg-grid-cell-selected'];
|
|
433
480
|
}
|
|
434
481
|
};
|
|
@@ -17,6 +17,7 @@ const TABLE_SELECTORS = {
|
|
|
17
17
|
},
|
|
18
18
|
columnDelete: 'column-delete',
|
|
19
19
|
rowDelete: 'row-menu-delete',
|
|
20
|
+
addRow: 'table.toolbar.add-row',
|
|
20
21
|
} as const;
|
|
21
22
|
|
|
22
23
|
export class TableManager {
|
|
@@ -53,12 +54,18 @@ export class TableManager {
|
|
|
53
54
|
await this.page.getByTestId(TABLE_SELECTORS.columnSort[direction]).click();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
public async
|
|
57
|
+
public async addRow(): Promise<void> {
|
|
58
|
+
await this.page.getByTestId(TABLE_SELECTORS.addRow).click();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async addColumn({ label, format }: { label: string; format?: string }): Promise<void> {
|
|
57
62
|
await this.page.getByTestId(TABLE_SELECTORS.newColumnButton).click();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await this.page.
|
|
63
|
+
if (format) {
|
|
64
|
+
await this.page.getByRole('combobox').click();
|
|
65
|
+
await this.page.getByLabel(format).click();
|
|
66
|
+
}
|
|
67
|
+
await this.page.getByPlaceholder('Property label').click();
|
|
68
|
+
await this.page.getByPlaceholder('Property label').fill(label);
|
|
69
|
+
await this.page.getByRole('button', { name: 'Save' }).click();
|
|
63
70
|
}
|
|
64
71
|
}
|
|
@@ -10,7 +10,7 @@ export default defineConfig({
|
|
|
10
10
|
...e2ePreset(import.meta.dirname),
|
|
11
11
|
// TODO(wittjosiah): Avoid hard-coding ports.
|
|
12
12
|
webServer: {
|
|
13
|
-
command: '
|
|
13
|
+
command: 'pnpm storybook dev --ci --quiet --port=9004 --config-dir=.storybook',
|
|
14
14
|
port: 9004,
|
|
15
15
|
reuseExistingServer: false,
|
|
16
16
|
},
|