@dxos/react-ui-table 0.8.3 → 0.8.4-main.28f8d3d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +18 -0
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs +1814 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +728 -2104
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +123 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +3 -5
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +20 -0
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs +1815 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +728 -2104
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +124 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +3 -5
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +4 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/{relations.stories.d.ts → Relations.stories.d.ts} +2 -2
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +1 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +13 -0
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -0
- package/dist/types/src/hooks/useTableModel.d.ts +8 -7
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +2 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +58 -21
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +10 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +4 -4
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/useTestTableModel.d.ts +41 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +5 -6
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +8 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util/dynamic-table.d.ts +3 -4
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +15 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +9 -11
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +52 -40
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +15 -11
- package/src/components/Table/CreateRefPanel.tsx +6 -4
- package/src/components/Table/DynamicTable.stories.tsx +4 -3
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +37 -37
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +109 -148
- package/src/components/Table/Table.tsx +95 -66
- package/src/components/TableCellEditor/CellValidationMessage.tsx +7 -2
- package/src/components/TableCellEditor/FormCellEditor.tsx +30 -21
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +27 -30
- package/src/components/TableCellEditor/TableCellEditor.tsx +88 -34
- package/src/components/TableCellEditor/extension.ts +2 -2
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +7 -24
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAddRow.ts +36 -0
- package/src/hooks/useTableModel.ts +21 -17
- package/src/index.ts +1 -2
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +6 -3
- package/src/model/table-model.test.ts +8 -8
- package/src/model/table-model.ts +278 -86
- package/src/model/table-presentation.test.ts +8 -8
- package/src/model/table-presentation.ts +240 -135
- package/src/model/table-sorting.ts +11 -11
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +113 -20
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +123 -0
- package/src/translations.ts +7 -5
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +16 -0
- package/src/util/dynamic-table.ts +20 -18
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +26 -2
- package/src/util/table-controls.ts +4 -1
- package/src/util/table.ts +37 -111
- package/dist/lib/browser/chunk-TL7CKQQV.mjs +0 -27
- package/dist/lib/browser/chunk-TL7CKQQV.mjs.map +0 -7
- package/dist/lib/node/chunk-O2VPBNS5.cjs +0 -50
- package/dist/lib/node/chunk-O2VPBNS5.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -2739
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/types/index.cjs +0 -31
- package/dist/lib/node/types/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs +0 -29
- package/dist/lib/node-esm/chunk-GBT6GD4G.mjs.map +0 -7
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/types/table.d.ts +0 -56
- package/dist/types/src/types/table.d.ts.map +0 -1
- package/src/playwright/playwright.config.cts +0 -18
- package/src/types/table.ts +0 -27
|
@@ -5,28 +5,32 @@
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
6
|
import { isNotNullable } from 'effect/Predicate';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { Obj } from '@dxos/echo';
|
|
9
|
+
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo-schema';
|
|
9
10
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
10
11
|
import {
|
|
12
|
+
type DxGridCellValue,
|
|
11
13
|
type DxGridPlane,
|
|
12
14
|
type DxGridPlaneCells,
|
|
13
15
|
type DxGridPlaneRange,
|
|
14
|
-
type DxGridCellValue,
|
|
15
16
|
toPlaneCellIndex,
|
|
16
17
|
} from '@dxos/react-ui-grid';
|
|
17
18
|
import { mx } from '@dxos/react-ui-theme';
|
|
18
|
-
import {
|
|
19
|
+
import { type FieldType, VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
19
20
|
|
|
20
|
-
import { type SelectionMode } from './selection-model';
|
|
21
|
-
import { type TableRow, type TableModel } from './table-model';
|
|
22
21
|
import { tableButtons, tableControls } from '../util';
|
|
23
22
|
|
|
23
|
+
import { type SelectionMode } from './selection-model';
|
|
24
|
+
import { type TableModel, type TableRow } from './table-model';
|
|
25
|
+
|
|
24
26
|
/**
|
|
25
27
|
* Presentation layer for a table component, handling cell rendering and grid display logic.
|
|
26
28
|
* Manages rendering of table cells, headers, selection columns, and action columns across
|
|
27
29
|
* different grid planes.
|
|
28
30
|
*/
|
|
29
31
|
export class TablePresentation<T extends TableRow = TableRow> {
|
|
32
|
+
private fieldProjectionCache = new Map<string, ReturnType<typeof this.model.projection.getFieldProjection>>();
|
|
33
|
+
|
|
30
34
|
constructor(
|
|
31
35
|
private readonly model: TableModel<T>,
|
|
32
36
|
private readonly _visibleRange = signal<DxGridPlaneRange>({
|
|
@@ -36,6 +40,9 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
36
40
|
) {}
|
|
37
41
|
|
|
38
42
|
public getCells(range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells {
|
|
43
|
+
// Clear cache at the start of each render pass
|
|
44
|
+
this.fieldProjectionCache.clear();
|
|
45
|
+
|
|
39
46
|
let cells: DxGridPlaneCells;
|
|
40
47
|
|
|
41
48
|
switch (plane) {
|
|
@@ -58,12 +65,21 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
58
65
|
case 'fixedStartEnd':
|
|
59
66
|
cells = this.getNewColumnCell();
|
|
60
67
|
break;
|
|
68
|
+
case 'frozenRowsEnd':
|
|
69
|
+
cells = this.getDraftRowCells(range);
|
|
70
|
+
break;
|
|
71
|
+
case 'fixedEndStart':
|
|
72
|
+
cells = this.getDraftSelectCells(range);
|
|
73
|
+
break;
|
|
74
|
+
case 'fixedEndEnd':
|
|
75
|
+
cells = this.getDraftActionCells(range);
|
|
76
|
+
break;
|
|
61
77
|
default:
|
|
62
78
|
cells = {};
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
if (plane === 'grid' && this.model.features.dataEditable === false) {
|
|
66
|
-
Object.values(cells).forEach((cell) => {
|
|
82
|
+
Object.values<DxGridCellValue>(cells).forEach((cell) => {
|
|
67
83
|
cell.readonly = 'text-select';
|
|
68
84
|
});
|
|
69
85
|
}
|
|
@@ -71,164 +87,219 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
71
87
|
return cells;
|
|
72
88
|
}
|
|
73
89
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Gets a field projection, using cache if available within the current render pass.
|
|
92
|
+
*/
|
|
93
|
+
private getFieldProjection(fieldId: string) {
|
|
94
|
+
let projection = this.fieldProjectionCache.get(fieldId);
|
|
95
|
+
if (!projection) {
|
|
96
|
+
projection = this.model.projection.getFieldProjection(fieldId);
|
|
97
|
+
this.fieldProjectionCache.set(fieldId, projection);
|
|
98
|
+
}
|
|
99
|
+
return projection;
|
|
100
|
+
}
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
private createDataCell(
|
|
103
|
+
cells: DxGridPlaneCells,
|
|
104
|
+
obj: T,
|
|
105
|
+
field: FieldType,
|
|
106
|
+
colIndex: number,
|
|
107
|
+
displayIndex: number,
|
|
108
|
+
): void {
|
|
109
|
+
const { props } = this.getFieldProjection(field.id);
|
|
110
|
+
|
|
111
|
+
const cell: DxGridCellValue = {
|
|
112
|
+
get value() {
|
|
113
|
+
const value = getValue(obj, field.path);
|
|
114
|
+
if (value == null) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
80
117
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (value == null) {
|
|
85
|
-
return '';
|
|
86
|
-
}
|
|
118
|
+
if (props.type === 'array') {
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
87
121
|
|
|
88
|
-
|
|
122
|
+
switch (props.format) {
|
|
123
|
+
case FormatEnum.Boolean:
|
|
124
|
+
case FormatEnum.SingleSelect:
|
|
125
|
+
case FormatEnum.MultiSelect: {
|
|
89
126
|
return '';
|
|
90
127
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
case FormatEnum.SingleSelect:
|
|
95
|
-
case FormatEnum.MultiSelect: {
|
|
96
|
-
return '';
|
|
128
|
+
case FormatEnum.Ref: {
|
|
129
|
+
if (!field.referencePath) {
|
|
130
|
+
return ''; // TODO(burdon): Show error.
|
|
97
131
|
}
|
|
98
|
-
case FormatEnum.Ref: {
|
|
99
|
-
if (!field.referencePath) {
|
|
100
|
-
return ''; // TODO(burdon): Show error.
|
|
101
|
-
}
|
|
102
132
|
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
return getValue(value.target, field.referencePath);
|
|
134
|
+
}
|
|
105
135
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
136
|
+
default: {
|
|
137
|
+
return formatForDisplay({ type: props.type, format: props.format, value });
|
|
109
138
|
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
112
142
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
const classes = [];
|
|
144
|
+
const formatClasses = cellClassesForFieldType({ type: props.type, format: props.format });
|
|
145
|
+
if (formatClasses) {
|
|
146
|
+
classes.push(formatClasses);
|
|
147
|
+
}
|
|
148
|
+
const rowSelectionClasses = cellClassesForRowSelection(
|
|
149
|
+
this.model.selection.isObjectSelected(obj),
|
|
150
|
+
this.model.selection.selectionMode,
|
|
151
|
+
);
|
|
152
|
+
if (rowSelectionClasses) {
|
|
153
|
+
classes.push(rowSelectionClasses);
|
|
154
|
+
}
|
|
155
|
+
if (classes.length > 0) {
|
|
156
|
+
cell.className = mx(classes.flat());
|
|
157
|
+
}
|
|
128
158
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
159
|
+
// Arrays.
|
|
160
|
+
if (props.type === TypeEnum.Array) {
|
|
161
|
+
const targetArray = getValue(obj, field.path);
|
|
162
|
+
if (targetArray && Array.isArray(targetArray)) {
|
|
163
|
+
// TODO(ZaymonFC): For arrays of objects, objects should have a 'label' annotation on the field
|
|
164
|
+
// that can be used to render tags.
|
|
165
|
+
// TODO(ZaymonFC): Move to util.
|
|
166
|
+
const getLabel = (value: any) => {
|
|
167
|
+
// If the value is falsy, return undefined
|
|
168
|
+
// If the object is an array, check for 'name' property
|
|
169
|
+
// If the object is a primitive type (string, number, boolean) stringify
|
|
170
|
+
// If the object is an array return 'Array'
|
|
171
|
+
if (!value) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
175
|
+
return String(value);
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(value)) {
|
|
178
|
+
return 'Array';
|
|
179
|
+
}
|
|
180
|
+
if (typeof value === 'object' && value.name) {
|
|
181
|
+
return value.name;
|
|
182
|
+
}
|
|
183
|
+
if (typeof value === 'object' && value.value) {
|
|
184
|
+
return `${value.value}`;
|
|
185
|
+
}
|
|
186
|
+
return 'Object';
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const tags = targetArray
|
|
190
|
+
.map(getLabel)
|
|
191
|
+
.filter(isNotNullable)
|
|
192
|
+
.map((title) => {
|
|
193
|
+
return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
|
|
194
|
+
})
|
|
195
|
+
.join('');
|
|
196
|
+
|
|
197
|
+
cell.accessoryHtml = `<div role='none' class="flex flex-row gap-1 overflow-x-auto">${tags}</div>`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
165
200
|
|
|
166
|
-
|
|
167
|
-
|
|
201
|
+
// References.
|
|
202
|
+
if (props.format === FormatEnum.Ref && props.referenceSchema) {
|
|
203
|
+
const targetObj = getValue(obj, field.path)?.target;
|
|
204
|
+
if (targetObj) {
|
|
205
|
+
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
206
|
+
cell.accessoryHtml = `<dx-ref-tag refId=${dxn} class="dx-button is-6 pli-[3px] pbe-[2px] min-bs-0 absolute inline-end-2 block-start-[.2rem]" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-ref-tag>`;
|
|
168
207
|
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Booleans.
|
|
211
|
+
if (props.format === FormatEnum.Boolean) {
|
|
212
|
+
const value = getValue(obj, field.path);
|
|
213
|
+
cell.accessoryHtml = tableControls.switch.render({
|
|
214
|
+
colIndex,
|
|
215
|
+
rowIndex: displayIndex,
|
|
216
|
+
checked: value ?? false,
|
|
217
|
+
});
|
|
218
|
+
cell.readonly = 'no-text-select';
|
|
219
|
+
}
|
|
169
220
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
221
|
+
// Single-Selects.
|
|
222
|
+
if (props.format === FormatEnum.SingleSelect) {
|
|
223
|
+
const value = getValue(obj, field.path);
|
|
224
|
+
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
225
|
+
if (options) {
|
|
226
|
+
const option = options.find((o) => o.id === value);
|
|
227
|
+
if (option) {
|
|
228
|
+
cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
175
229
|
}
|
|
176
230
|
}
|
|
231
|
+
}
|
|
177
232
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
233
|
+
// Multi-Selects.
|
|
234
|
+
if (props.format === FormatEnum.MultiSelect) {
|
|
235
|
+
const values = getValue(obj, field.path) as string[] | undefined;
|
|
236
|
+
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
237
|
+
if (options && values && values.length > 0) {
|
|
238
|
+
const tags = values
|
|
239
|
+
.map((value) => {
|
|
240
|
+
const option = options.find((o) => o.id === value);
|
|
241
|
+
if (option) {
|
|
242
|
+
return `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
})
|
|
246
|
+
.filter(Boolean)
|
|
247
|
+
.join('');
|
|
187
248
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
191
|
-
if (options) {
|
|
192
|
-
const option = options.find((o) => o.id === value);
|
|
193
|
-
if (option) {
|
|
194
|
-
cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
195
|
-
}
|
|
249
|
+
if (tags) {
|
|
250
|
+
cell.accessoryHtml = tags;
|
|
196
251
|
}
|
|
197
252
|
}
|
|
253
|
+
}
|
|
198
254
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (options && values && values.length > 0) {
|
|
203
|
-
const tags = values
|
|
204
|
-
.map((value) => {
|
|
205
|
-
const option = options.find((o) => o.id === value);
|
|
206
|
-
if (option) {
|
|
207
|
-
return `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
208
|
-
}
|
|
209
|
-
return null;
|
|
210
|
-
})
|
|
211
|
-
.filter(Boolean)
|
|
212
|
-
.join(' ');
|
|
255
|
+
const idx = toPlaneCellIndex({ col: colIndex, row: displayIndex });
|
|
256
|
+
cells[idx] = cell;
|
|
257
|
+
}
|
|
213
258
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
259
|
+
private getMainGridCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
260
|
+
const cells: DxGridPlaneCells = {};
|
|
261
|
+
const fields = this.model.projection?.fields ?? [];
|
|
262
|
+
|
|
263
|
+
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
264
|
+
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
265
|
+
const field = fields[col];
|
|
266
|
+
if (!field) {
|
|
267
|
+
continue;
|
|
217
268
|
}
|
|
269
|
+
|
|
270
|
+
this.createDataCell(cells, this.model.rows.value[row], field, col, row);
|
|
218
271
|
}
|
|
272
|
+
}
|
|
219
273
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
};
|
|
274
|
+
return cells;
|
|
275
|
+
}
|
|
223
276
|
|
|
224
|
-
|
|
277
|
+
private getDraftRowCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
278
|
+
const cells: DxGridPlaneCells = {};
|
|
279
|
+
const fields = this.model.projection?.fields ?? [];
|
|
280
|
+
const draftRows = this.model.draftRows.value;
|
|
281
|
+
|
|
282
|
+
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
283
|
+
const draftRow = draftRows[row];
|
|
225
284
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
285
|
+
const cellIndex = toPlaneCellIndex({ col, row });
|
|
226
286
|
const field = fields[col];
|
|
227
287
|
if (!field) {
|
|
228
288
|
continue;
|
|
229
289
|
}
|
|
230
290
|
|
|
231
|
-
|
|
291
|
+
this.createDataCell(cells, draftRow.data, field, col, row);
|
|
292
|
+
|
|
293
|
+
if (this.model.hasDraftRowValidationError(row, field.path)) {
|
|
294
|
+
const cellValue = cells[cellIndex];
|
|
295
|
+
if (cellValue) {
|
|
296
|
+
const existingClasses = cellValue.className || '';
|
|
297
|
+
const draftClasses = 'dx-grid__cell--flagged';
|
|
298
|
+
cellValue.className = existingClasses ? `${existingClasses} ${draftClasses}` : draftClasses;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
cells[cellIndex].className += ' !bg-toolbarSurface';
|
|
232
303
|
}
|
|
233
304
|
}
|
|
234
305
|
|
|
@@ -237,7 +308,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
237
308
|
|
|
238
309
|
private getHeaderCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
239
310
|
const cells: DxGridPlaneCells = {};
|
|
240
|
-
const fields = this.model.
|
|
311
|
+
const fields = this.model.projection?.fields ?? [];
|
|
241
312
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
242
313
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
243
314
|
const sorting = this.model.sorting?.sorting;
|
|
@@ -321,7 +392,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
321
392
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
322
393
|
accessoryHtml: this.model.features.schemaEditable
|
|
323
394
|
? tableButtons.addColumn.render({
|
|
324
|
-
disabled: (this.model.
|
|
395
|
+
disabled: (this.model.projection?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
325
396
|
})
|
|
326
397
|
: undefined,
|
|
327
398
|
className: '!bg-toolbarSurface',
|
|
@@ -330,6 +401,40 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
330
401
|
},
|
|
331
402
|
};
|
|
332
403
|
}
|
|
404
|
+
|
|
405
|
+
private getDraftActionCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
406
|
+
const cells: DxGridPlaneCells = {};
|
|
407
|
+
const draftRows = this.model.draftRows.value;
|
|
408
|
+
|
|
409
|
+
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
410
|
+
const draftRow = draftRows[row];
|
|
411
|
+
const disabled = !draftRow.valid;
|
|
412
|
+
|
|
413
|
+
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
414
|
+
value: '',
|
|
415
|
+
readonly: true,
|
|
416
|
+
accessoryHtml: tableButtons.saveDraftRow.render({ rowIndex: row, disabled }),
|
|
417
|
+
className: '!bg-toolbarSurface',
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return cells;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
private getDraftSelectCells(range: DxGridPlaneRange): DxGridPlaneCells {
|
|
425
|
+
const cells: DxGridPlaneCells = {};
|
|
426
|
+
const draftRows = this.model.draftRows.value;
|
|
427
|
+
|
|
428
|
+
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
429
|
+
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
430
|
+
value: '',
|
|
431
|
+
readonly: true,
|
|
432
|
+
className: '!bg-toolbarSurface',
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return cells;
|
|
437
|
+
}
|
|
333
438
|
}
|
|
334
439
|
|
|
335
440
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
@@ -344,7 +449,7 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
344
449
|
switch (selectionMode) {
|
|
345
450
|
case 'single':
|
|
346
451
|
// TODO(ZaymonFC): @thure, do we need a grid version of 'currentRelated'?
|
|
347
|
-
return ['!bg-currentRelated hover:bg-hoverSurface
|
|
452
|
+
return ['!bg-currentRelated dx-grid__cell--no-focus-unfurl hover:bg-hoverSurface !cursor-pointer'];
|
|
348
453
|
case 'multiple':
|
|
349
454
|
return ['!bg-gridCellSelected'];
|
|
350
455
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type ReadonlySignal, type Signal, computed, signal } from '@preact/signals-core';
|
|
6
6
|
import orderBy from 'lodash.orderby';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { type FieldSortType, FormatEnum, type SortDirectionType, TypeEnum, getValue } from '@dxos/echo-schema';
|
|
9
9
|
import { formatForDisplay } from '@dxos/react-ui-form';
|
|
10
|
-
import type {
|
|
10
|
+
import type { DataType, FieldType, ProjectionModel, PropertyType } from '@dxos/schema';
|
|
11
11
|
|
|
12
12
|
import { type TableRow } from './table-model';
|
|
13
13
|
|
|
@@ -49,13 +49,13 @@ export class TableSorting<T extends TableRow> {
|
|
|
49
49
|
|
|
50
50
|
constructor(
|
|
51
51
|
rows: Signal<T[]>,
|
|
52
|
-
private readonly _view:
|
|
53
|
-
private readonly _projection:
|
|
52
|
+
private readonly _view: DataType.View,
|
|
53
|
+
private readonly _projection: ProjectionModel,
|
|
54
54
|
) {
|
|
55
55
|
this._rows = rows;
|
|
56
56
|
this._isDirty = computed(() => {
|
|
57
57
|
const local = this._localSort.value;
|
|
58
|
-
const viewSort = this._view
|
|
58
|
+
const viewSort = this._view.query.sort?.[0];
|
|
59
59
|
if (local?.type === 'cleared') {
|
|
60
60
|
return viewSort !== undefined;
|
|
61
61
|
}
|
|
@@ -83,7 +83,7 @@ export class TableSorting<T extends TableRow> {
|
|
|
83
83
|
return local.sort;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
return this._view
|
|
86
|
+
return this._view.query.sort?.[0];
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
@@ -106,7 +106,7 @@ export class TableSorting<T extends TableRow> {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
public toggleSort(fieldId: string): void {
|
|
109
|
-
if (!this.
|
|
109
|
+
if (!this.sorting || this.sorting.fieldId !== fieldId) {
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -127,7 +127,7 @@ export class TableSorting<T extends TableRow> {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
public save(): void {
|
|
130
|
-
if (this.
|
|
130
|
+
if (this._localSort.value !== undefined) {
|
|
131
131
|
if (this._localSort.value.type === 'active') {
|
|
132
132
|
this._view.query.sort = [this._localSort.value.sort];
|
|
133
133
|
} else {
|
|
@@ -145,11 +145,11 @@ export class TableSorting<T extends TableRow> {
|
|
|
145
145
|
return computed(() => {
|
|
146
146
|
this._displayToDataIndex.clear();
|
|
147
147
|
const sort = this.sorting;
|
|
148
|
-
if (!sort
|
|
148
|
+
if (!sort) {
|
|
149
149
|
return this._rows.value;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
const field = this.
|
|
152
|
+
const field = this._projection.fields.find((f) => f.id === sort.fieldId);
|
|
153
153
|
if (!field) {
|
|
154
154
|
return this._rows.value;
|
|
155
155
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { defineConfig } from '@playwright/test';
|
|
6
|
+
|
|
7
|
+
import { e2ePreset } from '@dxos/test-utils/playwright';
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
...e2ePreset(import.meta.dirname),
|
|
11
|
+
// TODO(wittjosiah): Avoid hard-coding ports.
|
|
12
|
+
webServer: {
|
|
13
|
+
command: 'moon run storybook:serve-e2e -- --port=9004',
|
|
14
|
+
port: 9004,
|
|
15
|
+
reuseExistingServer: false,
|
|
16
|
+
},
|
|
17
|
+
});
|