@dxos/react-ui-table 0.9.1-staging.ee54ba693a → 0.11.0
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/chunk-types.mjs +65 -0
- package/dist/lib/chunk-types.mjs.map +1 -0
- package/dist/lib/chunk-useTableModel.mjs +1595 -0
- package/dist/lib/chunk-useTableModel.mjs.map +1 -0
- package/dist/lib/index.mjs +1133 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing.mjs +88 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +30 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/types/src/components/Table/DynamicTable.d.ts +4 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +1 -0
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +3 -9
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +2 -0
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContext.d.ts +10 -0
- package/dist/types/src/components/Table/TableContext.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +1 -1
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/Table/index.d.ts +1 -0
- package/dist/types/src/components/Table/index.d.ts.map +1 -1
- 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 +1 -1
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +8 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +1 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +42 -7
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +20 -5
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-view-state.d.ts +5 -1
- package/dist/types/src/model/table-view-state.d.ts.map +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +1 -1
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts +5 -3
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +48 -50
- package/src/components/Table/DynamicTable.stories.tsx +59 -1
- package/src/components/Table/DynamicTable.tsx +16 -8
- package/src/components/Table/Relations.stories.tsx +2 -2
- package/src/components/Table/Table.stories.tsx +76 -2
- package/src/components/Table/Table.tsx +3 -15
- package/src/components/Table/TableContent.tsx +6 -5
- package/src/components/Table/TableContext.ts +21 -0
- package/src/components/Table/index.ts +2 -0
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +2 -3
- package/src/components/TableCellEditor/TableCellEditor.tsx +27 -22
- package/src/components/testing.ts +1 -1
- package/src/hooks/useProjectionModel.ts +1 -1
- package/src/hooks/useTableModel.ts +18 -6
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +1 -1
- package/src/model/table-model.test.ts +3 -3
- package/src/model/table-model.ts +148 -31
- package/src/model/table-presentation.test.ts +59 -1
- package/src/model/table-presentation.ts +171 -81
- package/src/model/table-view-state.ts +2 -2
- package/src/testing/useTestTableModel.ts +1 -1
- package/src/types/index.ts +4 -0
- package/src/util/dynamic-table.test.ts +1 -1
- package/src/util/dynamic-table.ts +1 -1
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +18 -10
- package/dist/lib/browser/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/browser/chunk-K4J2CKTZ.mjs +0 -1751
- package/dist/lib/browser/chunk-K4J2CKTZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-VV3P3ADH.mjs +0 -67
- package/dist/lib/browser/chunk-VV3P3ADH.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -1416
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs +0 -115
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/browser/translations.mjs +0 -39
- package/dist/lib/browser/translations.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -8
- package/dist/lib/browser/types/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-E6V274ZW.mjs +0 -1753
- package/dist/lib/node-esm/chunk-E6V274ZW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RPEWNOZZ.mjs +0 -68
- package/dist/lib/node-esm/chunk-RPEWNOZZ.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -1417
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -116
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/translations.mjs +0 -40
- package/dist/lib/node-esm/translations.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -9
- package/dist/lib/node-esm/types/index.mjs.map +0 -7
|
@@ -1,1751 +0,0 @@
|
|
|
1
|
-
// src/util/query-ast.ts
|
|
2
|
-
var extractOrder = (queryAst) => {
|
|
3
|
-
if (queryAst.type === "order") {
|
|
4
|
-
return queryAst.order;
|
|
5
|
-
}
|
|
6
|
-
if (queryAst.type === "options") {
|
|
7
|
-
return extractOrder(queryAst.query);
|
|
8
|
-
}
|
|
9
|
-
if (queryAst.type === "from") {
|
|
10
|
-
return extractOrder(queryAst.query);
|
|
11
|
-
}
|
|
12
|
-
return void 0;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// src/util/sort.ts
|
|
16
|
-
import { Format } from "@dxos/echo";
|
|
17
|
-
var getSortValue = (value, format) => {
|
|
18
|
-
if (value === void 0 || value === null) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
const isDateFormat = format === Format.TypeFormat.DateTime || format === Format.TypeFormat.Date;
|
|
22
|
-
if (isDateFormat) {
|
|
23
|
-
const date = new Date(value);
|
|
24
|
-
if (!isNaN(date.getTime())) {
|
|
25
|
-
return date.getTime();
|
|
26
|
-
}
|
|
27
|
-
return String(value);
|
|
28
|
-
}
|
|
29
|
-
if (format === Format.TypeFormat.Number || format === Format.TypeFormat.Integer || format === Format.TypeFormat.Currency || format === Format.TypeFormat.Percent || format === Format.TypeFormat.Duration) {
|
|
30
|
-
const num = typeof value === "number" ? value : Number(value);
|
|
31
|
-
if (!isNaN(num)) {
|
|
32
|
-
return num;
|
|
33
|
-
}
|
|
34
|
-
return String(value);
|
|
35
|
-
}
|
|
36
|
-
if (typeof value === "string") {
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
if (typeof value === "number") {
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
return String(value);
|
|
43
|
-
};
|
|
44
|
-
var compareValues = (aValue, bValue, format, direction) => {
|
|
45
|
-
if (aValue === void 0 || aValue === null) {
|
|
46
|
-
return bValue === void 0 || bValue === null ? 0 : 1;
|
|
47
|
-
}
|
|
48
|
-
if (bValue === void 0 || bValue === null) {
|
|
49
|
-
return -1;
|
|
50
|
-
}
|
|
51
|
-
const aSortValue = getSortValue(aValue, format);
|
|
52
|
-
const bSortValue = getSortValue(bValue, format);
|
|
53
|
-
let comparison = 0;
|
|
54
|
-
if (typeof aSortValue === "number" && typeof bSortValue === "number") {
|
|
55
|
-
comparison = aSortValue - bSortValue;
|
|
56
|
-
} else if (typeof aSortValue === "string" && typeof bSortValue === "string") {
|
|
57
|
-
comparison = aSortValue.localeCompare(bSortValue);
|
|
58
|
-
} else {
|
|
59
|
-
comparison = String(aSortValue).localeCompare(String(bSortValue));
|
|
60
|
-
}
|
|
61
|
-
return direction === "desc" ? -comparison : comparison;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// src/util/table-buttons.ts
|
|
65
|
-
var BUTTON_IDENTIFIERS = {
|
|
66
|
-
columnSettings: "data-table-column-settings-button",
|
|
67
|
-
newColumn: "data-table-new-column-button",
|
|
68
|
-
referencedCell: "data-table-ref-cell-button",
|
|
69
|
-
rowMenu: "data-table-row-menu-button",
|
|
70
|
-
saveDraftRow: "data-table-save-draft-row-button",
|
|
71
|
-
sort: "data-table-sort-button"
|
|
72
|
-
};
|
|
73
|
-
var createButton = ({ attr, data, disabled = false, icon, testId, type: _type = "primary" }) => {
|
|
74
|
-
const dataAttrs = Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
75
|
-
return `<button ${attr} data-testid="${testId}" class="dx-button w-6 aspect-square px-0.5 min-h-0" ${dataAttrs} ${disabled ? "disabled" : ""} data-dx-grid-action="accessory"><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
|
|
76
|
-
};
|
|
77
|
-
var addColumnButton = {
|
|
78
|
-
attr: BUTTON_IDENTIFIERS.newColumn,
|
|
79
|
-
icon: "ph--plus--regular",
|
|
80
|
-
render: ({ disabled }) => {
|
|
81
|
-
return createButton({
|
|
82
|
-
attr: BUTTON_IDENTIFIERS.newColumn,
|
|
83
|
-
icon: addColumnButton.icon,
|
|
84
|
-
disabled,
|
|
85
|
-
data: {},
|
|
86
|
-
testId: "table-new-column-button"
|
|
87
|
-
});
|
|
88
|
-
},
|
|
89
|
-
getData: (_el) => ({
|
|
90
|
-
type: "newColumn"
|
|
91
|
-
})
|
|
92
|
-
};
|
|
93
|
-
var columnSettingsButton = {
|
|
94
|
-
attr: BUTTON_IDENTIFIERS.columnSettings,
|
|
95
|
-
icon: "ph--caret-down--regular",
|
|
96
|
-
render: ({ fieldId }) => {
|
|
97
|
-
return createButton({
|
|
98
|
-
attr: BUTTON_IDENTIFIERS.columnSettings,
|
|
99
|
-
icon: columnSettingsButton.icon,
|
|
100
|
-
data: {
|
|
101
|
-
"data-field-id": fieldId,
|
|
102
|
-
"data-variant": "ghost"
|
|
103
|
-
},
|
|
104
|
-
testId: "table-column-settings-button"
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
getData: (el) => ({
|
|
108
|
-
type: "columnSettings",
|
|
109
|
-
fieldId: el.getAttribute("data-field-id")
|
|
110
|
-
})
|
|
111
|
-
};
|
|
112
|
-
var rowMenuButton = {
|
|
113
|
-
attr: BUTTON_IDENTIFIERS.rowMenu,
|
|
114
|
-
icon: "ph--dots-three--regular",
|
|
115
|
-
render: ({ rowIndex }) => {
|
|
116
|
-
return createButton({
|
|
117
|
-
attr: BUTTON_IDENTIFIERS.rowMenu,
|
|
118
|
-
icon: rowMenuButton.icon,
|
|
119
|
-
data: {
|
|
120
|
-
"data-row-index": rowIndex.toString()
|
|
121
|
-
},
|
|
122
|
-
testId: "table-row-menu-button"
|
|
123
|
-
});
|
|
124
|
-
},
|
|
125
|
-
getData: (el) => ({
|
|
126
|
-
type: "rowMenu",
|
|
127
|
-
rowIndex: Number(el.getAttribute("data-row-index"))
|
|
128
|
-
})
|
|
129
|
-
};
|
|
130
|
-
var sortButton = {
|
|
131
|
-
attr: BUTTON_IDENTIFIERS.sort,
|
|
132
|
-
icon: "ph--sort-ascending--regular",
|
|
133
|
-
render: ({ fieldId, direction }) => {
|
|
134
|
-
return createButton({
|
|
135
|
-
attr: BUTTON_IDENTIFIERS.sort,
|
|
136
|
-
icon: direction === "desc" ? "ph--sort-descending--regular" : "ph--sort-ascending--regular",
|
|
137
|
-
data: {
|
|
138
|
-
"data-field-id": fieldId,
|
|
139
|
-
"data-direction": direction ?? ""
|
|
140
|
-
},
|
|
141
|
-
testId: "table-sort-button",
|
|
142
|
-
// TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
|
|
143
|
-
// This is a bit hacky.
|
|
144
|
-
type: "secondary"
|
|
145
|
-
});
|
|
146
|
-
},
|
|
147
|
-
getData: (el) => ({
|
|
148
|
-
type: "sort",
|
|
149
|
-
fieldId: el.getAttribute("data-field-id"),
|
|
150
|
-
direction: el.getAttribute("data-direction")
|
|
151
|
-
})
|
|
152
|
-
};
|
|
153
|
-
var saveDraftRowButton = {
|
|
154
|
-
attr: BUTTON_IDENTIFIERS.saveDraftRow,
|
|
155
|
-
icon: "ph--floppy-disk--regular",
|
|
156
|
-
render: ({ rowIndex, disabled }) => {
|
|
157
|
-
return createButton({
|
|
158
|
-
attr: BUTTON_IDENTIFIERS.saveDraftRow,
|
|
159
|
-
icon: saveDraftRowButton.icon,
|
|
160
|
-
disabled,
|
|
161
|
-
data: {
|
|
162
|
-
"data-row-index": rowIndex.toString(),
|
|
163
|
-
"data-variant": "primary"
|
|
164
|
-
},
|
|
165
|
-
testId: "table-save-draft-row-button"
|
|
166
|
-
});
|
|
167
|
-
},
|
|
168
|
-
getData: (el) => ({
|
|
169
|
-
type: "saveDraftRow",
|
|
170
|
-
rowIndex: Number(el.getAttribute("data-row-index"))
|
|
171
|
-
})
|
|
172
|
-
};
|
|
173
|
-
var tableButtons = {
|
|
174
|
-
addColumn: addColumnButton,
|
|
175
|
-
columnSettings: columnSettingsButton,
|
|
176
|
-
rowMenu: rowMenuButton,
|
|
177
|
-
saveDraftRow: saveDraftRowButton,
|
|
178
|
-
sort: sortButton
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
// src/util/table-controls.ts
|
|
182
|
-
import { accessoryHandlesPointerdownAttrs } from "@dxos/lit-grid";
|
|
183
|
-
var CONTROL_IDENTIFIERS = {
|
|
184
|
-
checkbox: "data-table-checkbox",
|
|
185
|
-
switch: "data-table-switch"
|
|
186
|
-
};
|
|
187
|
-
var BASE_CLASSES = {
|
|
188
|
-
checkbox: "absolute inset-y-[.375rem] end-[.375rem] dx-checkbox",
|
|
189
|
-
switch: "absolute inset-y-[.25rem] end-[.25rem] dx-checkbox--switch"
|
|
190
|
-
};
|
|
191
|
-
var renderAttributes = (data) => {
|
|
192
|
-
return Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
193
|
-
};
|
|
194
|
-
var renderInput = (baseClass, attrs, checked = false, disabled = false, preventToggle = false, testId) => {
|
|
195
|
-
return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? "checked" : ""} ${preventToggle ? 'onclick="return false"' : ""} ${disabled ? "disabled" : ""} data-testid="${testId}" data-dx-grid-action="accessory"/>`;
|
|
196
|
-
};
|
|
197
|
-
var CheckboxStory = ({ rowIndex, header = false, checked = false, disabled = false }) => {
|
|
198
|
-
const attrs = {
|
|
199
|
-
[CONTROL_IDENTIFIERS.checkbox]: "",
|
|
200
|
-
"data-row-index": rowIndex.toString(),
|
|
201
|
-
...header && {
|
|
202
|
-
"data-header": ""
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
return renderInput(BASE_CLASSES.checkbox, attrs, checked, disabled, true, "table-selection");
|
|
206
|
-
};
|
|
207
|
-
var SwitchStory = ({ colIndex, rowIndex, checked = false, disabled = false }) => {
|
|
208
|
-
const attrs = {
|
|
209
|
-
[CONTROL_IDENTIFIERS.switch]: "",
|
|
210
|
-
"data-row-index": rowIndex.toString(),
|
|
211
|
-
"data-col-index": colIndex.toString(),
|
|
212
|
-
...accessoryHandlesPointerdownAttrs
|
|
213
|
-
};
|
|
214
|
-
return renderInput(BASE_CLASSES.switch, attrs, checked, disabled, true, "table-switch");
|
|
215
|
-
};
|
|
216
|
-
var tableControls = {
|
|
217
|
-
checkbox: {
|
|
218
|
-
attr: CONTROL_IDENTIFIERS.checkbox,
|
|
219
|
-
render: CheckboxStory,
|
|
220
|
-
getData: (el) => ({
|
|
221
|
-
type: "checkbox",
|
|
222
|
-
rowIndex: Number(el.getAttribute("data-row-index")),
|
|
223
|
-
header: el.hasAttribute("data-header")
|
|
224
|
-
})
|
|
225
|
-
},
|
|
226
|
-
switch: {
|
|
227
|
-
attr: CONTROL_IDENTIFIERS.switch,
|
|
228
|
-
render: SwitchStory,
|
|
229
|
-
getData: (el) => ({
|
|
230
|
-
type: "switch",
|
|
231
|
-
rowIndex: Number(el.getAttribute("data-row-index")),
|
|
232
|
-
colIndex: Number(el.getAttribute("data-col-index"))
|
|
233
|
-
})
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// src/model/table-view-state.ts
|
|
238
|
-
import * as Schema from "effect/Schema";
|
|
239
|
-
import { QueryAST } from "@dxos/echo";
|
|
240
|
-
import { defineViewState } from "@dxos/react-ui-attention";
|
|
241
|
-
var FieldSortSchema = Schema.Struct({
|
|
242
|
-
fieldId: Schema.String,
|
|
243
|
-
direction: QueryAST.OrderDirection
|
|
244
|
-
});
|
|
245
|
-
var tableSortAspect = defineViewState({
|
|
246
|
-
key: "table-sort",
|
|
247
|
-
backend: "local",
|
|
248
|
-
schema: Schema.UndefinedOr(FieldSortSchema),
|
|
249
|
-
defaultValue: () => void 0
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// src/model/table-model.ts
|
|
253
|
-
import { Atom as Atom2 } from "@effect-atom/atom-react";
|
|
254
|
-
import { Resource as Resource2 } from "@dxos/context";
|
|
255
|
-
import { Format as Format2, Obj, Order, Query, Ref, Type } from "@dxos/echo";
|
|
256
|
-
import { toEffectSchema } from "@dxos/echo/JsonSchema";
|
|
257
|
-
import { getSnapshot } from "@dxos/echo/Obj";
|
|
258
|
-
import { SchemaEx } from "@dxos/effect";
|
|
259
|
-
import { invariant } from "@dxos/invariant";
|
|
260
|
-
import { EntityId } from "@dxos/keys";
|
|
261
|
-
import { formatForEditing, parseValue } from "@dxos/react-ui-form";
|
|
262
|
-
import { validateSchema } from "@dxos/schema";
|
|
263
|
-
|
|
264
|
-
// src/util/tag.ts
|
|
265
|
-
var TAG_PATTERN_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
266
|
-
var extractTagIds = (value) => {
|
|
267
|
-
if (typeof value !== "string") {
|
|
268
|
-
return void 0;
|
|
269
|
-
}
|
|
270
|
-
const ids = [];
|
|
271
|
-
let match;
|
|
272
|
-
while ((match = TAG_PATTERN_REGEX.exec(value)) !== null) {
|
|
273
|
-
if (match[2]) {
|
|
274
|
-
ids.push(match[2]);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return ids.length > 0 ? ids : void 0;
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// src/model/selection-model.ts
|
|
281
|
-
import { Atom } from "@effect-atom/atom-react";
|
|
282
|
-
import { Resource } from "@dxos/context";
|
|
283
|
-
var SelectionModel = class extends Resource {
|
|
284
|
-
_registry;
|
|
285
|
-
_rows;
|
|
286
|
-
_selection;
|
|
287
|
-
_validSelectedIds;
|
|
288
|
-
_hasSelection;
|
|
289
|
-
_allSelected;
|
|
290
|
-
_selectionMode;
|
|
291
|
-
_onSelectionChanged;
|
|
292
|
-
constructor(registry, rows, selectionMode, initialSelection, onSelectionChanged) {
|
|
293
|
-
super();
|
|
294
|
-
this._registry = registry;
|
|
295
|
-
this._rows = rows;
|
|
296
|
-
this._selectionMode = selectionMode;
|
|
297
|
-
this._onSelectionChanged = onSelectionChanged;
|
|
298
|
-
this._selection = Atom.make(new Set(initialSelection));
|
|
299
|
-
this._validSelectedIds = Atom.make((get) => {
|
|
300
|
-
const rows2 = get(this._rows);
|
|
301
|
-
if (!rows2) {
|
|
302
|
-
return /* @__PURE__ */ new Set();
|
|
303
|
-
}
|
|
304
|
-
const validIds = new Set(rows2.map((row) => row.id));
|
|
305
|
-
const selection = get(this._selection);
|
|
306
|
-
return new Set([
|
|
307
|
-
...selection
|
|
308
|
-
].filter((id) => validIds.has(id)));
|
|
309
|
-
});
|
|
310
|
-
this._hasSelection = Atom.make((get) => get(this._validSelectedIds).size > 0);
|
|
311
|
-
this._allSelected = Atom.make((get) => {
|
|
312
|
-
const rows2 = get(this._rows);
|
|
313
|
-
if (rows2.length === 0) {
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
|
-
const selection = get(this._selection);
|
|
317
|
-
return rows2.every((row) => selection.has(row.id));
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
async _open() {
|
|
321
|
-
const selectionUnsubscribe = this._registry.subscribe(this._selection, () => {
|
|
322
|
-
this._onSelectionChanged?.();
|
|
323
|
-
});
|
|
324
|
-
this._ctx.onDispose(selectionUnsubscribe);
|
|
325
|
-
}
|
|
326
|
-
//
|
|
327
|
-
// Getters
|
|
328
|
-
//
|
|
329
|
-
/** Get the selection atom for reactive access. */
|
|
330
|
-
get selectionAtom() {
|
|
331
|
-
return this._selection;
|
|
332
|
-
}
|
|
333
|
-
/** Get the current selection value. */
|
|
334
|
-
get selection() {
|
|
335
|
-
return this._registry.get(this._selection);
|
|
336
|
-
}
|
|
337
|
-
/** Get the hasSelection atom for reactive access. */
|
|
338
|
-
get hasSelectionAtom() {
|
|
339
|
-
return this._hasSelection;
|
|
340
|
-
}
|
|
341
|
-
/** Check if there is any selection. */
|
|
342
|
-
get hasSelection() {
|
|
343
|
-
return this._registry.get(this._hasSelection);
|
|
344
|
-
}
|
|
345
|
-
/** Get the allRowsSelected atom for reactive access. */
|
|
346
|
-
get allRowsSelectedAtom() {
|
|
347
|
-
return this._allSelected;
|
|
348
|
-
}
|
|
349
|
-
/** Check if all rows are selected. */
|
|
350
|
-
get allRowsSelected() {
|
|
351
|
-
return this._registry.get(this._allSelected);
|
|
352
|
-
}
|
|
353
|
-
get selectionMode() {
|
|
354
|
-
return this._selectionMode;
|
|
355
|
-
}
|
|
356
|
-
getSelectedRows = () => {
|
|
357
|
-
const selectedIds = this._registry.get(this._validSelectedIds);
|
|
358
|
-
return this._registry.get(this._rows).filter((row) => selectedIds.has(row.id));
|
|
359
|
-
};
|
|
360
|
-
isRowIndexSelected = (rowIndex) => {
|
|
361
|
-
const row = this._registry.get(this._rows)[rowIndex];
|
|
362
|
-
return this._registry.get(this._selection).has(row.id);
|
|
363
|
-
};
|
|
364
|
-
isObjectSelected = (object) => {
|
|
365
|
-
return this._registry.get(this._selection).has(object.id);
|
|
366
|
-
};
|
|
367
|
-
//
|
|
368
|
-
// Manipulation
|
|
369
|
-
//
|
|
370
|
-
toggleSelectionForRowIndex = (rowIndex) => {
|
|
371
|
-
const row = this._registry.get(this._rows)[rowIndex];
|
|
372
|
-
const currentSelection = this._registry.get(this._selection);
|
|
373
|
-
const newSelection = new Set(currentSelection);
|
|
374
|
-
if (newSelection.has(row.id)) {
|
|
375
|
-
newSelection.delete(row.id);
|
|
376
|
-
} else {
|
|
377
|
-
if (this._selectionMode === "single") {
|
|
378
|
-
newSelection.clear();
|
|
379
|
-
}
|
|
380
|
-
newSelection.add(row.id);
|
|
381
|
-
}
|
|
382
|
-
this._registry.set(this._selection, newSelection);
|
|
383
|
-
};
|
|
384
|
-
setSelection = (mode) => {
|
|
385
|
-
switch (mode) {
|
|
386
|
-
case "all": {
|
|
387
|
-
this._registry.set(this._selection, new Set(this._registry.get(this._rows).map((row) => row.id)));
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
case "none": {
|
|
391
|
-
this._registry.set(this._selection, /* @__PURE__ */ new Set());
|
|
392
|
-
break;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
// src/model/table-model.ts
|
|
399
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
|
|
400
|
-
var createEchoChangeCallback = (table) => ({
|
|
401
|
-
table: (mutate) => Obj.update(table, (table2) => mutate(table2)),
|
|
402
|
-
row: (row, mutate) => {
|
|
403
|
-
if (Obj.isObject(row)) {
|
|
404
|
-
Obj.update(row, (row2) => mutate(row2));
|
|
405
|
-
} else {
|
|
406
|
-
mutate(row);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
var createDirectChangeCallback = (table) => ({
|
|
411
|
-
table: (mutate) => mutate(table),
|
|
412
|
-
row: (row, mutate) => mutate(row)
|
|
413
|
-
});
|
|
414
|
-
var defaultFeatures = {
|
|
415
|
-
selection: {
|
|
416
|
-
enabled: true,
|
|
417
|
-
mode: "multiple"
|
|
418
|
-
},
|
|
419
|
-
dataEditable: false,
|
|
420
|
-
schemaEditable: false
|
|
421
|
-
};
|
|
422
|
-
var TableModel = class extends Resource2 {
|
|
423
|
-
_registry;
|
|
424
|
-
_object;
|
|
425
|
-
_projection;
|
|
426
|
-
_db;
|
|
427
|
-
_change;
|
|
428
|
-
_visibleRange;
|
|
429
|
-
_onInsertRow;
|
|
430
|
-
_onDeleteRows;
|
|
431
|
-
_onColumnDelete;
|
|
432
|
-
_onCellUpdate;
|
|
433
|
-
_onRowOrderChange;
|
|
434
|
-
_onRowAction;
|
|
435
|
-
_rowActions;
|
|
436
|
-
_features;
|
|
437
|
-
_rows;
|
|
438
|
-
_draftRows;
|
|
439
|
-
// Optional per-context UI state manager; when present the column sort is view-state-backed.
|
|
440
|
-
_viewState;
|
|
441
|
-
// Per-user sort state - backed by the `tableSortAspect` view state (keyed by table URI) when a
|
|
442
|
-
// ViewStateManager is provided, else an ephemeral atom. Changes are local until saved to the view.
|
|
443
|
-
_inMemorySort;
|
|
444
|
-
// Derived atom for persisted sort from view.query.ast.
|
|
445
|
-
_persistedSort;
|
|
446
|
-
// Derived atom for current sort (in-memory takes precedence over persisted).
|
|
447
|
-
_sorting;
|
|
448
|
-
// Derived atom for sorted rows (used by rows getter and SelectionModel).
|
|
449
|
-
_sortedRows;
|
|
450
|
-
// Derived atom for viewDirty state.
|
|
451
|
-
_viewDirty;
|
|
452
|
-
_pinnedRows;
|
|
453
|
-
_selection;
|
|
454
|
-
_columnMeta;
|
|
455
|
-
// Counter atom that increments when cells are updated - allows UI to react to cell changes.
|
|
456
|
-
_cellUpdateCounter;
|
|
457
|
-
constructor({ registry, object, projection, db, viewState, change, features = {}, initialSelection = [], pinnedRows = {
|
|
458
|
-
top: [],
|
|
459
|
-
bottom: []
|
|
460
|
-
}, rowActions = [], onCellUpdate, onColumnDelete, onDeleteRows, onInsertRow, onRowOrderChange, onRowAction }) {
|
|
461
|
-
super();
|
|
462
|
-
this._registry = registry;
|
|
463
|
-
this._object = object;
|
|
464
|
-
this._viewState = viewState;
|
|
465
|
-
this._projection = projection;
|
|
466
|
-
this._projection.normalizeView();
|
|
467
|
-
this._db = db;
|
|
468
|
-
this._change = change ?? createEchoChangeCallback(object);
|
|
469
|
-
this._features = {
|
|
470
|
-
...defaultFeatures,
|
|
471
|
-
...features
|
|
472
|
-
};
|
|
473
|
-
invariant(!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === "single"), "Single selection is not compatible with editable tables.", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 99, S: this, A: ["!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === 'single')", "'Single selection is not compatible with editable tables.'"] });
|
|
474
|
-
this._visibleRange = Atom2.make({
|
|
475
|
-
start: {
|
|
476
|
-
row: 0,
|
|
477
|
-
col: 0
|
|
478
|
-
},
|
|
479
|
-
end: {
|
|
480
|
-
row: 0,
|
|
481
|
-
col: 0
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
this._rows = Atom2.make([]);
|
|
485
|
-
this._draftRows = Atom2.make([]);
|
|
486
|
-
this._inMemorySort = this._viewState ? this._viewState.atom(tableSortAspect, this.id) : Atom2.make(void 0);
|
|
487
|
-
this._cellUpdateCounter = Atom2.make(0);
|
|
488
|
-
this._persistedSort = Atom2.make((get) => {
|
|
489
|
-
const view = get(this._object.view.atom);
|
|
490
|
-
if (!view) {
|
|
491
|
-
return void 0;
|
|
492
|
-
}
|
|
493
|
-
const viewSnapshot = Obj.getSnapshot(view);
|
|
494
|
-
const ast = viewSnapshot.query.ast;
|
|
495
|
-
const orders = extractOrder(ast);
|
|
496
|
-
if (orders && orders.length > 0) {
|
|
497
|
-
const firstOrder = orders[0];
|
|
498
|
-
if (firstOrder.kind === "property") {
|
|
499
|
-
const field = this._projection.getFields().find((f) => f.path === firstOrder.property);
|
|
500
|
-
if (field) {
|
|
501
|
-
return {
|
|
502
|
-
fieldId: field.id,
|
|
503
|
-
direction: firstOrder.direction
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return void 0;
|
|
509
|
-
});
|
|
510
|
-
this._sorting = Atom2.make((get) => {
|
|
511
|
-
const inMemorySort = get(this._inMemorySort);
|
|
512
|
-
return inMemorySort ?? get(this._persistedSort);
|
|
513
|
-
});
|
|
514
|
-
this._sortedRows = Atom2.make((get) => {
|
|
515
|
-
const rows = get(this._rows);
|
|
516
|
-
const sort = get(this._sorting);
|
|
517
|
-
if (!sort || rows.length === 0) {
|
|
518
|
-
return rows;
|
|
519
|
-
}
|
|
520
|
-
const field = this._projection.getFields().find((f) => f.id === sort.fieldId);
|
|
521
|
-
if (!field) {
|
|
522
|
-
return rows;
|
|
523
|
-
}
|
|
524
|
-
const fieldProjection = this._projection.getFieldProjection(field.id);
|
|
525
|
-
const sorted = [
|
|
526
|
-
...rows
|
|
527
|
-
].sort((a, b) => {
|
|
528
|
-
const aValue = SchemaEx.getValue(a, field.path);
|
|
529
|
-
const bValue = SchemaEx.getValue(b, field.path);
|
|
530
|
-
return compareValues(aValue, bValue, fieldProjection.props.format, sort.direction);
|
|
531
|
-
});
|
|
532
|
-
return sorted;
|
|
533
|
-
});
|
|
534
|
-
this._selection = new SelectionModel(this._registry, this._sortedRows, this._features.selection.mode ?? "multiple", initialSelection, () => this._onRowOrderChange?.());
|
|
535
|
-
this._viewDirty = Atom2.make((get) => {
|
|
536
|
-
const inMemorySort = get(this._inMemorySort);
|
|
537
|
-
if (!inMemorySort) {
|
|
538
|
-
return false;
|
|
539
|
-
}
|
|
540
|
-
const persisted = get(this._persistedSort);
|
|
541
|
-
if (!persisted) {
|
|
542
|
-
return true;
|
|
543
|
-
}
|
|
544
|
-
return inMemorySort.fieldId !== persisted.fieldId || inMemorySort.direction !== persisted.direction;
|
|
545
|
-
});
|
|
546
|
-
this._pinnedRows = pinnedRows;
|
|
547
|
-
this._rowActions = rowActions;
|
|
548
|
-
this._onInsertRow = onInsertRow;
|
|
549
|
-
this._onDeleteRows = onDeleteRows;
|
|
550
|
-
this._onColumnDelete = onColumnDelete;
|
|
551
|
-
this._onCellUpdate = onCellUpdate;
|
|
552
|
-
this._onRowOrderChange = onRowOrderChange;
|
|
553
|
-
this._onRowAction = onRowAction;
|
|
554
|
-
}
|
|
555
|
-
get id() {
|
|
556
|
-
return Obj.getURI(this._object);
|
|
557
|
-
}
|
|
558
|
-
get view() {
|
|
559
|
-
const view = this._object.view.target;
|
|
560
|
-
invariant(view, "Table model not initialized", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 197, S: this, A: ["view", "'Table model not initialized'"] });
|
|
561
|
-
return view;
|
|
562
|
-
}
|
|
563
|
-
get table() {
|
|
564
|
-
return this._object;
|
|
565
|
-
}
|
|
566
|
-
get projection() {
|
|
567
|
-
return this._projection;
|
|
568
|
-
}
|
|
569
|
-
get db() {
|
|
570
|
-
return this._db;
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Atom for reactive access to rows with in-memory sorting applied.
|
|
574
|
-
*/
|
|
575
|
-
get rows() {
|
|
576
|
-
return this._sortedRows;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Gets rows with in-memory sorting applied.
|
|
580
|
-
* In-memory sort is local to this instance until saved.
|
|
581
|
-
*/
|
|
582
|
-
getRows() {
|
|
583
|
-
return this._registry.get(this._sortedRows);
|
|
584
|
-
}
|
|
585
|
-
/** Atom for reactive access to cell changes. */
|
|
586
|
-
get cellUpdate() {
|
|
587
|
-
return this._cellUpdateCounter;
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Change a row using the configured change callback.
|
|
591
|
-
* Use this instead of directly mutating to ensure consistent mutation handling.
|
|
592
|
-
*/
|
|
593
|
-
changeRow(row, mutate) {
|
|
594
|
-
this._change.row(row, mutate);
|
|
595
|
-
}
|
|
596
|
-
get features() {
|
|
597
|
-
return this._features;
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* Gets the current sort state.
|
|
601
|
-
* Priority: in-memory sort (local changes) > persisted sort (from query AST).
|
|
602
|
-
*/
|
|
603
|
-
getSorting() {
|
|
604
|
-
return this._registry.get(this._sorting);
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Gets the row data at the specified display index.
|
|
608
|
-
*/
|
|
609
|
-
getRowAt(displayIndex) {
|
|
610
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
611
|
-
if (displayIndex < 0 || displayIndex >= sortedRows.length) {
|
|
612
|
-
return void 0;
|
|
613
|
-
}
|
|
614
|
-
return sortedRows[displayIndex];
|
|
615
|
-
}
|
|
616
|
-
get pinnedRows() {
|
|
617
|
-
return this._pinnedRows;
|
|
618
|
-
}
|
|
619
|
-
get columnMeta() {
|
|
620
|
-
invariant(this._columnMeta, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 251, S: this, A: ["this._columnMeta", ""] });
|
|
621
|
-
return this._columnMeta;
|
|
622
|
-
}
|
|
623
|
-
get selection() {
|
|
624
|
-
return this._selection;
|
|
625
|
-
}
|
|
626
|
-
get rowActions() {
|
|
627
|
-
return this._rowActions;
|
|
628
|
-
}
|
|
629
|
-
//
|
|
630
|
-
// Initialisation
|
|
631
|
-
//
|
|
632
|
-
async _open() {
|
|
633
|
-
await this._object.view.load();
|
|
634
|
-
this.initializeColumnMeta();
|
|
635
|
-
this.initializeEffects();
|
|
636
|
-
await this._selection.open(this._ctx);
|
|
637
|
-
}
|
|
638
|
-
initializeColumnMeta() {
|
|
639
|
-
invariant(this._object.view.target, "View must be loaded before initializing column meta", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 270, S: this, A: ["this._object.view.target", "'View must be loaded before initializing column meta'"] });
|
|
640
|
-
this._columnMeta = Atom2.make((get) => {
|
|
641
|
-
const fields = get(this._projection.fields);
|
|
642
|
-
const meta = Object.fromEntries(fields.map((field, index) => [
|
|
643
|
-
index,
|
|
644
|
-
{
|
|
645
|
-
size: this.table.sizes[field.path] ?? 256,
|
|
646
|
-
resizeable: true
|
|
647
|
-
}
|
|
648
|
-
]));
|
|
649
|
-
return {
|
|
650
|
-
grid: meta,
|
|
651
|
-
frozenColsStart: {
|
|
652
|
-
0: {
|
|
653
|
-
size: 30,
|
|
654
|
-
resizeable: false
|
|
655
|
-
}
|
|
656
|
-
},
|
|
657
|
-
frozenColsEnd: {
|
|
658
|
-
0: {
|
|
659
|
-
size: 32,
|
|
660
|
-
resizeable: false
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
initializeEffects() {
|
|
667
|
-
const rowsUnsubscribe = this._registry.subscribe(this._rows, () => {
|
|
668
|
-
this._onRowOrderChange?.();
|
|
669
|
-
});
|
|
670
|
-
this._ctx.onDispose(rowsUnsubscribe);
|
|
671
|
-
const rowSubscriptions = /* @__PURE__ */ new Map();
|
|
672
|
-
const visibleRangeUnsubscribe = this._registry.subscribe(this._visibleRange, () => {
|
|
673
|
-
for (const [id, unsub] of rowSubscriptions) {
|
|
674
|
-
unsub();
|
|
675
|
-
rowSubscriptions.delete(id);
|
|
676
|
-
}
|
|
677
|
-
const { start, end } = this._registry.get(this._visibleRange);
|
|
678
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
679
|
-
for (let row = start.row; row <= end.row && row < sortedRows.length; row++) {
|
|
680
|
-
const obj = sortedRows[row];
|
|
681
|
-
if (Obj.isObject(obj) && !rowSubscriptions.has(obj.id)) {
|
|
682
|
-
const unsub = Obj.subscribe(obj, () => {
|
|
683
|
-
this._registry.set(this._cellUpdateCounter, this._registry.get(this._cellUpdateCounter) + 1);
|
|
684
|
-
this._onCellUpdate?.({
|
|
685
|
-
row,
|
|
686
|
-
col: start.col,
|
|
687
|
-
plane: "grid"
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
rowSubscriptions.set(obj.id, unsub);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
this._ctx.onDispose(() => {
|
|
695
|
-
visibleRangeUnsubscribe();
|
|
696
|
-
for (const unsub of rowSubscriptions.values()) {
|
|
697
|
-
unsub();
|
|
698
|
-
}
|
|
699
|
-
});
|
|
700
|
-
const draftRowsUnsubscribe = this._registry.subscribe(this._draftRows, () => {
|
|
701
|
-
this._onRowOrderChange?.();
|
|
702
|
-
});
|
|
703
|
-
this._ctx.onDispose(draftRowsUnsubscribe);
|
|
704
|
-
}
|
|
705
|
-
//
|
|
706
|
-
// Data
|
|
707
|
-
//
|
|
708
|
-
setRows = (obj) => {
|
|
709
|
-
this._registry.set(this._rows, obj);
|
|
710
|
-
};
|
|
711
|
-
getRowCount = () => this._registry.get(this._sortedRows).length;
|
|
712
|
-
getDraftRowCount() {
|
|
713
|
-
return this._registry.get(this._draftRows).length;
|
|
714
|
-
}
|
|
715
|
-
/** Atom for reactive access to draft rows. */
|
|
716
|
-
get draftRows() {
|
|
717
|
-
return this._draftRows;
|
|
718
|
-
}
|
|
719
|
-
/** Gets the current draft rows. */
|
|
720
|
-
getDraftRows() {
|
|
721
|
-
return this._registry.get(this._draftRows);
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Checks if a specific field path has validation errors for a given draft row.
|
|
725
|
-
* @param draftRowIndex - The index of the draft row to check
|
|
726
|
-
* @param fieldPath - The path of the field to check for validation errors
|
|
727
|
-
* @returns true if the field has validation errors, false otherwise
|
|
728
|
-
*/
|
|
729
|
-
hasDraftRowValidationError(draftRowIndex, fieldPath) {
|
|
730
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._registry.get(this._draftRows).length) {
|
|
731
|
-
return false;
|
|
732
|
-
}
|
|
733
|
-
const draftRow = this._registry.get(this._draftRows)[draftRowIndex];
|
|
734
|
-
const validationErrors = draftRow.validationErrors || [];
|
|
735
|
-
return validationErrors.some((error) => error.path === fieldPath);
|
|
736
|
-
}
|
|
737
|
-
getColumnCount = () => this._projection?.getFields().length ?? 0;
|
|
738
|
-
insertRow = () => {
|
|
739
|
-
const result = this._onInsertRow?.();
|
|
740
|
-
if (result === "draft" && this._registry.get(this._draftRows).length === 0) {
|
|
741
|
-
this.createDraftRow();
|
|
742
|
-
}
|
|
743
|
-
return result ?? "final";
|
|
744
|
-
};
|
|
745
|
-
createDraftRow() {
|
|
746
|
-
if (!this._projection) {
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
const draftData = {
|
|
750
|
-
id: EntityId.random()
|
|
751
|
-
};
|
|
752
|
-
const validationErrors = this.validateDraftRowData(draftData);
|
|
753
|
-
const draftRow = {
|
|
754
|
-
data: draftData,
|
|
755
|
-
valid: validationErrors.length === 0,
|
|
756
|
-
validationErrors
|
|
757
|
-
};
|
|
758
|
-
this._registry.set(this._draftRows, [
|
|
759
|
-
...this._registry.get(this._draftRows),
|
|
760
|
-
draftRow
|
|
761
|
-
]);
|
|
762
|
-
}
|
|
763
|
-
validateDraftRow(draftRowIndex) {
|
|
764
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._registry.get(this._draftRows).length) {
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
const draftRow = this._registry.get(this._draftRows)[draftRowIndex];
|
|
768
|
-
const validationErrors = this.validateDraftRowData(draftRow.data);
|
|
769
|
-
const updatedDraftRow = {
|
|
770
|
-
...draftRow,
|
|
771
|
-
valid: validationErrors.length === 0,
|
|
772
|
-
validationErrors
|
|
773
|
-
};
|
|
774
|
-
const newDraftRows = [
|
|
775
|
-
...this._registry.get(this._draftRows)
|
|
776
|
-
];
|
|
777
|
-
newDraftRows[draftRowIndex] = updatedDraftRow;
|
|
778
|
-
this._registry.set(this._draftRows, newDraftRows);
|
|
779
|
-
}
|
|
780
|
-
validateDraftRowData(data) {
|
|
781
|
-
const schema = toEffectSchema(this._projection.baseSchema);
|
|
782
|
-
return validateSchema(schema, data) || [];
|
|
783
|
-
}
|
|
784
|
-
commitDraftRow = (draftRowIndex) => {
|
|
785
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._registry.get(this._draftRows).length) {
|
|
786
|
-
return false;
|
|
787
|
-
}
|
|
788
|
-
const draftRow = this._registry.get(this._draftRows)[draftRowIndex];
|
|
789
|
-
if (!draftRow.valid) {
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
const insertRowResult = this._onInsertRow?.(draftRow.data);
|
|
793
|
-
if (insertRowResult === "final") {
|
|
794
|
-
const newDraftRows = this._registry.get(this._draftRows).filter((_, index) => index !== draftRowIndex);
|
|
795
|
-
this._registry.set(this._draftRows, newDraftRows);
|
|
796
|
-
}
|
|
797
|
-
return insertRowResult === "final";
|
|
798
|
-
};
|
|
799
|
-
deleteRow = (rowIndex) => {
|
|
800
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
801
|
-
const obj = sortedRows[rowIndex];
|
|
802
|
-
const objectsToDelete = [];
|
|
803
|
-
if (this.features.selection.enabled && this._selection.hasSelection) {
|
|
804
|
-
const selectedRows = this._selection.getSelectedRows();
|
|
805
|
-
objectsToDelete.push(...selectedRows);
|
|
806
|
-
} else if (obj) {
|
|
807
|
-
objectsToDelete.push(obj);
|
|
808
|
-
}
|
|
809
|
-
if (objectsToDelete.length > 0) {
|
|
810
|
-
this._onDeleteRows?.(rowIndex, objectsToDelete);
|
|
811
|
-
}
|
|
812
|
-
};
|
|
813
|
-
handleRowAction = (actionId, rowIndex) => {
|
|
814
|
-
if (!this._onRowAction) {
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
818
|
-
const data = sortedRows[rowIndex];
|
|
819
|
-
if (data) {
|
|
820
|
-
this._onRowAction(actionId, data);
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
getCellData = (cell) => {
|
|
824
|
-
const { col, row, plane } = cell;
|
|
825
|
-
const fields = this._projection?.getFields() ?? [];
|
|
826
|
-
if (col < 0 || col >= fields.length) {
|
|
827
|
-
return void 0;
|
|
828
|
-
}
|
|
829
|
-
const field = fields[col];
|
|
830
|
-
let value;
|
|
831
|
-
if (plane === "frozenRowsEnd") {
|
|
832
|
-
if (row >= 0 && row < this._registry.get(this._draftRows).length) {
|
|
833
|
-
value = SchemaEx.getValue(this._registry.get(this._draftRows)[row].data, field.path);
|
|
834
|
-
} else {
|
|
835
|
-
return void 0;
|
|
836
|
-
}
|
|
837
|
-
} else {
|
|
838
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
839
|
-
value = SchemaEx.getValue(sortedRows[row], field.path);
|
|
840
|
-
}
|
|
841
|
-
if (value == null) {
|
|
842
|
-
return "";
|
|
843
|
-
}
|
|
844
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
845
|
-
switch (props.format) {
|
|
846
|
-
case Format2.TypeFormat.Ref: {
|
|
847
|
-
if (!field.referencePath) {
|
|
848
|
-
return "";
|
|
849
|
-
}
|
|
850
|
-
return SchemaEx.getValue(value.target, field.referencePath);
|
|
851
|
-
}
|
|
852
|
-
default: {
|
|
853
|
-
return formatForEditing({
|
|
854
|
-
type: props.type,
|
|
855
|
-
format: props.format,
|
|
856
|
-
value
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
|
-
validateCellData = async ({ col, row, plane }, value) => {
|
|
862
|
-
const fields = this._projection?.getFields() ?? [];
|
|
863
|
-
if (col < 0 || col >= fields.length) {
|
|
864
|
-
return {
|
|
865
|
-
valid: false,
|
|
866
|
-
error: "Invalid column index"
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
const field = fields[col];
|
|
870
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
871
|
-
const transformedValue = editorTextToCellValue(props, value);
|
|
872
|
-
const isDraftRow = plane === "frozenRowsEnd";
|
|
873
|
-
if (isDraftRow) {
|
|
874
|
-
const isInvalidDraftRowIndex = row < 0 || row >= this._registry.get(this._draftRows).length;
|
|
875
|
-
if (isInvalidDraftRowIndex) {
|
|
876
|
-
return {
|
|
877
|
-
valid: false,
|
|
878
|
-
error: "Invalid draft row index"
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
const draftRow = this._registry.get(this._draftRows)[row];
|
|
882
|
-
const snapshot = {
|
|
883
|
-
...draftRow.data
|
|
884
|
-
};
|
|
885
|
-
SchemaEx.setValue(snapshot, field.path, transformedValue);
|
|
886
|
-
const validationErrors = this.validateDraftRowData(snapshot);
|
|
887
|
-
if (validationErrors.length > 0) {
|
|
888
|
-
const error = validationErrors.find((err) => err.path === field.path);
|
|
889
|
-
if (error) {
|
|
890
|
-
return {
|
|
891
|
-
valid: false,
|
|
892
|
-
error: error.message
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
return {
|
|
897
|
-
valid: true
|
|
898
|
-
};
|
|
899
|
-
} else {
|
|
900
|
-
const currentRow = this._registry.get(this._rows)[row];
|
|
901
|
-
invariant(currentRow, "Invalid row index", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 544, S: this, A: ["currentRow", "'Invalid row index'"] });
|
|
902
|
-
const snapshot = {
|
|
903
|
-
...getSnapshot(currentRow)
|
|
904
|
-
};
|
|
905
|
-
SchemaEx.setValue(snapshot, field.path, transformedValue);
|
|
906
|
-
const type = Obj.getType(currentRow);
|
|
907
|
-
invariant(type, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 551, S: this, A: ["type", ""] });
|
|
908
|
-
const schema = Type.getSchema(type);
|
|
909
|
-
const validationResult = validateSchema(schema, snapshot);
|
|
910
|
-
if (validationResult && validationResult.length > 0) {
|
|
911
|
-
const error = validationResult.find((err) => err.path === field.path);
|
|
912
|
-
if (error) {
|
|
913
|
-
return {
|
|
914
|
-
valid: false,
|
|
915
|
-
error: error.message
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
return {
|
|
920
|
-
valid: true
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
setCellData = (cell, value) => {
|
|
925
|
-
const { col, row, plane } = cell;
|
|
926
|
-
const fields = this._projection?.getFields() ?? [];
|
|
927
|
-
if (col < 0 || col >= fields.length) {
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
const field = fields[col];
|
|
931
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
932
|
-
const transformedValue = editorTextToCellValue(props, value);
|
|
933
|
-
if (props.format === Format2.TypeFormat.Ref && !Obj.isObject(value)) {
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
if (plane === "frozenRowsEnd") {
|
|
937
|
-
if (row >= 0 && row < this._registry.get(this._draftRows).length) {
|
|
938
|
-
SchemaEx.setValue(this._registry.get(this._draftRows)[row].data, field.path, transformedValue);
|
|
939
|
-
this.validateDraftRow(row);
|
|
940
|
-
}
|
|
941
|
-
} else {
|
|
942
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
943
|
-
const rowData = sortedRows[row];
|
|
944
|
-
this._change.row(rowData, (mutableRow) => {
|
|
945
|
-
SchemaEx.setValue(mutableRow, field.path, transformedValue);
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
};
|
|
949
|
-
/**
|
|
950
|
-
* Updates a cell's value using a transform function.
|
|
951
|
-
* @param {DxGridPlanePosition} position - The position of the cell to update.
|
|
952
|
-
* @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
|
|
953
|
-
*/
|
|
954
|
-
updateCellData({ col, row }, update) {
|
|
955
|
-
const fields = this._projection?.getFields() ?? [];
|
|
956
|
-
const field = fields[col];
|
|
957
|
-
const sortedRows = this._registry.get(this._sortedRows);
|
|
958
|
-
const rowData = sortedRows[row];
|
|
959
|
-
const value = SchemaEx.getValue(rowData, field.path);
|
|
960
|
-
const updatedValue = update(value);
|
|
961
|
-
this._change.row(rowData, (mutableRow) => {
|
|
962
|
-
SchemaEx.setValue(mutableRow, field.path, updatedValue);
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Gets the domain-appropriate cell type for a given cell position.
|
|
967
|
-
* This abstracts away the low-level grid plane concepts and provides domain-specific types.
|
|
968
|
-
* @param cell - The cell position to classify
|
|
969
|
-
* @returns The domain-appropriate cell type
|
|
970
|
-
*/
|
|
971
|
-
getCellType(cell) {
|
|
972
|
-
switch (cell.plane) {
|
|
973
|
-
case "frozenRowsEnd":
|
|
974
|
-
return "draft";
|
|
975
|
-
case "frozenRowsStart":
|
|
976
|
-
return "header";
|
|
977
|
-
case "grid":
|
|
978
|
-
default:
|
|
979
|
-
return "standard";
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
/**
|
|
983
|
-
* Convenience method to check if a cell is a draft cell.
|
|
984
|
-
* @param cell - The cell position to check
|
|
985
|
-
* @returns true if the cell is a draft cell, false otherwise
|
|
986
|
-
*/
|
|
987
|
-
isDraftCell(cell) {
|
|
988
|
-
return this.getCellType(cell) === "draft";
|
|
989
|
-
}
|
|
990
|
-
//
|
|
991
|
-
// Column Operations
|
|
992
|
-
//
|
|
993
|
-
deleteColumn(fieldId) {
|
|
994
|
-
if (!this._projection) {
|
|
995
|
-
return;
|
|
996
|
-
}
|
|
997
|
-
const field = this._projection?.getFields().find((field2) => field2.id === fieldId);
|
|
998
|
-
if (field && this._onColumnDelete) {
|
|
999
|
-
this._onColumnDelete(field.id);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
//
|
|
1003
|
-
// Resize
|
|
1004
|
-
//
|
|
1005
|
-
setColumnWidth(columnIndex, width) {
|
|
1006
|
-
const fields = this._projection?.getFields() ?? [];
|
|
1007
|
-
if (columnIndex < fields.length) {
|
|
1008
|
-
const newWidth = Math.max(0, width);
|
|
1009
|
-
const field = fields[columnIndex];
|
|
1010
|
-
if (field) {
|
|
1011
|
-
this._change.table((mutableTable) => {
|
|
1012
|
-
mutableTable.sizes[field.path] = newWidth;
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
//
|
|
1018
|
-
// Pinning
|
|
1019
|
-
//
|
|
1020
|
-
// TODO(burdon): Change to setPinned(on/off).
|
|
1021
|
-
pinRow(rowIndex, side) {
|
|
1022
|
-
this._pinnedRows[side].push(rowIndex);
|
|
1023
|
-
}
|
|
1024
|
-
unpinRow(rowIndex) {
|
|
1025
|
-
this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
|
|
1026
|
-
this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
|
|
1027
|
-
}
|
|
1028
|
-
//
|
|
1029
|
-
// Sort operations
|
|
1030
|
-
//
|
|
1031
|
-
/**
|
|
1032
|
-
* Sets the sort order for a field in-memory (local to this instance).
|
|
1033
|
-
* Use saveView() to persist the sort to the view query.
|
|
1034
|
-
*/
|
|
1035
|
-
setSort(fieldId, direction) {
|
|
1036
|
-
const field = this._projection.getFields().find((f) => f.id === fieldId);
|
|
1037
|
-
if (!field) {
|
|
1038
|
-
return;
|
|
1039
|
-
}
|
|
1040
|
-
this._writeSort({
|
|
1041
|
-
fieldId,
|
|
1042
|
-
direction
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Writes the per-user sort, routing through the ViewStateManager (so the `local` backend persists)
|
|
1047
|
-
* when one is configured, otherwise updating the ephemeral atom directly.
|
|
1048
|
-
*/
|
|
1049
|
-
_writeSort(value) {
|
|
1050
|
-
if (this._viewState) {
|
|
1051
|
-
this._viewState.set(tableSortAspect, this.id, value);
|
|
1052
|
-
} else {
|
|
1053
|
-
this._registry.set(this._inMemorySort, value);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Toggles the sort direction for a field in-memory.
|
|
1058
|
-
*/
|
|
1059
|
-
toggleSort(fieldId) {
|
|
1060
|
-
const currentSort = this.getSorting();
|
|
1061
|
-
if (!currentSort || currentSort.fieldId !== fieldId) {
|
|
1062
|
-
this.setSort(fieldId, "asc");
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
const newDirection = currentSort.direction === "asc" ? "desc" : "asc";
|
|
1066
|
-
this.setSort(fieldId, newDirection);
|
|
1067
|
-
}
|
|
1068
|
-
/**
|
|
1069
|
-
* Clears the in-memory sort order.
|
|
1070
|
-
*/
|
|
1071
|
-
clearSort() {
|
|
1072
|
-
this._writeSort(void 0);
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Saves the current in-memory sort to the view query AST.
|
|
1076
|
-
* This persists the sort so it becomes the default for all viewers.
|
|
1077
|
-
*/
|
|
1078
|
-
saveView() {
|
|
1079
|
-
const view = this.view;
|
|
1080
|
-
const inMemorySort = this._registry.get(this._inMemorySort);
|
|
1081
|
-
const viewSnapshot = Obj.getSnapshot(view);
|
|
1082
|
-
const currentQuery = Query.fromAst(viewSnapshot.query.ast);
|
|
1083
|
-
const baseQuery = this._getBaseQuery(currentQuery);
|
|
1084
|
-
if (inMemorySort) {
|
|
1085
|
-
const field = this._projection.getFields().find((f) => f.id === inMemorySort.fieldId);
|
|
1086
|
-
if (field) {
|
|
1087
|
-
const newQuery = baseQuery.orderBy(Order.property(field.path, inMemorySort.direction));
|
|
1088
|
-
Obj.update(view, (view2) => {
|
|
1089
|
-
view2.query.ast = newQuery.ast;
|
|
1090
|
-
});
|
|
1091
|
-
}
|
|
1092
|
-
} else {
|
|
1093
|
-
Obj.update(view, (view2) => {
|
|
1094
|
-
view2.query.ast = baseQuery.ast;
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
this._writeSort(void 0);
|
|
1098
|
-
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Extracts the base query without order clauses.
|
|
1101
|
-
*/
|
|
1102
|
-
_getBaseQuery(query) {
|
|
1103
|
-
const ast = query.ast;
|
|
1104
|
-
if (ast.type === "order") {
|
|
1105
|
-
return Query.fromAst(ast.query);
|
|
1106
|
-
}
|
|
1107
|
-
return query;
|
|
1108
|
-
}
|
|
1109
|
-
/**
|
|
1110
|
-
* Checks if the view has unsaved changes (in-memory sort differs from persisted sort).
|
|
1111
|
-
*/
|
|
1112
|
-
getViewDirty() {
|
|
1113
|
-
return this._registry.get(this._viewDirty);
|
|
1114
|
-
}
|
|
1115
|
-
};
|
|
1116
|
-
var editorTextToCellValue = (props, value) => {
|
|
1117
|
-
switch (props.format) {
|
|
1118
|
-
case Format2.TypeFormat.Ref: {
|
|
1119
|
-
if (Obj.isObject(value)) {
|
|
1120
|
-
return Ref.make(value);
|
|
1121
|
-
} else {
|
|
1122
|
-
return value;
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
case Format2.TypeFormat.SingleSelect: {
|
|
1126
|
-
const ids = extractTagIds(value);
|
|
1127
|
-
if (ids && ids.length > 0) {
|
|
1128
|
-
return ids[0];
|
|
1129
|
-
} else {
|
|
1130
|
-
return value;
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
case Format2.TypeFormat.MultiSelect: {
|
|
1134
|
-
const ids = extractTagIds(value);
|
|
1135
|
-
return ids || value;
|
|
1136
|
-
}
|
|
1137
|
-
default: {
|
|
1138
|
-
return parseValue({
|
|
1139
|
-
type: props.type,
|
|
1140
|
-
format: props.format,
|
|
1141
|
-
value
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
};
|
|
1146
|
-
|
|
1147
|
-
// src/model/table-presentation.ts
|
|
1148
|
-
import { Atom as Atom3 } from "@effect-atom/atom-react";
|
|
1149
|
-
import * as Predicate from "effect/Predicate";
|
|
1150
|
-
import { Obj as Obj2 } from "@dxos/echo";
|
|
1151
|
-
import { Format as Format3, TypeEnum } from "@dxos/echo/Format";
|
|
1152
|
-
import { SchemaEx as SchemaEx2 } from "@dxos/effect";
|
|
1153
|
-
import { cellClassesForFieldType, formatForDisplay } from "@dxos/react-ui-form";
|
|
1154
|
-
import { toPlaneCellIndex } from "@dxos/react-ui-grid";
|
|
1155
|
-
import { VIEW_FIELD_LIMIT } from "@dxos/schema";
|
|
1156
|
-
import { mx } from "@dxos/ui-theme";
|
|
1157
|
-
var TablePresentation = class {
|
|
1158
|
-
model;
|
|
1159
|
-
_registry;
|
|
1160
|
-
_visibleRange;
|
|
1161
|
-
fieldProjectionCache = /* @__PURE__ */ new Map();
|
|
1162
|
-
constructor(registry, model) {
|
|
1163
|
-
this.model = model;
|
|
1164
|
-
this._registry = registry;
|
|
1165
|
-
this._visibleRange = Atom3.make({
|
|
1166
|
-
start: {
|
|
1167
|
-
row: 0,
|
|
1168
|
-
col: 0
|
|
1169
|
-
},
|
|
1170
|
-
end: {
|
|
1171
|
-
row: 0,
|
|
1172
|
-
col: 0
|
|
1173
|
-
}
|
|
1174
|
-
});
|
|
1175
|
-
}
|
|
1176
|
-
getCells(range, plane) {
|
|
1177
|
-
this.fieldProjectionCache.clear();
|
|
1178
|
-
let cells;
|
|
1179
|
-
switch (plane) {
|
|
1180
|
-
case "grid":
|
|
1181
|
-
this._registry.set(this._visibleRange, range);
|
|
1182
|
-
cells = this.getMainGridCells(range);
|
|
1183
|
-
break;
|
|
1184
|
-
case "frozenRowsStart":
|
|
1185
|
-
cells = this.getHeaderCells(range);
|
|
1186
|
-
break;
|
|
1187
|
-
case "frozenColsStart":
|
|
1188
|
-
cells = this.getSelectionColumnCells(range);
|
|
1189
|
-
break;
|
|
1190
|
-
case "frozenColsEnd":
|
|
1191
|
-
cells = this.getActionColumnCells(range);
|
|
1192
|
-
break;
|
|
1193
|
-
case "fixedStartStart":
|
|
1194
|
-
cells = this.getSelectAllCell();
|
|
1195
|
-
break;
|
|
1196
|
-
case "fixedStartEnd":
|
|
1197
|
-
cells = this.getNewColumnCell();
|
|
1198
|
-
break;
|
|
1199
|
-
case "frozenRowsEnd":
|
|
1200
|
-
cells = this.getDraftRowCells(range);
|
|
1201
|
-
break;
|
|
1202
|
-
case "fixedEndStart":
|
|
1203
|
-
cells = this.getDraftIconCells(range);
|
|
1204
|
-
break;
|
|
1205
|
-
case "fixedEndEnd":
|
|
1206
|
-
cells = this.getDraftActionCells(range);
|
|
1207
|
-
break;
|
|
1208
|
-
default:
|
|
1209
|
-
cells = {};
|
|
1210
|
-
}
|
|
1211
|
-
if (plane === "grid" && this.model.features.dataEditable === false) {
|
|
1212
|
-
Object.values(cells).forEach((cell) => {
|
|
1213
|
-
cell.readonly = "text-select";
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1216
|
-
return cells;
|
|
1217
|
-
}
|
|
1218
|
-
/**
|
|
1219
|
-
* Gets a field projection, using cache if available within the current render pass.
|
|
1220
|
-
*/
|
|
1221
|
-
getFieldProjection(fieldId) {
|
|
1222
|
-
let projection = this.fieldProjectionCache.get(fieldId);
|
|
1223
|
-
if (!projection) {
|
|
1224
|
-
projection = this.model.projection.getFieldProjection(fieldId);
|
|
1225
|
-
this.fieldProjectionCache.set(fieldId, projection);
|
|
1226
|
-
}
|
|
1227
|
-
return projection;
|
|
1228
|
-
}
|
|
1229
|
-
createDataCell(cells, obj, field, colIndex, displayIndex) {
|
|
1230
|
-
const { props } = this.getFieldProjection(field.id);
|
|
1231
|
-
const cell = {
|
|
1232
|
-
get value() {
|
|
1233
|
-
const value = SchemaEx2.getValue(obj, field.path);
|
|
1234
|
-
if (value == null) {
|
|
1235
|
-
return "";
|
|
1236
|
-
}
|
|
1237
|
-
if (props.type === "array") {
|
|
1238
|
-
return "";
|
|
1239
|
-
}
|
|
1240
|
-
switch (props.format) {
|
|
1241
|
-
case Format3.TypeFormat.Boolean:
|
|
1242
|
-
case Format3.TypeFormat.SingleSelect:
|
|
1243
|
-
case Format3.TypeFormat.MultiSelect: {
|
|
1244
|
-
return "";
|
|
1245
|
-
}
|
|
1246
|
-
case Format3.TypeFormat.Ref: {
|
|
1247
|
-
if (!field.referencePath) {
|
|
1248
|
-
return "";
|
|
1249
|
-
}
|
|
1250
|
-
return SchemaEx2.getValue(value.target, field.referencePath);
|
|
1251
|
-
}
|
|
1252
|
-
default: {
|
|
1253
|
-
return formatForDisplay({
|
|
1254
|
-
type: props.type,
|
|
1255
|
-
format: props.format,
|
|
1256
|
-
value
|
|
1257
|
-
});
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
};
|
|
1262
|
-
const classes = [];
|
|
1263
|
-
const formatClasses = cellClassesForFieldType(props);
|
|
1264
|
-
if (formatClasses) {
|
|
1265
|
-
classes.push(formatClasses);
|
|
1266
|
-
}
|
|
1267
|
-
const rowSelectionClasses = cellClassesForRowSelection(this.model.selection.isObjectSelected(obj), this.model.selection.selectionMode);
|
|
1268
|
-
if (rowSelectionClasses) {
|
|
1269
|
-
classes.push(rowSelectionClasses);
|
|
1270
|
-
}
|
|
1271
|
-
cell.className = mx(classes);
|
|
1272
|
-
if (props.type === TypeEnum.Array) {
|
|
1273
|
-
const targetArray = SchemaEx2.getValue(obj, field.path);
|
|
1274
|
-
if (targetArray && Array.isArray(targetArray)) {
|
|
1275
|
-
const getLabel = (value) => {
|
|
1276
|
-
if (!value) {
|
|
1277
|
-
return void 0;
|
|
1278
|
-
}
|
|
1279
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1280
|
-
return String(value);
|
|
1281
|
-
}
|
|
1282
|
-
if (Array.isArray(value)) {
|
|
1283
|
-
return "Array";
|
|
1284
|
-
}
|
|
1285
|
-
if (typeof value === "object" && value.name) {
|
|
1286
|
-
return value.name;
|
|
1287
|
-
}
|
|
1288
|
-
if (typeof value === "object" && value.value) {
|
|
1289
|
-
return `${value.value}`;
|
|
1290
|
-
}
|
|
1291
|
-
return "Object";
|
|
1292
|
-
};
|
|
1293
|
-
const tags = targetArray.map(getLabel).filter(Predicate.isNotNullable).map((title) => {
|
|
1294
|
-
return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
|
|
1295
|
-
}).join("");
|
|
1296
|
-
cell.accessoryHtml = `<div class="flex flex-row gap-1 overflow-x-auto">${tags}</div>`;
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
if (props.format === Format3.TypeFormat.Ref && props.referenceSchema) {
|
|
1300
|
-
const targetObj = SchemaEx2.getValue(obj, field.path)?.target;
|
|
1301
|
-
if (targetObj) {
|
|
1302
|
-
const uri = Obj2.getURI(targetObj);
|
|
1303
|
-
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><dx-anchor uri=${uri} 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>`;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
if (props.format === Format3.TypeFormat.URL) {
|
|
1307
|
-
const value = SchemaEx2.getValue(obj, field.path);
|
|
1308
|
-
const href = typeof value === "string" ? safeHttpUrl(value) : void 0;
|
|
1309
|
-
if (href) {
|
|
1310
|
-
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><a href="${escapeHtmlAttribute(href)}" target="_blank" rel="noopener noreferrer" class="dx-button w-6 aspect-square min-h-0" data-dx-grid-action="accessory"><dx-icon icon="ph--arrow-square-out--regular"/></a></div>`;
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
if (props.format === Format3.TypeFormat.Boolean) {
|
|
1314
|
-
const value = SchemaEx2.getValue(obj, field.path);
|
|
1315
|
-
cell.accessoryHtml = tableControls.switch.render({
|
|
1316
|
-
colIndex,
|
|
1317
|
-
rowIndex: displayIndex,
|
|
1318
|
-
checked: value ?? false
|
|
1319
|
-
});
|
|
1320
|
-
cell.readonly = "no-text-select";
|
|
1321
|
-
}
|
|
1322
|
-
if (props.format === Format3.TypeFormat.SingleSelect) {
|
|
1323
|
-
const value = SchemaEx2.getValue(obj, field.path);
|
|
1324
|
-
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
1325
|
-
if (options) {
|
|
1326
|
-
const option = options.find((o) => o.id === value);
|
|
1327
|
-
if (option) {
|
|
1328
|
-
cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
if (props.format === Format3.TypeFormat.MultiSelect) {
|
|
1333
|
-
const values = SchemaEx2.getValue(obj, field.path);
|
|
1334
|
-
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
1335
|
-
if (options && values && values.length > 0) {
|
|
1336
|
-
const tags = values.map((value) => {
|
|
1337
|
-
const option = options.find((o) => o.id === value);
|
|
1338
|
-
if (option) {
|
|
1339
|
-
return `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
1340
|
-
}
|
|
1341
|
-
return null;
|
|
1342
|
-
}).filter(Boolean).join("");
|
|
1343
|
-
if (tags) {
|
|
1344
|
-
cell.accessoryHtml = tags;
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
const idx = toPlaneCellIndex({
|
|
1349
|
-
col: colIndex,
|
|
1350
|
-
row: displayIndex
|
|
1351
|
-
});
|
|
1352
|
-
cells[idx] = cell;
|
|
1353
|
-
}
|
|
1354
|
-
getMainGridCells(range) {
|
|
1355
|
-
const cells = {};
|
|
1356
|
-
const fields = this.model.projection?.getFields() ?? [];
|
|
1357
|
-
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1358
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1359
|
-
const field = fields[col];
|
|
1360
|
-
if (!field) {
|
|
1361
|
-
continue;
|
|
1362
|
-
}
|
|
1363
|
-
this.createDataCell(cells, this.model.getRows()[row], field, col, row);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
return cells;
|
|
1367
|
-
}
|
|
1368
|
-
getDraftRowCells(range) {
|
|
1369
|
-
const cells = {};
|
|
1370
|
-
const fields = this.model.projection?.getFields() ?? [];
|
|
1371
|
-
const draftRows = this.model.getDraftRows();
|
|
1372
|
-
if (draftRows.length === 0) {
|
|
1373
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1374
|
-
cells[toPlaneCellIndex({
|
|
1375
|
-
col,
|
|
1376
|
-
row: 0
|
|
1377
|
-
})] = {
|
|
1378
|
-
value: "",
|
|
1379
|
-
readonly: true,
|
|
1380
|
-
className: "dx-grid__row--cta__cell"
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
} else {
|
|
1384
|
-
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
1385
|
-
const draftRow = draftRows[row];
|
|
1386
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1387
|
-
const cellIndex = toPlaneCellIndex({
|
|
1388
|
-
col,
|
|
1389
|
-
row
|
|
1390
|
-
});
|
|
1391
|
-
const field = fields[col];
|
|
1392
|
-
if (!field) {
|
|
1393
|
-
continue;
|
|
1394
|
-
}
|
|
1395
|
-
this.createDataCell(cells, draftRow.data, field, col, row);
|
|
1396
|
-
if (this.model.hasDraftRowValidationError(row, field.path)) {
|
|
1397
|
-
const cellValue = cells[cellIndex];
|
|
1398
|
-
if (cellValue) {
|
|
1399
|
-
const existingClasses = cellValue.className || "";
|
|
1400
|
-
const draftClasses = "dx-grid__cell--flagged";
|
|
1401
|
-
cellValue.className = existingClasses ? `${existingClasses} ${draftClasses}` : draftClasses;
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
return cells;
|
|
1408
|
-
}
|
|
1409
|
-
getHeaderCells(range) {
|
|
1410
|
-
const cells = {};
|
|
1411
|
-
const fields = this.model.projection?.getFields() ?? [];
|
|
1412
|
-
const currentSort = this.model.getSorting();
|
|
1413
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1414
|
-
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
1415
|
-
const direction = currentSort?.fieldId === field.id ? currentSort.direction : void 0;
|
|
1416
|
-
cells[toPlaneCellIndex({
|
|
1417
|
-
col,
|
|
1418
|
-
row: 0
|
|
1419
|
-
})] = {
|
|
1420
|
-
// TODO(burdon): Use same logic as form for fallback title.
|
|
1421
|
-
value: "",
|
|
1422
|
-
resizeHandle: "col",
|
|
1423
|
-
accessoryHtml: `
|
|
1424
|
-
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
1425
|
-
${direction !== void 0 ? tableButtons.sort.render({
|
|
1426
|
-
fieldId: field.id,
|
|
1427
|
-
direction
|
|
1428
|
-
}) : ""}
|
|
1429
|
-
${tableButtons.columnSettings.render({
|
|
1430
|
-
fieldId: field.id
|
|
1431
|
-
})}
|
|
1432
|
-
`,
|
|
1433
|
-
className: "bg-axis-surface! text-axis-text! [&>div]:flex [&>div]:items-stretch"
|
|
1434
|
-
};
|
|
1435
|
-
}
|
|
1436
|
-
return cells;
|
|
1437
|
-
}
|
|
1438
|
-
getSelectionColumnCells(range) {
|
|
1439
|
-
const cells = {};
|
|
1440
|
-
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1441
|
-
const isSelected = this.model.selection.isRowIndexSelected(row);
|
|
1442
|
-
const classes = cellClassesForRowSelection(isSelected, this.model.selection.selectionMode);
|
|
1443
|
-
cells[toPlaneCellIndex({
|
|
1444
|
-
col: 0,
|
|
1445
|
-
row
|
|
1446
|
-
})] = {
|
|
1447
|
-
value: "",
|
|
1448
|
-
readonly: "no-text-select",
|
|
1449
|
-
className: classes ? mx(classes) : void 0,
|
|
1450
|
-
accessoryHtml: tableControls.checkbox.render({
|
|
1451
|
-
rowIndex: row,
|
|
1452
|
-
checked: isSelected
|
|
1453
|
-
})
|
|
1454
|
-
};
|
|
1455
|
-
}
|
|
1456
|
-
return cells;
|
|
1457
|
-
}
|
|
1458
|
-
getActionColumnCells(range) {
|
|
1459
|
-
const cells = {};
|
|
1460
|
-
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1461
|
-
const isSelected = this.model.selection.isRowIndexSelected(row);
|
|
1462
|
-
const classes = cellClassesForRowSelection(isSelected, this.model.selection.selectionMode);
|
|
1463
|
-
cells[toPlaneCellIndex({
|
|
1464
|
-
col: 0,
|
|
1465
|
-
row
|
|
1466
|
-
})] = {
|
|
1467
|
-
value: "",
|
|
1468
|
-
readonly: true,
|
|
1469
|
-
className: classes ? mx(classes) : void 0,
|
|
1470
|
-
accessoryHtml: tableButtons.rowMenu.render({
|
|
1471
|
-
rowIndex: row
|
|
1472
|
-
})
|
|
1473
|
-
};
|
|
1474
|
-
}
|
|
1475
|
-
return cells;
|
|
1476
|
-
}
|
|
1477
|
-
getSelectAllCell() {
|
|
1478
|
-
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === "single") {
|
|
1479
|
-
return {
|
|
1480
|
-
[toPlaneCellIndex({
|
|
1481
|
-
col: 0,
|
|
1482
|
-
row: 0
|
|
1483
|
-
})]: {
|
|
1484
|
-
className: "bg-axis-surface!",
|
|
1485
|
-
readonly: true,
|
|
1486
|
-
value: ""
|
|
1487
|
-
}
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
return {
|
|
1491
|
-
[toPlaneCellIndex({
|
|
1492
|
-
col: 0,
|
|
1493
|
-
row: 0
|
|
1494
|
-
})]: {
|
|
1495
|
-
accessoryHtml: tableControls.checkbox.render({
|
|
1496
|
-
rowIndex: 0,
|
|
1497
|
-
header: true,
|
|
1498
|
-
checked: this.model.selection.allRowsSelected
|
|
1499
|
-
}),
|
|
1500
|
-
className: "bg-axis-surface!",
|
|
1501
|
-
readonly: true,
|
|
1502
|
-
value: ""
|
|
1503
|
-
}
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
getNewColumnCell() {
|
|
1507
|
-
return {
|
|
1508
|
-
[toPlaneCellIndex({
|
|
1509
|
-
col: 0,
|
|
1510
|
-
row: 0
|
|
1511
|
-
})]: {
|
|
1512
|
-
accessoryHtml: this.model.features.schemaEditable ? tableButtons.addColumn.render({
|
|
1513
|
-
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT
|
|
1514
|
-
}) : void 0,
|
|
1515
|
-
className: "bg-axis-surface!",
|
|
1516
|
-
readonly: true,
|
|
1517
|
-
value: ""
|
|
1518
|
-
}
|
|
1519
|
-
};
|
|
1520
|
-
}
|
|
1521
|
-
getDraftActionCells(range) {
|
|
1522
|
-
const cells = {};
|
|
1523
|
-
const draftRows = this.model.getDraftRows();
|
|
1524
|
-
if (draftRows.length === 0) {
|
|
1525
|
-
cells[toPlaneCellIndex({
|
|
1526
|
-
col: 0,
|
|
1527
|
-
row: 0
|
|
1528
|
-
})] = {
|
|
1529
|
-
value: "",
|
|
1530
|
-
className: "dx-grid__row--cta__cell",
|
|
1531
|
-
readonly: true
|
|
1532
|
-
};
|
|
1533
|
-
} else {
|
|
1534
|
-
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
1535
|
-
const draftRow = draftRows[row];
|
|
1536
|
-
const disabled = !draftRow.valid;
|
|
1537
|
-
cells[toPlaneCellIndex({
|
|
1538
|
-
col: 0,
|
|
1539
|
-
row
|
|
1540
|
-
})] = {
|
|
1541
|
-
value: "",
|
|
1542
|
-
readonly: true,
|
|
1543
|
-
accessoryHtml: tableButtons.saveDraftRow.render({
|
|
1544
|
-
rowIndex: row,
|
|
1545
|
-
disabled
|
|
1546
|
-
})
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
return cells;
|
|
1551
|
-
}
|
|
1552
|
-
getDraftIconCells(range) {
|
|
1553
|
-
const cells = {};
|
|
1554
|
-
const draftRows = this.model.getDraftRows();
|
|
1555
|
-
for (let row = range.start.row; row <= range.end.row; row++) {
|
|
1556
|
-
cells[toPlaneCellIndex({
|
|
1557
|
-
col: 0,
|
|
1558
|
-
row
|
|
1559
|
-
})] = {
|
|
1560
|
-
value: "",
|
|
1561
|
-
readonly: true,
|
|
1562
|
-
accessoryHtml: '<dx-icon icon="ph--plus--regular" class="contents"></dx-icon>',
|
|
1563
|
-
className: mx("[&>div]:grid [&>div]:place-content-center", draftRows.length < 1 && "dx-grid__row--cta__cell")
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
return cells;
|
|
1567
|
-
}
|
|
1568
|
-
};
|
|
1569
|
-
var safeHttpUrl = (value) => {
|
|
1570
|
-
try {
|
|
1571
|
-
const normalized = /^https?:\/\//i.test(value) ? value : `https://${value}`;
|
|
1572
|
-
const url = new URL(normalized);
|
|
1573
|
-
return url.protocol === "http:" || url.protocol === "https:" ? url.href : void 0;
|
|
1574
|
-
} catch {
|
|
1575
|
-
return void 0;
|
|
1576
|
-
}
|
|
1577
|
-
};
|
|
1578
|
-
var escapeHtmlAttribute = (value) => value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1579
|
-
var cellClassesForRowSelection = (selected, selectionMode) => {
|
|
1580
|
-
if (!selected) {
|
|
1581
|
-
if (selectionMode === "single") {
|
|
1582
|
-
return [
|
|
1583
|
-
"cursor-pointer!"
|
|
1584
|
-
];
|
|
1585
|
-
} else {
|
|
1586
|
-
return void 0;
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
switch (selectionMode) {
|
|
1590
|
-
case "single":
|
|
1591
|
-
return [
|
|
1592
|
-
"dx-grid__cell--no-focus-unfurl bg-current-surface! hover:bg-hover-surface cursor-pointer!"
|
|
1593
|
-
];
|
|
1594
|
-
case "multiple":
|
|
1595
|
-
return [
|
|
1596
|
-
"bg-grid-cell-selected!"
|
|
1597
|
-
];
|
|
1598
|
-
}
|
|
1599
|
-
};
|
|
1600
|
-
|
|
1601
|
-
// src/hooks/useAddRow.ts
|
|
1602
|
-
import { useCallback } from "react";
|
|
1603
|
-
import { Obj as Obj3 } from "@dxos/echo";
|
|
1604
|
-
var useAddRow = ({ db, schema }) => {
|
|
1605
|
-
return useCallback((data) => {
|
|
1606
|
-
if (db && schema) {
|
|
1607
|
-
try {
|
|
1608
|
-
db.add(Obj3.make(schema, data ?? {}));
|
|
1609
|
-
return "final";
|
|
1610
|
-
} catch (error) {
|
|
1611
|
-
return "draft";
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
return "final";
|
|
1615
|
-
}, [
|
|
1616
|
-
db,
|
|
1617
|
-
schema
|
|
1618
|
-
]);
|
|
1619
|
-
};
|
|
1620
|
-
|
|
1621
|
-
// src/hooks/useProjectionModel.ts
|
|
1622
|
-
import { useState } from "react";
|
|
1623
|
-
import { Type as Type2 } from "@dxos/echo";
|
|
1624
|
-
import { useAsyncEffect } from "@dxos/react-ui";
|
|
1625
|
-
import { ProjectionModel, createEchoChangeCallback as createEchoChangeCallback2 } from "@dxos/schema";
|
|
1626
|
-
var useProjectionModel = (schema, table, registry) => {
|
|
1627
|
-
const [projection, setProjection] = useState();
|
|
1628
|
-
useAsyncEffect(async () => {
|
|
1629
|
-
if (schema && table) {
|
|
1630
|
-
const view = await table.view.load();
|
|
1631
|
-
const jsonSchema = schema.jsonSchema;
|
|
1632
|
-
const change = createEchoChangeCallback2(view, Type2.getDatabase(schema) != null ? schema : void 0);
|
|
1633
|
-
const projection2 = new ProjectionModel({
|
|
1634
|
-
registry,
|
|
1635
|
-
view,
|
|
1636
|
-
baseSchema: jsonSchema,
|
|
1637
|
-
change
|
|
1638
|
-
});
|
|
1639
|
-
projection2.normalizeView();
|
|
1640
|
-
setProjection(projection2);
|
|
1641
|
-
}
|
|
1642
|
-
}, [
|
|
1643
|
-
schema,
|
|
1644
|
-
table,
|
|
1645
|
-
registry
|
|
1646
|
-
]);
|
|
1647
|
-
return projection;
|
|
1648
|
-
};
|
|
1649
|
-
|
|
1650
|
-
// src/hooks/useTableModel.ts
|
|
1651
|
-
import { RegistryContext } from "@effect-atom/atom-react";
|
|
1652
|
-
import { useContext, useEffect, useMemo, useState as useState2 } from "react";
|
|
1653
|
-
import { Obj as Obj4 } from "@dxos/echo";
|
|
1654
|
-
import { useSelection, useSelectionActions, useViewStateManagerOptional } from "@dxos/react-ui-attention";
|
|
1655
|
-
var useTableModel = ({ object, projection, db, rows, rowActions, features, onSelectionChanged, onRowAction, ...props }) => {
|
|
1656
|
-
const registry = useContext(RegistryContext);
|
|
1657
|
-
const viewState = useViewStateManagerOptional();
|
|
1658
|
-
const selected = useSelection(object && Obj4.getURI(object), "multi");
|
|
1659
|
-
const initialSelection = useMemo(() => selected, [
|
|
1660
|
-
object
|
|
1661
|
-
]);
|
|
1662
|
-
const [model, setModel] = useState2();
|
|
1663
|
-
useEffect(() => {
|
|
1664
|
-
if (!object || !projection) {
|
|
1665
|
-
return;
|
|
1666
|
-
}
|
|
1667
|
-
let model2;
|
|
1668
|
-
const t = setTimeout(async () => {
|
|
1669
|
-
model2 = new TableModel({
|
|
1670
|
-
registry,
|
|
1671
|
-
object,
|
|
1672
|
-
projection,
|
|
1673
|
-
db,
|
|
1674
|
-
viewState,
|
|
1675
|
-
change: createEchoChangeCallback(object),
|
|
1676
|
-
features,
|
|
1677
|
-
rowActions,
|
|
1678
|
-
initialSelection,
|
|
1679
|
-
onRowAction,
|
|
1680
|
-
...props
|
|
1681
|
-
});
|
|
1682
|
-
await model2.open();
|
|
1683
|
-
setModel(model2);
|
|
1684
|
-
});
|
|
1685
|
-
return () => {
|
|
1686
|
-
clearTimeout(t);
|
|
1687
|
-
void model2?.close();
|
|
1688
|
-
};
|
|
1689
|
-
}, [
|
|
1690
|
-
registry,
|
|
1691
|
-
viewState,
|
|
1692
|
-
object,
|
|
1693
|
-
projection,
|
|
1694
|
-
features,
|
|
1695
|
-
rowActions,
|
|
1696
|
-
initialSelection
|
|
1697
|
-
]);
|
|
1698
|
-
useEffect(() => {
|
|
1699
|
-
if (rows && model) {
|
|
1700
|
-
model.setRows(rows);
|
|
1701
|
-
}
|
|
1702
|
-
}, [
|
|
1703
|
-
model,
|
|
1704
|
-
rows
|
|
1705
|
-
]);
|
|
1706
|
-
const { multi, clear } = useSelectionActions(model?.id);
|
|
1707
|
-
useEffect(() => {
|
|
1708
|
-
if (!model) {
|
|
1709
|
-
return;
|
|
1710
|
-
}
|
|
1711
|
-
const unsubscribe = registry.subscribe(model.selection.selectionAtom, () => {
|
|
1712
|
-
const selectedItems = [
|
|
1713
|
-
...model.selection.selection
|
|
1714
|
-
];
|
|
1715
|
-
multi(selectedItems);
|
|
1716
|
-
onSelectionChanged?.(selectedItems);
|
|
1717
|
-
});
|
|
1718
|
-
return () => {
|
|
1719
|
-
clear();
|
|
1720
|
-
unsubscribe();
|
|
1721
|
-
};
|
|
1722
|
-
}, [
|
|
1723
|
-
registry,
|
|
1724
|
-
model,
|
|
1725
|
-
onSelectionChanged
|
|
1726
|
-
]);
|
|
1727
|
-
return model;
|
|
1728
|
-
};
|
|
1729
|
-
|
|
1730
|
-
export {
|
|
1731
|
-
extractOrder,
|
|
1732
|
-
getSortValue,
|
|
1733
|
-
compareValues,
|
|
1734
|
-
BUTTON_IDENTIFIERS,
|
|
1735
|
-
tableButtons,
|
|
1736
|
-
CONTROL_IDENTIFIERS,
|
|
1737
|
-
CheckboxStory,
|
|
1738
|
-
SwitchStory,
|
|
1739
|
-
tableControls,
|
|
1740
|
-
FieldSortSchema,
|
|
1741
|
-
tableSortAspect,
|
|
1742
|
-
createEchoChangeCallback,
|
|
1743
|
-
createDirectChangeCallback,
|
|
1744
|
-
TableModel,
|
|
1745
|
-
TablePresentation,
|
|
1746
|
-
cellClassesForRowSelection,
|
|
1747
|
-
useAddRow,
|
|
1748
|
-
useProjectionModel,
|
|
1749
|
-
useTableModel
|
|
1750
|
-
};
|
|
1751
|
-
//# sourceMappingURL=chunk-K4J2CKTZ.mjs.map
|