@dxos/react-ui-table 0.7.4 → 0.7.5-main.9d26e3a
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-IYCSJDMB.mjs → chunk-AT7O4RAD.mjs} +4 -4
- package/dist/lib/browser/{chunk-IYCSJDMB.mjs.map → chunk-AT7O4RAD.mjs.map} +2 -2
- package/dist/lib/browser/index.mjs +1404 -1218
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node/{chunk-MHBMVCFZ.cjs → chunk-YSBXNBDJ.cjs} +6 -6
- package/dist/lib/node/{chunk-MHBMVCFZ.cjs.map → chunk-YSBXNBDJ.cjs.map} +2 -2
- package/dist/lib/node/index.cjs +1136 -936
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +3 -3
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{chunk-BMWGPXPL.mjs → chunk-6OH2NHUX.mjs} +4 -4
- package/dist/lib/node-esm/{chunk-BMWGPXPL.mjs.map → chunk-6OH2NHUX.mjs.map} +2 -2
- package/dist/lib/node-esm/index.mjs +1404 -1218
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +4 -3
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +3 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +3 -2
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/RefPanel.d.ts +4 -3
- package/dist/types/src/components/Table/RefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +3 -2
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +2 -1
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +3 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +55 -71
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/index.d.ts +1 -0
- package/dist/types/src/model/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +6 -2
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +8 -12
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +22 -0
- package/dist/types/src/model/table-presentation.d.ts.map +1 -0
- package/dist/types/src/model/table-presentation.test.d.ts +2 -0
- package/dist/types/src/model/table-presentation.test.d.ts.map +1 -0
- 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/types/table.d.ts +118 -154
- package/dist/types/src/types/table.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +50 -20
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +32 -9
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +2 -2
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +29 -29
- package/src/components/Table/ColumnActionsMenu.tsx +8 -8
- package/src/components/Table/ColumnSettings.tsx +11 -11
- package/src/components/Table/CreateRefPanel.tsx +13 -14
- package/src/components/Table/RefPanel.tsx +9 -9
- package/src/components/Table/RowActionsMenu.tsx +6 -7
- package/src/components/Table/Table.stories.tsx +40 -23
- package/src/components/Table/Table.tsx +235 -148
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -12
- package/src/components/TableCellEditor/TableCellEditor.tsx +6 -4
- package/src/components/testing.ts +10 -8
- package/src/index.ts +2 -0
- package/src/model/index.ts +1 -0
- package/src/model/modal-controller.ts +15 -48
- package/src/model/selection-model.ts +12 -1
- package/src/model/table-model.test.ts +4 -71
- package/src/model/table-model.ts +59 -232
- package/src/model/table-presentation.test.ts +102 -0
- package/src/model/table-presentation.ts +214 -0
- package/src/playwright/TableManager.ts +5 -1
- package/src/playwright/smoke.spec.ts +79 -1
- package/src/types/table.ts +3 -3
- package/src/util/index.ts +1 -0
- package/src/util/table-buttons.ts +97 -48
- package/src/util/table-controls.ts +71 -7
- package/src/util/table.ts +21 -13
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
|
-
import { type RefObject, type
|
|
6
|
+
import { type RefObject, type MutableRefObject } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type ReactiveObject } from '@dxos/live-object';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
|
|
11
|
-
import { tableButtons } from '../util';
|
|
12
|
-
|
|
13
11
|
export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
|
|
14
12
|
|
|
15
13
|
export type ModalState =
|
|
@@ -37,55 +35,24 @@ export class ModalController {
|
|
|
37
35
|
return this._triggerRef as RefObject<HTMLButtonElement>;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
public
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// TODO(thure): why not just get the value of the closest recognized attribute and `switch` on that?
|
|
44
|
-
// Repeated querying is more error-prone and less legible, I’d think.
|
|
45
|
-
|
|
46
|
-
const rowButton = target.closest(`button[${tableButtons.rowMenu.attr}]`);
|
|
47
|
-
if (rowButton) {
|
|
48
|
-
this._triggerRef.current = rowButton as HTMLElement;
|
|
49
|
-
this._state.value = {
|
|
50
|
-
type: 'row',
|
|
51
|
-
rowIndex: Number(rowButton.getAttribute(tableButtons.rowMenu.attr)),
|
|
52
|
-
};
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
38
|
+
public setTrigger = (trigger: HTMLElement) => {
|
|
39
|
+
this._triggerRef.current = trigger;
|
|
40
|
+
};
|
|
55
41
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this._triggerRef.current = columnButton as HTMLElement;
|
|
60
|
-
this._state.value = {
|
|
61
|
-
type: 'column',
|
|
62
|
-
fieldId,
|
|
63
|
-
};
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
42
|
+
public showRowMenu = (rowIndex: number) => {
|
|
43
|
+
this._state.value = { type: 'row', rowIndex };
|
|
44
|
+
};
|
|
66
45
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this._state.value = {
|
|
71
|
-
type: 'columnSettings',
|
|
72
|
-
mode: { type: 'create' },
|
|
73
|
-
};
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
46
|
+
public showColumnMenu = (fieldId: string) => {
|
|
47
|
+
this._state.value = { type: 'column', fieldId };
|
|
48
|
+
};
|
|
76
49
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const [schemaId, targetId] = refButton.getAttribute(tableButtons.referencedCell.attr)!.split('#');
|
|
81
|
-
this._state.value = {
|
|
82
|
-
type: 'refPanel',
|
|
83
|
-
targetId,
|
|
84
|
-
typename: schemaId,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
50
|
+
public showColumnCreator = () => {
|
|
51
|
+
this._state.value = { type: 'columnSettings', mode: { type: 'create' } };
|
|
52
|
+
};
|
|
87
53
|
|
|
88
|
-
|
|
54
|
+
public showReferencePanel = (targetId: string, schemaId: string) => {
|
|
55
|
+
this._state.value = { type: 'refPanel', targetId, typename: schemaId };
|
|
89
56
|
};
|
|
90
57
|
|
|
91
58
|
public openColumnSettings = () => {
|
|
@@ -17,6 +17,7 @@ export class SelectionModel<T extends BaseTableRow> extends Resource {
|
|
|
17
17
|
if (!rows) {
|
|
18
18
|
return new Set<string>();
|
|
19
19
|
}
|
|
20
|
+
|
|
20
21
|
const validIds = new Set(rows.map((row) => row.id));
|
|
21
22
|
return new Set([...this._selection.value].filter((id) => validIds.has(id)));
|
|
22
23
|
});
|
|
@@ -28,6 +29,7 @@ export class SelectionModel<T extends BaseTableRow> extends Resource {
|
|
|
28
29
|
if (rows.length === 0) {
|
|
29
30
|
return false;
|
|
30
31
|
}
|
|
32
|
+
|
|
31
33
|
return rows.every((row) => this._selection.value.has(row.id));
|
|
32
34
|
});
|
|
33
35
|
|
|
@@ -94,6 +96,15 @@ export class SelectionModel<T extends BaseTableRow> extends Resource {
|
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
public setSelection = (mode: 'all' | 'none'): void => {
|
|
97
|
-
|
|
99
|
+
switch (mode) {
|
|
100
|
+
case 'all': {
|
|
101
|
+
this._selection.value = new Set(this._rows.value.map((row) => row.id));
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'none': {
|
|
105
|
+
this._selection.value = new Set();
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
98
109
|
};
|
|
99
110
|
}
|
|
@@ -8,8 +8,8 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
8
8
|
import { S, TypedObject } from '@dxos/echo-schema';
|
|
9
9
|
import { updateCounter } from '@dxos/echo-schema/testing';
|
|
10
10
|
import { registerSignalsRuntime } from '@dxos/echo-signals';
|
|
11
|
-
import {
|
|
12
|
-
import { create } from '@dxos/react-client/echo';
|
|
11
|
+
import { createEchoSchema } from '@dxos/live-object/testing';
|
|
12
|
+
import { create, makeRef } from '@dxos/react-client/echo';
|
|
13
13
|
import { createView, ViewProjection } from '@dxos/schema';
|
|
14
14
|
|
|
15
15
|
import { TableModel, type TableModelProps } from './table-model';
|
|
@@ -100,73 +100,6 @@ describe('TableModel', () => {
|
|
|
100
100
|
expect(outerCounter.count).toBe(1);
|
|
101
101
|
expect(innerCounter.count).toBe(1);
|
|
102
102
|
});
|
|
103
|
-
|
|
104
|
-
describe('row reactivity', () => {
|
|
105
|
-
let data: any[];
|
|
106
|
-
|
|
107
|
-
beforeEach(async () => {
|
|
108
|
-
// TODO(burdon): Use generator.
|
|
109
|
-
({ data } = create({
|
|
110
|
-
data: [
|
|
111
|
-
{ col1: 'A', col2: 1, col3: true },
|
|
112
|
-
{ col1: 'B', col2: 2, col3: false },
|
|
113
|
-
],
|
|
114
|
-
}));
|
|
115
|
-
|
|
116
|
-
const model = createTableModel();
|
|
117
|
-
model.setRows(data);
|
|
118
|
-
await model.open();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should update with row-level reactivity', () => {
|
|
122
|
-
// Set up visible range to include our test data
|
|
123
|
-
model.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
124
|
-
|
|
125
|
-
// Trigger a row update
|
|
126
|
-
data[0].col1 = 'New Value';
|
|
127
|
-
expect(updateCount).toBe(1);
|
|
128
|
-
|
|
129
|
-
// Verify the new value through getCells
|
|
130
|
-
const cells = model.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 0 } }, 'grid');
|
|
131
|
-
expect(cells['0,0']?.value).toBe('New Value');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should update reactively when adding a new row', () => {
|
|
135
|
-
// Set up visible range to include our test data
|
|
136
|
-
model.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
137
|
-
|
|
138
|
-
data.push({ col1: 'C', col2: 3, col3: true });
|
|
139
|
-
expect(updateCount).toBe(1);
|
|
140
|
-
|
|
141
|
-
const cells = model.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
142
|
-
|
|
143
|
-
// Verify the new row's data
|
|
144
|
-
expect(cells['0,2']?.value).toBe('C');
|
|
145
|
-
expect(cells['1,2']?.value).toBe('3');
|
|
146
|
-
expect(cells['2,2']?.value).toBe('true');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should handle combined operations reactively', () => {
|
|
150
|
-
// Set up visible range
|
|
151
|
-
model.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
|
|
152
|
-
|
|
153
|
-
// Multiple operations
|
|
154
|
-
data[0].col1 = 'Updated A';
|
|
155
|
-
data.push({ col1: 'C', col2: 3, col3: true });
|
|
156
|
-
data.splice(1, 1);
|
|
157
|
-
|
|
158
|
-
// We expect one update per operation affecting visible rows
|
|
159
|
-
expect(updateCount).toBe(3);
|
|
160
|
-
|
|
161
|
-
const cells = model.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
|
|
162
|
-
|
|
163
|
-
// Verify final state
|
|
164
|
-
expect(cells['0,0']?.value).toBe('Updated A');
|
|
165
|
-
expect(cells['0,1']?.value).toBe('C');
|
|
166
|
-
expect(cells['1,1']?.value).toBe('3');
|
|
167
|
-
expect(cells['2,1']?.value).toBe('true');
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
103
|
});
|
|
171
104
|
});
|
|
172
105
|
|
|
@@ -176,9 +109,9 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
176
109
|
}) {}
|
|
177
110
|
|
|
178
111
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
179
|
-
const schema =
|
|
112
|
+
const schema = createEchoSchema(Test);
|
|
180
113
|
const view = createView({ name: 'Test', typename: schema.typename, jsonSchema: schema.jsonSchema });
|
|
181
114
|
const projection = new ViewProjection(schema, view);
|
|
182
|
-
const table = create(TableType, { view });
|
|
115
|
+
const table = create(TableType, { view: makeRef(view) });
|
|
183
116
|
return new TableModel({ table, projection, ...props });
|
|
184
117
|
};
|
package/src/model/table-model.ts
CHANGED
|
@@ -8,32 +8,20 @@ import orderBy from 'lodash.orderby';
|
|
|
8
8
|
import { Resource } from '@dxos/context';
|
|
9
9
|
import { getValue, setValue, FormatEnum, type JsonProp } from '@dxos/echo-schema';
|
|
10
10
|
import { invariant } from '@dxos/invariant';
|
|
11
|
+
import { isReactiveObject, makeRef } from '@dxos/live-object';
|
|
11
12
|
import { PublicKey } from '@dxos/react-client';
|
|
12
|
-
import {
|
|
13
|
-
cellClassesForFieldType,
|
|
14
|
-
cellClassesForRowSelection,
|
|
15
|
-
formatForDisplay,
|
|
16
|
-
formatForEditing,
|
|
17
|
-
parseValue,
|
|
18
|
-
} from '@dxos/react-ui-form';
|
|
13
|
+
import { formatForDisplay, formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
19
14
|
import {
|
|
20
15
|
type DxGridAxisMeta,
|
|
21
|
-
type DxGridCellValue,
|
|
22
|
-
type DxGridPlane,
|
|
23
|
-
type DxGridPlaneCells,
|
|
24
16
|
type DxGridPlaneRange,
|
|
25
17
|
type DxGridPlanePosition,
|
|
26
|
-
toPlaneCellIndex,
|
|
27
18
|
type DxGridPosition,
|
|
28
19
|
} from '@dxos/react-ui-grid';
|
|
29
|
-
import {
|
|
30
|
-
import { VIEW_FIELD_LIMIT, type ViewProjection, type FieldType } from '@dxos/schema';
|
|
20
|
+
import { type ViewProjection } from '@dxos/schema';
|
|
31
21
|
|
|
32
|
-
import { ModalController } from './modal-controller';
|
|
33
22
|
import { SelectionModel } from './selection-model';
|
|
34
23
|
import { type TableType } from '../types';
|
|
35
|
-
import {
|
|
36
|
-
import { tableControls } from '../util/table-controls';
|
|
24
|
+
import { touch } from '../util';
|
|
37
25
|
|
|
38
26
|
export type SortDirection = 'asc' | 'desc';
|
|
39
27
|
export type SortConfig = { fieldId: string; direction: SortDirection };
|
|
@@ -84,7 +72,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
84
72
|
|
|
85
73
|
private _selection!: SelectionModel<T>;
|
|
86
74
|
private _columnMeta?: ReadonlySignal<DxGridAxisMeta>;
|
|
87
|
-
private readonly _modalController = new ModalController();
|
|
88
75
|
|
|
89
76
|
constructor({
|
|
90
77
|
table,
|
|
@@ -118,6 +105,10 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
118
105
|
return this._projection;
|
|
119
106
|
}
|
|
120
107
|
|
|
108
|
+
public get rows(): ReadonlySignal<T[]> {
|
|
109
|
+
return this._sortedRows;
|
|
110
|
+
}
|
|
111
|
+
|
|
121
112
|
public get pinnedRows(): NonNullable<TableModelProps<T>['pinnedRows']> {
|
|
122
113
|
return this._pinnedRows;
|
|
123
114
|
}
|
|
@@ -131,10 +122,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
131
122
|
return this._sorting.value;
|
|
132
123
|
}
|
|
133
124
|
|
|
134
|
-
public get modalController() {
|
|
135
|
-
return this._modalController;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
125
|
public get selection() {
|
|
139
126
|
return this._selection;
|
|
140
127
|
}
|
|
@@ -153,7 +140,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
153
140
|
|
|
154
141
|
private initializeColumnMeta(): void {
|
|
155
142
|
this._columnMeta = computed(() => {
|
|
156
|
-
const fields = this._table.view?.fields ?? [];
|
|
143
|
+
const fields = this._table.view?.target?.fields ?? [];
|
|
157
144
|
const meta = Object.fromEntries(
|
|
158
145
|
fields.map((field, index: number) => [index, { size: field?.size ?? 256, resizeable: true }]),
|
|
159
146
|
);
|
|
@@ -174,15 +161,35 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
174
161
|
return this._rows.value;
|
|
175
162
|
}
|
|
176
163
|
|
|
177
|
-
const field = this._table.view?.fields.find((field) => field.id === sort.fieldId);
|
|
164
|
+
const field = this._table.view?.target?.fields.find((field) => field.id === sort.fieldId);
|
|
178
165
|
if (!field) {
|
|
179
166
|
return this._rows.value;
|
|
180
167
|
}
|
|
181
168
|
|
|
169
|
+
const { props } = this._projection.getFieldProjection(field.id);
|
|
170
|
+
|
|
182
171
|
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
183
|
-
const
|
|
184
|
-
|
|
172
|
+
const rawValue = getValue(item, field.path);
|
|
173
|
+
let sortValue = rawValue;
|
|
174
|
+
|
|
175
|
+
if (props.type.startsWith('boolean')) {
|
|
176
|
+
sortValue = !!rawValue; // Coerce null/undefined/false to false for booleans
|
|
177
|
+
} else if (rawValue != null) {
|
|
178
|
+
if (props.format === FormatEnum.Ref && field.referencePath) {
|
|
179
|
+
sortValue = getValue(rawValue, field.referencePath);
|
|
180
|
+
} else {
|
|
181
|
+
sortValue = formatForDisplay({ type: props.type, format: props.format, value: rawValue });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
item,
|
|
187
|
+
index,
|
|
188
|
+
value: sortValue,
|
|
189
|
+
isEmpty: !props.type.startsWith('boolean') && (sortValue == null || sortValue === ''),
|
|
190
|
+
};
|
|
185
191
|
});
|
|
192
|
+
|
|
186
193
|
const sorted = orderBy(dataWithIndices, ['isEmpty', 'value'], ['asc', sort.direction]);
|
|
187
194
|
|
|
188
195
|
for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
|
|
@@ -215,7 +222,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
215
222
|
rowEffects.push(
|
|
216
223
|
effect(() => {
|
|
217
224
|
const obj = this._sortedRows.value[row];
|
|
218
|
-
this?._table?.view?.fields.forEach((field) => touch(getValue(obj, field.path)));
|
|
225
|
+
this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
|
|
219
226
|
this._onCellUpdate?.({ row, col: start.col, plane: 'grid' });
|
|
220
227
|
}),
|
|
221
228
|
);
|
|
@@ -226,181 +233,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
226
233
|
this._ctx.onDispose(rowEffectManager);
|
|
227
234
|
}
|
|
228
235
|
|
|
229
|
-
//
|
|
230
|
-
// Get Cells
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
public getCells = (range: DxGridPlaneRange, plane: DxGridPlane): DxGridPlaneCells => {
|
|
234
|
-
switch (plane) {
|
|
235
|
-
case 'grid': {
|
|
236
|
-
this._visibleRange.value = range;
|
|
237
|
-
return this.getMainGridCells(range);
|
|
238
|
-
}
|
|
239
|
-
case 'frozenRowsStart': {
|
|
240
|
-
return this.getHeaderCells(range);
|
|
241
|
-
}
|
|
242
|
-
case 'frozenColsStart': {
|
|
243
|
-
return this.getSelectionColumnCells(range);
|
|
244
|
-
}
|
|
245
|
-
case 'frozenColsEnd': {
|
|
246
|
-
return this.getActionColumnCells(range);
|
|
247
|
-
}
|
|
248
|
-
case 'fixedStartStart': {
|
|
249
|
-
return this.getSelectAllCell();
|
|
250
|
-
}
|
|
251
|
-
case 'fixedStartEnd': {
|
|
252
|
-
return this.getNewColumnCell();
|
|
253
|
-
}
|
|
254
|
-
default: {
|
|
255
|
-
return {};
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
private getMainGridCells = (range: DxGridPlaneRange): DxGridPlaneCells => {
|
|
261
|
-
const cells: DxGridPlaneCells = {};
|
|
262
|
-
const fields = this._table.view?.fields ?? [];
|
|
263
|
-
|
|
264
|
-
const addCell = (obj: T, field: FieldType, colIndex: number, displayIndex: number): void => {
|
|
265
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
266
|
-
|
|
267
|
-
const cell: DxGridCellValue = {
|
|
268
|
-
get value() {
|
|
269
|
-
const value = getValue(obj, field.path);
|
|
270
|
-
if (value == null) {
|
|
271
|
-
return '';
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
switch (props.format) {
|
|
275
|
-
case FormatEnum.Ref: {
|
|
276
|
-
if (!field.referencePath) {
|
|
277
|
-
return ''; // TODO(burdon): Show error.
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return getValue(value, field.referencePath);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
default: {
|
|
284
|
-
return formatForDisplay({ type: props.type, format: props.format, value });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
},
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
const classes = [];
|
|
291
|
-
const formatClasses = cellClassesForFieldType({ type: props.type, format: props.format });
|
|
292
|
-
if (formatClasses) {
|
|
293
|
-
classes.push(formatClasses);
|
|
294
|
-
}
|
|
295
|
-
const rowSelectionClasses = cellClassesForRowSelection(this._selection.isObjectSelected(obj));
|
|
296
|
-
if (rowSelectionClasses) {
|
|
297
|
-
classes.push(rowSelectionClasses);
|
|
298
|
-
}
|
|
299
|
-
if (classes.length > 0) {
|
|
300
|
-
cell.className = mx(classes.flat());
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (cell.value && props.format === FormatEnum.Ref && props.referenceSchema) {
|
|
304
|
-
const targetObj = getValue(obj, field.path);
|
|
305
|
-
cell.accessoryHtml = tableButtons.referencedCell.render({
|
|
306
|
-
targetId: targetObj.id,
|
|
307
|
-
schemaId: props.referenceSchema,
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const idx = toPlaneCellIndex({ col: colIndex, row: displayIndex });
|
|
312
|
-
cells[idx] = cell;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
for (let row = range.start.row; row <= range.end.row && row < this._sortedRows.value.length; row++) {
|
|
316
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
317
|
-
const field = fields[col];
|
|
318
|
-
if (!field) {
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
addCell(this._sortedRows.value[row], field, col, row);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return cells;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
private getHeaderCells = (range: DxGridPlaneRange): DxGridPlaneCells => {
|
|
330
|
-
const cells: DxGridPlaneCells = {};
|
|
331
|
-
const fields = this.table.view?.fields ?? [];
|
|
332
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
333
|
-
const { field, props } = this._projection.getFieldProjection(fields[col].id);
|
|
334
|
-
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
335
|
-
// TODO(burdon): Use same logic as form for fallback title.
|
|
336
|
-
value: props.title ?? field.path,
|
|
337
|
-
readonly: true,
|
|
338
|
-
resizeHandle: 'col',
|
|
339
|
-
accessoryHtml: tableButtons.columnSettings.render({ fieldId: field.id }),
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return cells;
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
private getSelectionColumnCells = (range: DxGridPlaneRange): DxGridPlaneCells => {
|
|
347
|
-
const cells: DxGridPlaneCells = {};
|
|
348
|
-
for (let row = range.start.row; row <= range.end.row && row < this._rows.value.length; row++) {
|
|
349
|
-
const isSelected = this._selection.isRowIndexSelected(row);
|
|
350
|
-
const classes = cellClassesForRowSelection(isSelected);
|
|
351
|
-
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
352
|
-
value: '',
|
|
353
|
-
readonly: true,
|
|
354
|
-
className: classes ? mx(classes) : undefined,
|
|
355
|
-
accessoryHtml: tableControls.checkbox.render({ rowIndex: row, checked: isSelected }),
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return cells;
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
private getActionColumnCells = (range: DxGridPlaneRange): DxGridPlaneCells => {
|
|
363
|
-
const cells: DxGridPlaneCells = {};
|
|
364
|
-
for (let row = range.start.row; row <= range.end.row && row < this._rows.value.length; row++) {
|
|
365
|
-
const isSelected = this._selection.isRowIndexSelected(row);
|
|
366
|
-
const classes = cellClassesForRowSelection(isSelected);
|
|
367
|
-
cells[toPlaneCellIndex({ col: 0, row })] = {
|
|
368
|
-
value: '',
|
|
369
|
-
readonly: true,
|
|
370
|
-
className: classes ? mx(classes) : undefined,
|
|
371
|
-
accessoryHtml: tableButtons.rowMenu.render({ rowIndex: row }),
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return cells;
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
private getSelectAllCell = (): DxGridPlaneCells => {
|
|
379
|
-
return {
|
|
380
|
-
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
381
|
-
value: '',
|
|
382
|
-
accessoryHtml: tableControls.checkbox.render({
|
|
383
|
-
rowIndex: 0,
|
|
384
|
-
header: true,
|
|
385
|
-
checked: this._selection.allRowsSeleted.value,
|
|
386
|
-
}),
|
|
387
|
-
readonly: true,
|
|
388
|
-
},
|
|
389
|
-
};
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
private getNewColumnCell = (): DxGridPlaneCells => {
|
|
393
|
-
return {
|
|
394
|
-
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
395
|
-
value: '',
|
|
396
|
-
accessoryHtml: tableButtons.addColumn.render({
|
|
397
|
-
disabled: (this._table.view?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
398
|
-
}),
|
|
399
|
-
readonly: true,
|
|
400
|
-
},
|
|
401
|
-
};
|
|
402
|
-
};
|
|
403
|
-
|
|
404
236
|
//
|
|
405
237
|
// Data
|
|
406
238
|
//
|
|
@@ -411,7 +243,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
411
243
|
|
|
412
244
|
public getRowCount = (): number => this._rows.value.length;
|
|
413
245
|
|
|
414
|
-
public getColumnCount = (): number => this.table.view?.fields.length ?? 0;
|
|
246
|
+
public getColumnCount = (): number => this.table.view?.target?.fields.length ?? 0;
|
|
415
247
|
|
|
416
248
|
public insertRow = (rowIndex?: number): void => {
|
|
417
249
|
const row = rowIndex !== undefined ? this._displayToDataIndex.get(rowIndex) ?? rowIndex : this._rows.value.length;
|
|
@@ -434,7 +266,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
434
266
|
};
|
|
435
267
|
|
|
436
268
|
public getCellData = ({ col, row }: DxGridPlanePosition): any => {
|
|
437
|
-
const fields = this.table.view?.fields ?? [];
|
|
269
|
+
const fields = this.table.view?.target?.fields ?? [];
|
|
438
270
|
if (col < 0 || col >= fields.length) {
|
|
439
271
|
return undefined;
|
|
440
272
|
}
|
|
@@ -453,7 +285,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
453
285
|
return ''; // TODO(burdon): Show error.
|
|
454
286
|
}
|
|
455
287
|
|
|
456
|
-
return getValue(value, field.referencePath);
|
|
288
|
+
return getValue(value.target, field.referencePath);
|
|
457
289
|
}
|
|
458
290
|
|
|
459
291
|
default: {
|
|
@@ -464,7 +296,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
464
296
|
|
|
465
297
|
public setCellData = ({ col, row }: DxGridPlanePosition, value: any): void => {
|
|
466
298
|
const rowIdx = this._displayToDataIndex.get(row) ?? row;
|
|
467
|
-
const fields = this.table.view?.fields ?? [];
|
|
299
|
+
const fields = this.table.view?.target?.fields ?? [];
|
|
468
300
|
if (col < 0 || col >= fields.length) {
|
|
469
301
|
return;
|
|
470
302
|
}
|
|
@@ -473,7 +305,11 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
473
305
|
const { props } = this._projection.getFieldProjection(field.id);
|
|
474
306
|
switch (props.format) {
|
|
475
307
|
case FormatEnum.Ref: {
|
|
476
|
-
|
|
308
|
+
// TODO(ZaymonFC): This get's called an additional time by the cell editor onBlur, but with the cell editors
|
|
309
|
+
// plain string value. Maybe onBlur should be called with the actual value?
|
|
310
|
+
if (isReactiveObject(value)) {
|
|
311
|
+
setValue(this._rows.value[rowIdx], field.path, makeRef(value));
|
|
312
|
+
}
|
|
477
313
|
break;
|
|
478
314
|
}
|
|
479
315
|
|
|
@@ -492,6 +328,21 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
492
328
|
}
|
|
493
329
|
};
|
|
494
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Updates a cell's value using a transform function.
|
|
333
|
+
* @param {DxGridPlanePosition} position - The position of the cell to update.
|
|
334
|
+
* @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
|
|
335
|
+
*/
|
|
336
|
+
public updateCellData({ col, row }: DxGridPlanePosition, update: (value: any) => any): void {
|
|
337
|
+
const dataRow = this._displayToDataIndex.get(row) ?? row;
|
|
338
|
+
const fields = this.table.view?.target?.fields ?? [];
|
|
339
|
+
const field = fields[col];
|
|
340
|
+
|
|
341
|
+
const value = getValue(this._rows.value[dataRow], field.path);
|
|
342
|
+
const updatedValue = update(value);
|
|
343
|
+
setValue(this._rows.value[dataRow], field.path, updatedValue);
|
|
344
|
+
}
|
|
345
|
+
|
|
495
346
|
//
|
|
496
347
|
// Column Operations
|
|
497
348
|
//
|
|
@@ -501,7 +352,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
501
352
|
return;
|
|
502
353
|
}
|
|
503
354
|
|
|
504
|
-
const field = this.table.view.fields.find((field) => field.id === fieldId);
|
|
355
|
+
const field = this.table.view.target?.fields.find((field) => field.id === fieldId);
|
|
505
356
|
if (field && this._onDeleteColumn) {
|
|
506
357
|
if (this._sorting.value?.fieldId === fieldId) {
|
|
507
358
|
this.clearSort();
|
|
@@ -510,36 +361,12 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
510
361
|
}
|
|
511
362
|
}
|
|
512
363
|
|
|
513
|
-
//
|
|
514
|
-
// Interactions
|
|
515
|
-
//
|
|
516
|
-
public handleGridClick = (event: React.MouseEvent): void => {
|
|
517
|
-
if (!this._modalController.handleClick(event)) {
|
|
518
|
-
const target = event.target as HTMLElement;
|
|
519
|
-
|
|
520
|
-
const selectionCheckbox = target.closest(`input[${tableControls.checkbox.attributes.checkbox}]`);
|
|
521
|
-
if (selectionCheckbox) {
|
|
522
|
-
const isHeader = selectionCheckbox.hasAttribute(tableControls.checkbox.attributes.header);
|
|
523
|
-
if (isHeader) {
|
|
524
|
-
if (this._selection.allRowsSeleted.value) {
|
|
525
|
-
this._selection.setSelection('none');
|
|
526
|
-
} else {
|
|
527
|
-
this._selection.setSelection('all');
|
|
528
|
-
}
|
|
529
|
-
} else {
|
|
530
|
-
const rowIndex = Number(selectionCheckbox.getAttribute(tableControls.checkbox.attributes.checkbox));
|
|
531
|
-
this._selection.toggleSelectionForRowIndex(rowIndex);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
|
|
537
364
|
//
|
|
538
365
|
// Resize
|
|
539
366
|
//
|
|
540
367
|
|
|
541
368
|
public setColumnWidth(columnIndex: number, width: number): void {
|
|
542
|
-
const fields = this.table.view?.fields ?? [];
|
|
369
|
+
const fields = this.table.view?.target?.fields ?? [];
|
|
543
370
|
if (columnIndex < fields.length) {
|
|
544
371
|
const newWidth = Math.max(0, width);
|
|
545
372
|
const field = fields[columnIndex];
|