@dxos/react-ui-table 0.8.4-main.f9ba587 → 0.8.4-main.fd6878d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/chunk-L2LSB6KV.mjs +18 -0
- package/dist/lib/browser/chunk-L2LSB6KV.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs +1814 -0
- package/dist/lib/browser/chunk-ZAOSIG3L.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +658 -2359
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +123 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +3 -5
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs +20 -0
- package/dist/lib/node-esm/chunk-NXZ4CJYX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs +1815 -0
- package/dist/lib/node-esm/chunk-ZOFFI3JJ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +658 -2359
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +124 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +3 -5
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +4 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/{relations.stories.d.ts → Relations.stories.d.ts} +2 -2
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -0
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +1 -0
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +2 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +8 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/extension.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -40
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +8 -7
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +2 -1
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +17 -16
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +2 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +4 -4
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/useTestTableModel.d.ts +41 -0
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +8 -0
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util/dynamic-table.d.ts +3 -4
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table-controls.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +9 -11
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +50 -36
- package/src/components/Table/ColumnActionsMenu.tsx +1 -1
- package/src/components/Table/ColumnSettings.tsx +15 -11
- package/src/components/Table/CreateRefPanel.tsx +6 -4
- package/src/components/Table/DynamicTable.stories.tsx +3 -2
- package/src/components/Table/DynamicTable.tsx +17 -10
- package/src/components/Table/{relations.stories.tsx → Relations.stories.tsx} +36 -35
- package/src/components/Table/RowActionsMenu.tsx +3 -3
- package/src/components/Table/Table.stories.tsx +99 -144
- package/src/components/Table/Table.tsx +87 -71
- package/src/components/TableCellEditor/CellValidationMessage.tsx +1 -1
- package/src/components/TableCellEditor/FormCellEditor.tsx +30 -21
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +24 -27
- package/src/components/TableCellEditor/TableCellEditor.tsx +42 -27
- package/src/components/TableCellEditor/extension.ts +2 -2
- package/src/components/TableToolbar/TableToolbar.tsx +2 -2
- package/src/components/testing.ts +7 -24
- package/src/hooks/useTableModel.ts +21 -17
- package/src/index.ts +0 -1
- package/src/model/modal-controller.ts +1 -1
- package/src/model/selection-model.ts +6 -3
- package/src/model/table-model.test.ts +8 -8
- package/src/model/table-model.ts +68 -62
- package/src/model/table-presentation.test.ts +8 -8
- package/src/model/table-presentation.ts +35 -13
- package/src/model/table-sorting.ts +11 -11
- package/src/playwright/smoke.spec.ts +105 -14
- package/src/testing/index.ts +5 -0
- package/src/testing/useTestTableModel.ts +123 -0
- package/src/translations.ts +2 -2
- package/src/types/index.ts +2 -2
- package/src/types/schema.ts +16 -0
- package/src/util/dynamic-table.ts +20 -18
- package/src/util/schema.ts +1 -1
- package/src/util/table-buttons.ts +2 -1
- package/src/util/table-controls.ts +4 -1
- package/src/util/table.ts +37 -111
- package/dist/lib/browser/chunk-Q5QQXJUO.mjs +0 -27
- package/dist/lib/browser/chunk-Q5QQXJUO.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs +0 -29
- package/dist/lib/node-esm/chunk-ZNOU6LHG.mjs.map +0 -7
- package/dist/types/src/components/Table/relations.stories.d.ts.map +0 -1
- package/dist/types/src/types/table.d.ts +0 -56
- package/dist/types/src/types/table.d.ts.map +0 -1
- package/src/types/table.ts +0 -27
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
BUTTON_IDENTIFIERS,
|
|
3
|
+
CONTROL_IDENTIFIERS,
|
|
4
|
+
CheckboxStory,
|
|
5
|
+
ModalController,
|
|
6
|
+
SwitchStory,
|
|
7
|
+
TableModel,
|
|
8
|
+
TablePresentation,
|
|
9
|
+
cellClassesForRowSelection,
|
|
10
|
+
createTable,
|
|
11
|
+
getBaseSchema,
|
|
12
|
+
makeDynamicTable,
|
|
13
|
+
narrowSchema,
|
|
14
|
+
tableButtons,
|
|
15
|
+
tableControls,
|
|
16
|
+
touch,
|
|
17
|
+
useAddRow,
|
|
18
|
+
useTableModel
|
|
19
|
+
} from "./chunk-ZAOSIG3L.mjs";
|
|
20
|
+
import "./chunk-L2LSB6KV.mjs";
|
|
5
21
|
|
|
6
22
|
// src/translations.ts
|
|
7
23
|
var translationKey = "react-ui-table";
|
|
@@ -13,1956 +29,65 @@ var translations = [
|
|
|
13
29
|
"create object label": "Create table",
|
|
14
30
|
"table name placeholder": "Table name",
|
|
15
31
|
"settings title": "Table settings",
|
|
16
|
-
"table schema label": "
|
|
17
|
-
"new schema": "New
|
|
32
|
+
"table schema label": "Record type",
|
|
33
|
+
"new schema": "New record type",
|
|
18
34
|
"continue label": "Continue",
|
|
19
|
-
"create stack section label": "Create table",
|
|
20
|
-
"add row label": "Add row",
|
|
21
|
-
"create comment": "Create thread",
|
|
22
|
-
"column action sort descending": "Sort Descending",
|
|
23
|
-
"column action sort ascending": "Sort Ascending",
|
|
24
|
-
"column action clear sorting": "Clear Sorting",
|
|
25
|
-
"column action settings": "Column Settings",
|
|
26
|
-
"column action delete": "Delete Column",
|
|
27
|
-
"delete row label": "Delete row",
|
|
28
|
-
"bulk delete row label": "Delete selected rows",
|
|
29
|
-
"column deleted label": "Column deleted",
|
|
30
|
-
"new column button label": "Create column",
|
|
31
|
-
"create new object label": "Create new object \u201C{{text}}\u201D",
|
|
32
|
-
"save view label": "Save changes to view"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// src/components/Table/Table.tsx
|
|
39
|
-
import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
|
|
40
|
-
import React8, { forwardRef, useCallback as useCallback5, useImperativeHandle, useState as useState3, useMemo as useMemo5, useEffect as useEffect4 } from "react";
|
|
41
|
-
import { getValue as getValue4 } from "@dxos/echo-schema";
|
|
42
|
-
import { invariant as invariant4 } from "@dxos/invariant";
|
|
43
|
-
import { Filter } from "@dxos/react-client/echo";
|
|
44
|
-
import { useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
45
|
-
import { useAttention } from "@dxos/react-ui-attention";
|
|
46
|
-
import { closestCell, Grid, gridSeparatorInlineEnd, gridSeparatorBlockEnd } from "@dxos/react-ui-grid";
|
|
47
|
-
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
48
|
-
import { isNotFalsy, safeParseInt } from "@dxos/util";
|
|
49
|
-
|
|
50
|
-
// src/components/Table/ColumnActionsMenu.tsx
|
|
51
|
-
import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
|
|
52
|
-
import React from "react";
|
|
53
|
-
import { DropdownMenu, useTranslation } from "@dxos/react-ui";
|
|
54
|
-
var ColumnActionsMenu = ({ model, modals }) => {
|
|
55
|
-
var _effect = _useSignals();
|
|
56
|
-
try {
|
|
57
|
-
const { t } = useTranslation(translationKey);
|
|
58
|
-
const state = modals.state.value;
|
|
59
|
-
if (state?.type !== "column") {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
const currentSort = model.sorting?.sorting;
|
|
63
|
-
const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
|
|
64
|
-
return /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
|
|
65
|
-
modal: false,
|
|
66
|
-
open: true,
|
|
67
|
-
onOpenChange: modals.close
|
|
68
|
-
}, /* @__PURE__ */ React.createElement(DropdownMenu.VirtualTrigger, {
|
|
69
|
-
virtualRef: modals.trigger
|
|
70
|
-
}), /* @__PURE__ */ React.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React.createElement(DropdownMenu.Content, null, /* @__PURE__ */ React.createElement(DropdownMenu.Viewport, null, (!isCurrentColumnSorted || currentSort?.direction === "asc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
|
|
71
|
-
"data-testid": "column-sort-descending",
|
|
72
|
-
onClick: () => model.sorting?.setSort(state.fieldId, "desc")
|
|
73
|
-
}, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
|
|
74
|
-
"data-testid": "column-sort-ascending",
|
|
75
|
-
onClick: () => model.sorting?.setSort(state.fieldId, "asc")
|
|
76
|
-
}, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
|
|
77
|
-
"data-testid": "column-clear-sort",
|
|
78
|
-
onClick: () => model.sorting?.clearSort()
|
|
79
|
-
}, t("column action clear sorting")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
|
|
80
|
-
"data-testid": "column-delete",
|
|
81
|
-
onClick: () => model.deleteColumn(state.fieldId)
|
|
82
|
-
}, t("column action delete")), model.features.schemaEditable && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
|
|
83
|
-
"data-testid": "column-settings",
|
|
84
|
-
onClick: () => modals.openColumnSettings()
|
|
85
|
-
}, t("column action settings"))), /* @__PURE__ */ React.createElement(DropdownMenu.Arrow, null))));
|
|
86
|
-
} finally {
|
|
87
|
-
_effect.f();
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// src/components/Table/ColumnSettings.tsx
|
|
92
|
-
import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
|
|
93
|
-
import React2, { useCallback, useEffect, useMemo, useState } from "react";
|
|
94
|
-
import { Popover } from "@dxos/react-ui";
|
|
95
|
-
import { FieldEditor } from "@dxos/react-ui-form";
|
|
96
|
-
var ColumnSettings = ({ model, modals, onNewColumn }) => {
|
|
97
|
-
var _effect = _useSignals2();
|
|
98
|
-
try {
|
|
99
|
-
const [newField, setNewField] = useState();
|
|
100
|
-
const state = modals.state.value;
|
|
101
|
-
const space = model?.space;
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
|
|
104
|
-
setNewField(model.projection.createFieldProjection());
|
|
105
|
-
requestAnimationFrame(() => {
|
|
106
|
-
onNewColumn();
|
|
107
|
-
});
|
|
108
|
-
} else {
|
|
109
|
-
setNewField(void 0);
|
|
110
|
-
}
|
|
111
|
-
}, [
|
|
112
|
-
model?.projection,
|
|
113
|
-
state
|
|
114
|
-
]);
|
|
115
|
-
const existingField = useMemo(() => {
|
|
116
|
-
if (state?.type === "columnSettings") {
|
|
117
|
-
const { mode } = state;
|
|
118
|
-
if (mode.type === "edit") {
|
|
119
|
-
return model?.view?.fields.find((f) => f.id === mode.fieldId);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return void 0;
|
|
123
|
-
}, [
|
|
124
|
-
model?.view?.fields,
|
|
125
|
-
state
|
|
126
|
-
]);
|
|
127
|
-
const field = existingField ?? newField;
|
|
128
|
-
const handleSave = useCallback(() => {
|
|
129
|
-
modals.close();
|
|
130
|
-
}, [
|
|
131
|
-
modals
|
|
132
|
-
]);
|
|
133
|
-
const handleCancel = useCallback(() => {
|
|
134
|
-
if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
|
|
135
|
-
model?.projection?.deleteFieldProjection(newField.id);
|
|
136
|
-
}
|
|
137
|
-
}, [
|
|
138
|
-
model?.projection,
|
|
139
|
-
state,
|
|
140
|
-
newField
|
|
141
|
-
]);
|
|
142
|
-
if (!model?.view || !model.projection || !field) {
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
return /* @__PURE__ */ React2.createElement(Popover.Root, {
|
|
146
|
-
modal: false,
|
|
147
|
-
open: state?.type === "columnSettings"
|
|
148
|
-
}, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
|
|
149
|
-
virtualRef: modals.trigger
|
|
150
|
-
}), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
|
|
151
|
-
classNames: "md:is-64"
|
|
152
|
-
}, /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(FieldEditor, {
|
|
153
|
-
view: model.view,
|
|
154
|
-
projection: model.projection,
|
|
155
|
-
field,
|
|
156
|
-
registry: space?.db.schemaRegistry,
|
|
157
|
-
onSave: handleSave,
|
|
158
|
-
onCancel: handleCancel
|
|
159
|
-
})), /* @__PURE__ */ React2.createElement(Popover.Arrow, null))));
|
|
160
|
-
} finally {
|
|
161
|
-
_effect.f();
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
// src/components/Table/CreateRefPanel.tsx
|
|
166
|
-
import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
|
|
167
|
-
import { Schema } from "effect";
|
|
168
|
-
import React3, { useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
169
|
-
import { live } from "@dxos/react-client/echo";
|
|
170
|
-
import { Popover as Popover2 } from "@dxos/react-ui";
|
|
171
|
-
import { Form } from "@dxos/react-ui-form";
|
|
172
|
-
import { useGridContext } from "@dxos/react-ui-grid";
|
|
173
|
-
var CreateRefPanel = ({ model, modals, __gridScope }) => {
|
|
174
|
-
var _effect = _useSignals3();
|
|
175
|
-
try {
|
|
176
|
-
const { id: gridId } = useGridContext("TableCellEditor", __gridScope);
|
|
177
|
-
const space = model?.space;
|
|
178
|
-
const state = modals.state.value;
|
|
179
|
-
const schema = useMemo2(() => {
|
|
180
|
-
if (!space || state?.type !== "createRefPanel") {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
const [schema2] = space.db.schemaRegistry.query({
|
|
184
|
-
typename: state.typename
|
|
185
|
-
}).runSync();
|
|
186
|
-
if (schema2) {
|
|
187
|
-
const omit = Schema.omit("id");
|
|
188
|
-
return omit(schema2);
|
|
189
|
-
}
|
|
190
|
-
}, [
|
|
191
|
-
space,
|
|
192
|
-
state
|
|
193
|
-
]);
|
|
194
|
-
const handleSave = useCallback2((values) => {
|
|
195
|
-
if (!model || !space || state?.type !== "createRefPanel") {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const [schema2] = space.db.schemaRegistry.query({
|
|
199
|
-
typename: state.typename
|
|
200
|
-
}).runSync();
|
|
201
|
-
if (schema2) {
|
|
202
|
-
const obj = space.db.add(live(schema2, values));
|
|
203
|
-
state.onCreate?.(obj);
|
|
204
|
-
}
|
|
205
|
-
void modals.close();
|
|
206
|
-
}, [
|
|
207
|
-
modals,
|
|
208
|
-
space,
|
|
209
|
-
state
|
|
210
|
-
]);
|
|
211
|
-
const handleCancel = useCallback2(() => {
|
|
212
|
-
if (model) {
|
|
213
|
-
modals.close();
|
|
214
|
-
}
|
|
215
|
-
}, [
|
|
216
|
-
modals
|
|
217
|
-
]);
|
|
218
|
-
if (!model?.view || !model.projection) {
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
return /* @__PURE__ */ React3.createElement(Popover2.Root, {
|
|
222
|
-
modal: false,
|
|
223
|
-
open: state?.type === "createRefPanel" && !!schema,
|
|
224
|
-
onOpenChange: (nextOpen) => {
|
|
225
|
-
if (model && !nextOpen) {
|
|
226
|
-
return modals.close();
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}, /* @__PURE__ */ React3.createElement(Popover2.VirtualTrigger, {
|
|
230
|
-
virtualRef: modals.trigger
|
|
231
|
-
}), /* @__PURE__ */ React3.createElement(Popover2.Portal, null, /* @__PURE__ */ React3.createElement(Popover2.Content, {
|
|
232
|
-
classNames: "md:is-64",
|
|
233
|
-
"data-grid": gridId
|
|
234
|
-
}, /* @__PURE__ */ React3.createElement(Popover2.Viewport, null, state?.type === "createRefPanel" && schema && /* @__PURE__ */ React3.createElement(Form, {
|
|
235
|
-
schema,
|
|
236
|
-
values: state.initialValues ?? {},
|
|
237
|
-
onSave: handleSave,
|
|
238
|
-
onCancel: handleCancel
|
|
239
|
-
})), /* @__PURE__ */ React3.createElement(Popover2.Arrow, null))));
|
|
240
|
-
} finally {
|
|
241
|
-
_effect.f();
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// src/components/Table/RowActionsMenu.tsx
|
|
246
|
-
import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
|
|
247
|
-
import React4 from "react";
|
|
248
|
-
import { DropdownMenu as DropdownMenu2, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
249
|
-
var RowActionsMenu = ({ model, modals }) => {
|
|
250
|
-
var _effect = _useSignals4();
|
|
251
|
-
try {
|
|
252
|
-
const { t } = useTranslation2(translationKey);
|
|
253
|
-
const hasSelection = model.selection.hasSelection.value;
|
|
254
|
-
const state = modals.state.value;
|
|
255
|
-
if (state?.type !== "row") {
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
return /* @__PURE__ */ React4.createElement(DropdownMenu2.Root, {
|
|
259
|
-
modal: false,
|
|
260
|
-
open: true,
|
|
261
|
-
onOpenChange: modals.close
|
|
262
|
-
}, /* @__PURE__ */ React4.createElement(DropdownMenu2.VirtualTrigger, {
|
|
263
|
-
virtualRef: modals.trigger
|
|
264
|
-
}), /* @__PURE__ */ React4.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React4.createElement(DropdownMenu2.Viewport, null, model.features.dataEditable !== false && /* @__PURE__ */ React4.createElement(DropdownMenu2.Item, {
|
|
265
|
-
"data-testid": "row-menu-delete",
|
|
266
|
-
onClick: () => model.deleteRow(state.rowIndex)
|
|
267
|
-
}, t(hasSelection ? "bulk delete row label" : "delete row label")), model.rowActions?.map((action) => /* @__PURE__ */ React4.createElement(DropdownMenu2.Item, {
|
|
268
|
-
key: action.id,
|
|
269
|
-
"data-testid": `row-action-${action.id}`,
|
|
270
|
-
onClick: () => {
|
|
271
|
-
modals.close();
|
|
272
|
-
model.handleRowAction(action.id, state.rowIndex);
|
|
273
|
-
}
|
|
274
|
-
}, t(action.translationKey)))), /* @__PURE__ */ React4.createElement(DropdownMenu2.Arrow, null)));
|
|
275
|
-
} finally {
|
|
276
|
-
_effect.f();
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// src/model/table-model.ts
|
|
281
|
-
import { computed as computed3, effect as effect2, signal as signal3 } from "@preact/signals-core";
|
|
282
|
-
import { Resource as Resource2 } from "@dxos/context";
|
|
283
|
-
import { Ref as Ref3 } from "@dxos/echo";
|
|
284
|
-
import { FormatEnum as FormatEnum3, getValue as getValue2, setValue, getSnapshot, getSchema, toEffectSchema } from "@dxos/echo-schema";
|
|
285
|
-
import { invariant } from "@dxos/invariant";
|
|
286
|
-
import { ObjectId } from "@dxos/keys";
|
|
287
|
-
import { isLiveObject } from "@dxos/live-object";
|
|
288
|
-
import { formatForEditing, parseValue } from "@dxos/react-ui-form";
|
|
289
|
-
import { validateSchema } from "@dxos/schema";
|
|
290
|
-
|
|
291
|
-
// src/model/selection-model.ts
|
|
292
|
-
import { computed, effect, signal } from "@preact/signals-core";
|
|
293
|
-
import { Resource } from "@dxos/context";
|
|
294
|
-
|
|
295
|
-
// src/util/dynamic-table.ts
|
|
296
|
-
import { Obj, Ref } from "@dxos/echo";
|
|
297
|
-
import { getTypename, toJsonSchema } from "@dxos/echo-schema";
|
|
298
|
-
import { createView, getSchemaFromPropertyDefinitions, ViewProjection } from "@dxos/schema";
|
|
299
|
-
var getBaseSchema = ({ typename, properties, jsonSchema, schema }) => {
|
|
300
|
-
if (typename && properties) {
|
|
301
|
-
const schema2 = getSchemaFromPropertyDefinitions(typename, properties);
|
|
302
|
-
return {
|
|
303
|
-
typename: schema2.typename,
|
|
304
|
-
jsonSchema: schema2.jsonSchema
|
|
305
|
-
};
|
|
306
|
-
} else if (schema) {
|
|
307
|
-
return {
|
|
308
|
-
typename: getTypename(schema),
|
|
309
|
-
jsonSchema: toJsonSchema(schema)
|
|
310
|
-
};
|
|
311
|
-
} else if (typename && jsonSchema) {
|
|
312
|
-
return {
|
|
313
|
-
typename,
|
|
314
|
-
jsonSchema
|
|
315
|
-
};
|
|
316
|
-
} else {
|
|
317
|
-
throw new Error("invalid properties");
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
var makeDynamicTable = ({ typename, jsonSchema, properties }) => {
|
|
321
|
-
const view = createView({
|
|
322
|
-
name: "dynamic-table",
|
|
323
|
-
typename,
|
|
324
|
-
jsonSchema,
|
|
325
|
-
...properties && {
|
|
326
|
-
fields: properties.map((property) => property.name)
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
const table = Obj.make(TableType, {
|
|
330
|
-
name: "dynamic-table",
|
|
331
|
-
view: Ref.make(view)
|
|
332
|
-
});
|
|
333
|
-
const projection = new ViewProjection(jsonSchema, view);
|
|
334
|
-
if (properties && view.fields) {
|
|
335
|
-
setProperties(view, projection, properties);
|
|
336
|
-
}
|
|
337
|
-
return {
|
|
338
|
-
table,
|
|
339
|
-
projection
|
|
340
|
-
};
|
|
341
|
-
};
|
|
342
|
-
var setProperties = (view, projection, properties) => {
|
|
343
|
-
for (const property of properties) {
|
|
344
|
-
const field = view.fields.find((field2) => field2.path === property.name);
|
|
345
|
-
if (field) {
|
|
346
|
-
if (property.size !== void 0) {
|
|
347
|
-
field.size = property.size;
|
|
348
|
-
}
|
|
349
|
-
if (property.title !== void 0) {
|
|
350
|
-
const fieldProjection = projection.getFieldProjection(field.id);
|
|
351
|
-
projection.setFieldProjection({
|
|
352
|
-
...fieldProjection,
|
|
353
|
-
props: {
|
|
354
|
-
...fieldProjection.props,
|
|
355
|
-
title: property.title
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
if (property.sort) {
|
|
360
|
-
const fieldId = field.id;
|
|
361
|
-
view.query.sort = [
|
|
362
|
-
{
|
|
363
|
-
fieldId,
|
|
364
|
-
direction: property.sort
|
|
365
|
-
}
|
|
366
|
-
];
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// src/util/signals.ts
|
|
373
|
-
var touch = (value) => value;
|
|
374
|
-
|
|
375
|
-
// src/util/table.ts
|
|
376
|
-
import { Schema as Schema2 } from "effect";
|
|
377
|
-
import { Format, FormatEnum, Ref as Ref2, TypedObject, TypeEnum } from "@dxos/echo-schema";
|
|
378
|
-
import { PublicKey } from "@dxos/react-client";
|
|
379
|
-
import { live as live2 } from "@dxos/react-client/echo";
|
|
380
|
-
import { createFieldId, createView as createView2, getSchemaProperties, ViewProjection as ViewProjection2 } from "@dxos/schema";
|
|
381
|
-
var initializeTable = async ({ client, space, table, typename, initialRow = true }) => {
|
|
382
|
-
if (typename) {
|
|
383
|
-
const schema = await space.db.graph.getSchemaByTypename(typename, space.db);
|
|
384
|
-
if (!schema) {
|
|
385
|
-
throw new Error(`Schema not found: ${typename}`);
|
|
386
|
-
}
|
|
387
|
-
const fields = getSchemaProperties(schema.ast).map((prop) => prop.name);
|
|
388
|
-
table.view = Ref2.make(createView2({
|
|
389
|
-
// TODO(ZaymonFC): Don't hardcode name?
|
|
390
|
-
name: "View",
|
|
391
|
-
typename: schema.typename,
|
|
392
|
-
jsonSchema: schema.jsonSchema,
|
|
393
|
-
fields
|
|
394
|
-
}));
|
|
395
|
-
return schema;
|
|
396
|
-
} else {
|
|
397
|
-
const [schema] = await space.db.schemaRegistry.register([
|
|
398
|
-
createContactSchema()
|
|
399
|
-
]);
|
|
400
|
-
const fields = ContactFields;
|
|
401
|
-
table.view = Ref2.make(createView2({
|
|
402
|
-
name: "View",
|
|
403
|
-
typename: schema.typename,
|
|
404
|
-
jsonSchema: schema.jsonSchema,
|
|
405
|
-
fields
|
|
406
|
-
}));
|
|
407
|
-
createProjection(schema, table.view.target);
|
|
408
|
-
if (initialRow) {
|
|
409
|
-
space.db.add(live2(schema, {}));
|
|
410
|
-
}
|
|
411
|
-
return schema;
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
var createContactSchema = () => TypedObject({
|
|
415
|
-
typename: `example.com/type/${PublicKey.random().truncate()}`,
|
|
416
|
-
version: "0.1.0"
|
|
417
|
-
})({
|
|
418
|
-
name: Schema2.optional(Schema2.String).annotations({
|
|
419
|
-
title: "Name"
|
|
420
|
-
}),
|
|
421
|
-
active: Schema2.optional(Schema2.Boolean).annotations({
|
|
422
|
-
title: "Active"
|
|
423
|
-
}),
|
|
424
|
-
email: Schema2.optional(Format.Email),
|
|
425
|
-
salary: Schema2.optional(Format.Currency()).annotations({
|
|
426
|
-
title: "Salary"
|
|
427
|
-
})
|
|
428
|
-
});
|
|
429
|
-
var ContactFields = [
|
|
430
|
-
"name",
|
|
431
|
-
"email",
|
|
432
|
-
"salary",
|
|
433
|
-
"active"
|
|
434
|
-
];
|
|
435
|
-
var createProjection = (schema, view) => {
|
|
436
|
-
const projection = new ViewProjection2(schema.jsonSchema, view);
|
|
437
|
-
projection.setFieldProjection({
|
|
438
|
-
field: {
|
|
439
|
-
id: view.fields.find((f) => f.path === "salary").id,
|
|
440
|
-
path: "salary",
|
|
441
|
-
size: 150
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
projection.setFieldProjection({
|
|
445
|
-
field: {
|
|
446
|
-
id: view.fields.find((f) => f.path === "active").id,
|
|
447
|
-
path: "active",
|
|
448
|
-
size: 100
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
projection.setFieldProjection({
|
|
452
|
-
field: {
|
|
453
|
-
id: createFieldId(),
|
|
454
|
-
path: "manager",
|
|
455
|
-
referencePath: "name"
|
|
456
|
-
},
|
|
457
|
-
props: {
|
|
458
|
-
property: "manager",
|
|
459
|
-
type: TypeEnum.Ref,
|
|
460
|
-
format: FormatEnum.Ref,
|
|
461
|
-
referenceSchema: schema.typename,
|
|
462
|
-
title: "Manager"
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
return projection;
|
|
466
|
-
};
|
|
467
|
-
|
|
468
|
-
// src/util/table-buttons.ts
|
|
469
|
-
var BUTTON_IDENTIFIERS = {
|
|
470
|
-
columnSettings: "data-table-column-settings-button",
|
|
471
|
-
newColumn: "data-table-new-column-button",
|
|
472
|
-
referencedCell: "data-table-ref-cell-button",
|
|
473
|
-
rowMenu: "data-table-row-menu-button",
|
|
474
|
-
saveDraftRow: "data-table-save-draft-row-button",
|
|
475
|
-
sort: "data-table-sort-button"
|
|
476
|
-
};
|
|
477
|
-
var createButton = ({ attr, data, disabled = false, icon, testId, type = "primary" }) => {
|
|
478
|
-
const dataAttrs = Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
479
|
-
const positionClass = type === "primary" ? "inline-end-2" : "inline-end-9";
|
|
480
|
-
return `<button ${attr} data-testid="${testId}" class="dx-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 ${positionClass}" ${dataAttrs} ${disabled ? "disabled" : ""}><svg data-size="4"><use href="/icons.svg#${icon}"/></svg></button>`;
|
|
481
|
-
};
|
|
482
|
-
var addColumnButton = {
|
|
483
|
-
attr: BUTTON_IDENTIFIERS.newColumn,
|
|
484
|
-
icon: "ph--plus--regular",
|
|
485
|
-
render: ({ disabled }) => {
|
|
486
|
-
return createButton({
|
|
487
|
-
attr: BUTTON_IDENTIFIERS.newColumn,
|
|
488
|
-
icon: addColumnButton.icon,
|
|
489
|
-
disabled,
|
|
490
|
-
data: {},
|
|
491
|
-
testId: "table-new-column-button"
|
|
492
|
-
});
|
|
493
|
-
},
|
|
494
|
-
getData: (_el) => ({
|
|
495
|
-
type: "newColumn"
|
|
496
|
-
})
|
|
497
|
-
};
|
|
498
|
-
var columnSettingsButton = {
|
|
499
|
-
attr: BUTTON_IDENTIFIERS.columnSettings,
|
|
500
|
-
icon: "ph--caret-down--regular",
|
|
501
|
-
render: ({ fieldId }) => {
|
|
502
|
-
return createButton({
|
|
503
|
-
attr: BUTTON_IDENTIFIERS.columnSettings,
|
|
504
|
-
icon: columnSettingsButton.icon,
|
|
505
|
-
data: {
|
|
506
|
-
"data-field-id": fieldId
|
|
507
|
-
},
|
|
508
|
-
testId: "table-column-settings-button"
|
|
509
|
-
});
|
|
510
|
-
},
|
|
511
|
-
getData: (el) => ({
|
|
512
|
-
type: "columnSettings",
|
|
513
|
-
fieldId: el.getAttribute("data-field-id")
|
|
514
|
-
})
|
|
515
|
-
};
|
|
516
|
-
var rowMenuButton = {
|
|
517
|
-
attr: BUTTON_IDENTIFIERS.rowMenu,
|
|
518
|
-
icon: "ph--dots-three--regular",
|
|
519
|
-
render: ({ rowIndex }) => {
|
|
520
|
-
return createButton({
|
|
521
|
-
attr: BUTTON_IDENTIFIERS.rowMenu,
|
|
522
|
-
icon: rowMenuButton.icon,
|
|
523
|
-
data: {
|
|
524
|
-
"data-row-index": rowIndex.toString()
|
|
525
|
-
},
|
|
526
|
-
testId: "table-row-menu-button"
|
|
527
|
-
});
|
|
528
|
-
},
|
|
529
|
-
getData: (el) => ({
|
|
530
|
-
type: "rowMenu",
|
|
531
|
-
rowIndex: Number(el.getAttribute("data-row-index"))
|
|
532
|
-
})
|
|
533
|
-
};
|
|
534
|
-
var sortButton = {
|
|
535
|
-
attr: BUTTON_IDENTIFIERS.sort,
|
|
536
|
-
icon: "ph--sort-ascending--regular",
|
|
537
|
-
render: ({ fieldId, direction }) => {
|
|
538
|
-
return createButton({
|
|
539
|
-
attr: BUTTON_IDENTIFIERS.sort,
|
|
540
|
-
icon: direction === "desc" ? "ph--sort-descending--regular" : "ph--sort-ascending--regular",
|
|
541
|
-
data: {
|
|
542
|
-
"data-field-id": fieldId,
|
|
543
|
-
"data-direction": direction ?? ""
|
|
544
|
-
},
|
|
545
|
-
testId: "table-sort-button",
|
|
546
|
-
// TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
|
|
547
|
-
// This is a bit hacky.
|
|
548
|
-
type: "secondary"
|
|
549
|
-
});
|
|
550
|
-
},
|
|
551
|
-
getData: (el) => ({
|
|
552
|
-
type: "sort",
|
|
553
|
-
fieldId: el.getAttribute("data-field-id"),
|
|
554
|
-
direction: el.getAttribute("data-direction")
|
|
555
|
-
})
|
|
556
|
-
};
|
|
557
|
-
var saveDraftRowButton = {
|
|
558
|
-
attr: BUTTON_IDENTIFIERS.saveDraftRow,
|
|
559
|
-
icon: "ph--floppy-disk--regular",
|
|
560
|
-
render: ({ rowIndex, disabled }) => {
|
|
561
|
-
return createButton({
|
|
562
|
-
attr: BUTTON_IDENTIFIERS.saveDraftRow,
|
|
563
|
-
icon: saveDraftRowButton.icon,
|
|
564
|
-
disabled,
|
|
565
|
-
data: {
|
|
566
|
-
"data-row-index": rowIndex.toString()
|
|
567
|
-
},
|
|
568
|
-
testId: "table-save-draft-row-button"
|
|
569
|
-
});
|
|
570
|
-
},
|
|
571
|
-
getData: (el) => ({
|
|
572
|
-
type: "saveDraftRow",
|
|
573
|
-
rowIndex: Number(el.getAttribute("data-row-index"))
|
|
574
|
-
})
|
|
575
|
-
};
|
|
576
|
-
var tableButtons = {
|
|
577
|
-
addColumn: addColumnButton,
|
|
578
|
-
columnSettings: columnSettingsButton,
|
|
579
|
-
rowMenu: rowMenuButton,
|
|
580
|
-
saveDraftRow: saveDraftRowButton,
|
|
581
|
-
sort: sortButton
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
// src/util/table-controls.ts
|
|
585
|
-
var CONTROL_IDENTIFIERS = {
|
|
586
|
-
checkbox: "data-table-checkbox",
|
|
587
|
-
switch: "data-table-switch"
|
|
588
|
-
};
|
|
589
|
-
var BASE_CLASSES = {
|
|
590
|
-
checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] dx-checkbox",
|
|
591
|
-
switch: "absolute inset-block-[.25rem] inline-end-[.25rem] dx-checkbox--switch"
|
|
592
|
-
};
|
|
593
|
-
var renderAttributes = (data) => {
|
|
594
|
-
return Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
595
|
-
};
|
|
596
|
-
var renderInput = (baseClass, attrs, checked = false, disabled = false, preventToggle = false, testId) => {
|
|
597
|
-
return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? "checked" : ""} ${preventToggle ? 'onclick="return false"' : ""} ${disabled ? "disabled" : ""} data-testid="${testId}"/>`;
|
|
598
|
-
};
|
|
599
|
-
var CheckboxStory = ({ rowIndex, header = false, checked = false, disabled = false }) => {
|
|
600
|
-
const attrs = {
|
|
601
|
-
[CONTROL_IDENTIFIERS.checkbox]: "",
|
|
602
|
-
"data-row-index": rowIndex.toString(),
|
|
603
|
-
...header && {
|
|
604
|
-
"data-header": ""
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
return renderInput(BASE_CLASSES.checkbox, attrs, checked, disabled, true, "table-selection");
|
|
608
|
-
};
|
|
609
|
-
var SwitchStory = ({ colIndex, rowIndex, checked = false, disabled = false }) => {
|
|
610
|
-
const attrs = {
|
|
611
|
-
[CONTROL_IDENTIFIERS.switch]: "",
|
|
612
|
-
"data-row-index": rowIndex.toString(),
|
|
613
|
-
"data-col-index": colIndex.toString()
|
|
614
|
-
};
|
|
615
|
-
return renderInput(BASE_CLASSES.switch, attrs, checked, disabled, true, "table-switch");
|
|
616
|
-
};
|
|
617
|
-
var tableControls = {
|
|
618
|
-
checkbox: {
|
|
619
|
-
attr: CONTROL_IDENTIFIERS.checkbox,
|
|
620
|
-
render: CheckboxStory,
|
|
621
|
-
getData: (el) => ({
|
|
622
|
-
type: "checkbox",
|
|
623
|
-
rowIndex: Number(el.getAttribute("data-row-index")),
|
|
624
|
-
header: el.hasAttribute("data-header")
|
|
625
|
-
})
|
|
626
|
-
},
|
|
627
|
-
switch: {
|
|
628
|
-
attr: CONTROL_IDENTIFIERS.switch,
|
|
629
|
-
render: SwitchStory,
|
|
630
|
-
getData: (el) => ({
|
|
631
|
-
type: "switch",
|
|
632
|
-
rowIndex: Number(el.getAttribute("data-row-index")),
|
|
633
|
-
colIndex: Number(el.getAttribute("data-col-index"))
|
|
634
|
-
})
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
|
|
638
|
-
// src/util/schema.ts
|
|
639
|
-
import { Schema as Schema3 } from "effect";
|
|
640
|
-
import { isTypeLiteral, TypeLiteral } from "effect/SchemaAST";
|
|
641
|
-
var narrowSchema = (schema, paths) => {
|
|
642
|
-
const ast = schema?.ast;
|
|
643
|
-
if (isTypeLiteral(ast)) {
|
|
644
|
-
const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
|
|
645
|
-
if (propertySignatures.length > 0) {
|
|
646
|
-
const narrowType = new TypeLiteral(propertySignatures, []);
|
|
647
|
-
return Schema3.make(narrowType);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
return void 0;
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
// src/model/selection-model.ts
|
|
654
|
-
var SelectionModel = class extends Resource {
|
|
655
|
-
constructor(_rows, _selectionMode, _onSelectionChanged) {
|
|
656
|
-
super(), this._rows = _rows, this._selectionMode = _selectionMode, this._onSelectionChanged = _onSelectionChanged, this._selection = signal(/* @__PURE__ */ new Set()), this._validSelectedIds = computed(() => {
|
|
657
|
-
const rows = this._rows.value;
|
|
658
|
-
if (!rows) {
|
|
659
|
-
return /* @__PURE__ */ new Set();
|
|
660
|
-
}
|
|
661
|
-
const validIds = new Set(rows.map((row) => row.id));
|
|
662
|
-
return new Set([
|
|
663
|
-
...this._selection.value
|
|
664
|
-
].filter((id) => validIds.has(id)));
|
|
665
|
-
}), this._hasSelection = computed(() => this._validSelectedIds.value.size > 0), this._allSelected = computed(() => {
|
|
666
|
-
const rows = this._rows.value;
|
|
667
|
-
if (rows.length === 0) {
|
|
668
|
-
return false;
|
|
669
|
-
}
|
|
670
|
-
return rows.every((row) => this._selection.value.has(row.id));
|
|
671
|
-
}), this.getSelectedRows = () => {
|
|
672
|
-
const selectedIds = this._validSelectedIds.value;
|
|
673
|
-
return this._rows.value.filter((row) => selectedIds.has(row.id));
|
|
674
|
-
}, this.isRowIndexSelected = (rowIndex) => {
|
|
675
|
-
const row = this._rows.value[rowIndex];
|
|
676
|
-
return this._selection.value.has(row.id);
|
|
677
|
-
}, this.isObjectSelected = (object) => {
|
|
678
|
-
return this._selection.value.has(object.id);
|
|
679
|
-
}, this.toggleSelectionForRowIndex = (rowIndex) => {
|
|
680
|
-
const row = this._rows.value[rowIndex];
|
|
681
|
-
const newSelection = new Set(this._selection.value);
|
|
682
|
-
if (newSelection.has(row.id)) {
|
|
683
|
-
newSelection.delete(row.id);
|
|
684
|
-
} else {
|
|
685
|
-
if (this._selectionMode === "single") {
|
|
686
|
-
newSelection.clear();
|
|
687
|
-
}
|
|
688
|
-
newSelection.add(row.id);
|
|
689
|
-
}
|
|
690
|
-
this._selection.value = newSelection;
|
|
691
|
-
}, this.setSelection = (mode) => {
|
|
692
|
-
switch (mode) {
|
|
693
|
-
case "all": {
|
|
694
|
-
this._selection.value = new Set(this._rows.value.map((row) => row.id));
|
|
695
|
-
break;
|
|
696
|
-
}
|
|
697
|
-
case "none": {
|
|
698
|
-
this._selection.value = /* @__PURE__ */ new Set();
|
|
699
|
-
break;
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
async _open() {
|
|
705
|
-
const selectionWatcher = effect(() => {
|
|
706
|
-
touch(this._selection.value);
|
|
707
|
-
this._onSelectionChanged?.();
|
|
708
|
-
});
|
|
709
|
-
this._ctx.onDispose(selectionWatcher);
|
|
710
|
-
}
|
|
711
|
-
//
|
|
712
|
-
// Getters
|
|
713
|
-
//
|
|
714
|
-
get selection() {
|
|
715
|
-
return this._selection;
|
|
716
|
-
}
|
|
717
|
-
get hasSelection() {
|
|
718
|
-
return this._hasSelection;
|
|
719
|
-
}
|
|
720
|
-
get allRowsSeleted() {
|
|
721
|
-
return this._allSelected;
|
|
722
|
-
}
|
|
723
|
-
get selectionMode() {
|
|
724
|
-
return this._selectionMode;
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
|
|
728
|
-
// src/model/table-sorting.ts
|
|
729
|
-
import { computed as computed2, signal as signal2 } from "@preact/signals-core";
|
|
730
|
-
import orderBy from "lodash.orderby";
|
|
731
|
-
import { getValue, FormatEnum as FormatEnum2, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
|
|
732
|
-
import { formatForDisplay } from "@dxos/react-ui-form";
|
|
733
|
-
var TableSorting = class {
|
|
734
|
-
constructor(rows, _view, _projection) {
|
|
735
|
-
this._view = _view;
|
|
736
|
-
this._projection = _projection;
|
|
737
|
-
this._displayToDataIndex = /* @__PURE__ */ new Map();
|
|
738
|
-
this._localSort = signal2(void 0);
|
|
739
|
-
this._rows = rows;
|
|
740
|
-
this._isDirty = computed2(() => {
|
|
741
|
-
const local = this._localSort.value;
|
|
742
|
-
const viewSort = this._view?.query.sort?.[0];
|
|
743
|
-
if (local?.type === "cleared") {
|
|
744
|
-
return viewSort !== void 0;
|
|
745
|
-
}
|
|
746
|
-
if (local?.type === "active") {
|
|
747
|
-
if (!viewSort) {
|
|
748
|
-
return true;
|
|
749
|
-
}
|
|
750
|
-
return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
|
|
751
|
-
}
|
|
752
|
-
return false;
|
|
753
|
-
});
|
|
754
|
-
this.sortedRows = this.initialiseSortedRows();
|
|
755
|
-
}
|
|
756
|
-
/**
|
|
757
|
-
* @reactive
|
|
758
|
-
* @returns Local sort if present, falls back to view sort.
|
|
759
|
-
*/
|
|
760
|
-
get sorting() {
|
|
761
|
-
const local = this._localSort.value;
|
|
762
|
-
if (local?.type === "cleared") {
|
|
763
|
-
return void 0;
|
|
764
|
-
}
|
|
765
|
-
if (local?.type === "active") {
|
|
766
|
-
return local.sort;
|
|
767
|
-
}
|
|
768
|
-
return this._view?.query.sort?.[0];
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* @reactive
|
|
772
|
-
* @returns Whether local sort differs from view sort.
|
|
773
|
-
*/
|
|
774
|
-
get isDirty() {
|
|
775
|
-
return this._isDirty.value;
|
|
776
|
-
}
|
|
777
|
-
getDataIndex(displayIndex) {
|
|
778
|
-
return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
|
|
779
|
-
}
|
|
780
|
-
setSort(fieldId, direction) {
|
|
781
|
-
this._localSort.value = {
|
|
782
|
-
type: "active",
|
|
783
|
-
sort: {
|
|
784
|
-
fieldId,
|
|
785
|
-
direction
|
|
786
|
-
}
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
toggleSort(fieldId) {
|
|
790
|
-
if (!this._view || !this.sorting || this.sorting.fieldId !== fieldId) {
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
switch (this.sorting.direction) {
|
|
794
|
-
case "asc": {
|
|
795
|
-
this.setSort(fieldId, "desc");
|
|
796
|
-
break;
|
|
797
|
-
}
|
|
798
|
-
case "desc": {
|
|
799
|
-
this.setSort(fieldId, "asc");
|
|
800
|
-
break;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
clearSort() {
|
|
805
|
-
this._localSort.value = {
|
|
806
|
-
type: "cleared"
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
save() {
|
|
810
|
-
if (this._view && this._localSort.value !== void 0) {
|
|
811
|
-
if (this._localSort.value.type === "active") {
|
|
812
|
-
this._view.query.sort = [
|
|
813
|
-
this._localSort.value.sort
|
|
814
|
-
];
|
|
815
|
-
} else {
|
|
816
|
-
this._view.query.sort = [];
|
|
817
|
-
}
|
|
818
|
-
this._localSort.value = void 0;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
//
|
|
822
|
-
// Initialisation.
|
|
823
|
-
//
|
|
824
|
-
initialiseSortedRows() {
|
|
825
|
-
return computed2(() => {
|
|
826
|
-
this._displayToDataIndex.clear();
|
|
827
|
-
const sort = this.sorting;
|
|
828
|
-
if (!sort || !this._view) {
|
|
829
|
-
return this._rows.value;
|
|
830
|
-
}
|
|
831
|
-
const field = this._view.fields.find((f) => f.id === sort.fieldId);
|
|
832
|
-
if (!field) {
|
|
833
|
-
return this._rows.value;
|
|
834
|
-
}
|
|
835
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
836
|
-
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
837
|
-
const sortValue = this.getSortValue(props, field, item);
|
|
838
|
-
const isEmpty = !(props.type === TypeEnum2.Boolean) && (sortValue == null || sortValue === "");
|
|
839
|
-
return {
|
|
840
|
-
item,
|
|
841
|
-
index,
|
|
842
|
-
value: sortValue,
|
|
843
|
-
isEmpty
|
|
844
|
-
};
|
|
845
|
-
});
|
|
846
|
-
const ordered = orderBy(dataWithIndices, [
|
|
847
|
-
"isEmpty",
|
|
848
|
-
"value"
|
|
849
|
-
], [
|
|
850
|
-
"asc",
|
|
851
|
-
sort.direction
|
|
852
|
-
]);
|
|
853
|
-
ordered.forEach(({ index: dataIndex }, displayIndex) => {
|
|
854
|
-
if (displayIndex !== dataIndex) {
|
|
855
|
-
this._displayToDataIndex.set(displayIndex, dataIndex);
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
return ordered.map(({ item }) => item);
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
getSortValue(props, field, item) {
|
|
862
|
-
const { type, format } = props;
|
|
863
|
-
const value = getValue(item, field.path);
|
|
864
|
-
if (type === TypeEnum2.Boolean) {
|
|
865
|
-
return !!value;
|
|
866
|
-
}
|
|
867
|
-
if (type === TypeEnum2.Number) {
|
|
868
|
-
return value;
|
|
869
|
-
}
|
|
870
|
-
if (value == null) {
|
|
871
|
-
return value;
|
|
872
|
-
}
|
|
873
|
-
if (format === FormatEnum2.SingleSelect) {
|
|
874
|
-
const options = props.options;
|
|
875
|
-
if (!options) {
|
|
876
|
-
return void 0;
|
|
877
|
-
}
|
|
878
|
-
const option = options.find((option2) => option2.id === value);
|
|
879
|
-
return option ? options.indexOf(option) : void 0;
|
|
880
|
-
}
|
|
881
|
-
if (format === FormatEnum2.Ref && field.referencePath) {
|
|
882
|
-
const refValue = getValue(value.target, field.referencePath);
|
|
883
|
-
return refValue;
|
|
884
|
-
}
|
|
885
|
-
return formatForDisplay({
|
|
886
|
-
type,
|
|
887
|
-
format,
|
|
888
|
-
value
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
|
|
893
|
-
// src/util/tag.ts
|
|
894
|
-
var TAG_PATTERN_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
895
|
-
var extractTagIds = (value) => {
|
|
896
|
-
if (typeof value !== "string") {
|
|
897
|
-
return void 0;
|
|
898
|
-
}
|
|
899
|
-
const ids = [];
|
|
900
|
-
let match;
|
|
901
|
-
while ((match = TAG_PATTERN_REGEX.exec(value)) !== null) {
|
|
902
|
-
if (match[2]) {
|
|
903
|
-
ids.push(match[2]);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
return ids.length > 0 ? ids : void 0;
|
|
907
|
-
};
|
|
908
|
-
|
|
909
|
-
// src/model/table-model.ts
|
|
910
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
|
|
911
|
-
var defaultFeatures = {
|
|
912
|
-
selection: {
|
|
913
|
-
enabled: true,
|
|
914
|
-
mode: "multiple"
|
|
915
|
-
},
|
|
916
|
-
dataEditable: false,
|
|
917
|
-
schemaEditable: false
|
|
918
|
-
};
|
|
919
|
-
var TableModel = class extends Resource2 {
|
|
920
|
-
constructor({ id, space, view, projection, features = {}, sorting = [], pinnedRows = {
|
|
921
|
-
top: [],
|
|
922
|
-
bottom: []
|
|
923
|
-
}, rowActions = [], onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChange, onRowAction }) {
|
|
924
|
-
super();
|
|
925
|
-
this._visibleRange = signal3({
|
|
926
|
-
start: {
|
|
927
|
-
row: 0,
|
|
928
|
-
col: 0
|
|
929
|
-
},
|
|
930
|
-
end: {
|
|
931
|
-
row: 0,
|
|
932
|
-
col: 0
|
|
933
|
-
}
|
|
934
|
-
});
|
|
935
|
-
this._rows = signal3([]);
|
|
936
|
-
this._draftRows = signal3([]);
|
|
937
|
-
//
|
|
938
|
-
// Data
|
|
939
|
-
//
|
|
940
|
-
this.setRows = (obj) => {
|
|
941
|
-
this._rows.value = obj;
|
|
942
|
-
};
|
|
943
|
-
this.getRowCount = () => this._rows.value.length;
|
|
944
|
-
/**
|
|
945
|
-
* @reactive
|
|
946
|
-
*/
|
|
947
|
-
this.getDraftRowCount = () => this._draftRows.value.length;
|
|
948
|
-
this.getColumnCount = () => this._view?.fields.length ?? 0;
|
|
949
|
-
this.insertRow = () => {
|
|
950
|
-
const result = this._onInsertRow?.();
|
|
951
|
-
if (result === false && this._draftRows.value.length === 0) {
|
|
952
|
-
this.createDraftRow();
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
this.commitDraftRow = (draftRowIndex) => {
|
|
956
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.value.length) {
|
|
957
|
-
return false;
|
|
958
|
-
}
|
|
959
|
-
const draftRow = this._draftRows.value[draftRowIndex];
|
|
960
|
-
if (!draftRow.valid) {
|
|
961
|
-
return false;
|
|
962
|
-
}
|
|
963
|
-
const success = this._onInsertRow?.(draftRow.data);
|
|
964
|
-
if (success) {
|
|
965
|
-
const newDraftRows = this._draftRows.value.filter((_, index) => index !== draftRowIndex);
|
|
966
|
-
this._draftRows.value = newDraftRows;
|
|
967
|
-
}
|
|
968
|
-
return success ?? false;
|
|
969
|
-
};
|
|
970
|
-
this.deleteRow = (rowIndex) => {
|
|
971
|
-
const row = this._sorting.getDataIndex(rowIndex);
|
|
972
|
-
const obj = this._rows.value[row];
|
|
973
|
-
const objectsToDelete = [];
|
|
974
|
-
if (this.features.selection.enabled && this._selection.hasSelection.value) {
|
|
975
|
-
const selectedRows = this._selection.getSelectedRows();
|
|
976
|
-
objectsToDelete.push(...selectedRows);
|
|
977
|
-
} else {
|
|
978
|
-
objectsToDelete.push(obj);
|
|
979
|
-
}
|
|
980
|
-
this._onDeleteRows?.(row, objectsToDelete);
|
|
981
|
-
};
|
|
982
|
-
this.handleRowAction = (actionId, rowIndex) => {
|
|
983
|
-
if (!this._onRowAction) {
|
|
984
|
-
return;
|
|
985
|
-
}
|
|
986
|
-
const row = this._sorting.getDataIndex(rowIndex);
|
|
987
|
-
const data = this._rows.value[row];
|
|
988
|
-
if (data) {
|
|
989
|
-
this._onRowAction(actionId, data);
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
this.getCellData = (cell) => {
|
|
993
|
-
const { col, row, plane } = cell;
|
|
994
|
-
const fields = this._view?.fields ?? [];
|
|
995
|
-
if (col < 0 || col >= fields.length) {
|
|
996
|
-
return void 0;
|
|
997
|
-
}
|
|
998
|
-
const field = fields[col];
|
|
999
|
-
let value;
|
|
1000
|
-
if (plane === "frozenRowsEnd") {
|
|
1001
|
-
if (row >= 0 && row < this._draftRows.value.length) {
|
|
1002
|
-
value = getValue2(this._draftRows.value[row].data, field.path);
|
|
1003
|
-
} else {
|
|
1004
|
-
return void 0;
|
|
1005
|
-
}
|
|
1006
|
-
} else {
|
|
1007
|
-
const dataIndex = this._sorting.getDataIndex(row);
|
|
1008
|
-
value = getValue2(this._rows.value[dataIndex], field.path);
|
|
1009
|
-
}
|
|
1010
|
-
if (value == null) {
|
|
1011
|
-
return "";
|
|
1012
|
-
}
|
|
1013
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
1014
|
-
switch (props.format) {
|
|
1015
|
-
case FormatEnum3.Ref: {
|
|
1016
|
-
if (!field.referencePath) {
|
|
1017
|
-
return "";
|
|
1018
|
-
}
|
|
1019
|
-
return getValue2(value.target, field.referencePath);
|
|
1020
|
-
}
|
|
1021
|
-
default: {
|
|
1022
|
-
return formatForEditing({
|
|
1023
|
-
type: props.type,
|
|
1024
|
-
format: props.format,
|
|
1025
|
-
value
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
this.validateCellData = async ({ col, row, plane }, value) => {
|
|
1031
|
-
const fields = this._view?.fields ?? [];
|
|
1032
|
-
if (col < 0 || col >= fields.length) {
|
|
1033
|
-
return {
|
|
1034
|
-
valid: false,
|
|
1035
|
-
error: "Invalid column index"
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
const field = fields[col];
|
|
1039
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
1040
|
-
const transformedValue = editorTextToCellValue(props, value);
|
|
1041
|
-
const isDraftRow = plane === "frozenRowsEnd";
|
|
1042
|
-
if (isDraftRow) {
|
|
1043
|
-
const isInvalidDraftRowIndex = row < 0 || row >= this._draftRows.value.length;
|
|
1044
|
-
if (isInvalidDraftRowIndex) {
|
|
1045
|
-
return {
|
|
1046
|
-
valid: false,
|
|
1047
|
-
error: "Invalid draft row index"
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
const draftRow = this._draftRows.value[row];
|
|
1051
|
-
const snapshot = {
|
|
1052
|
-
...draftRow.data
|
|
1053
|
-
};
|
|
1054
|
-
setValue(snapshot, field.path, transformedValue);
|
|
1055
|
-
const validationErrors = this.validateDraftRowData(snapshot);
|
|
1056
|
-
if (validationErrors.length > 0) {
|
|
1057
|
-
const error = validationErrors.find((err) => err.path === field.path);
|
|
1058
|
-
if (error) {
|
|
1059
|
-
return {
|
|
1060
|
-
valid: false,
|
|
1061
|
-
error: error.message
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
return {
|
|
1066
|
-
valid: true
|
|
1067
|
-
};
|
|
1068
|
-
} else {
|
|
1069
|
-
const rowIdx = this._sorting.getDataIndex(row);
|
|
1070
|
-
const currentRow = this._rows.value[rowIdx];
|
|
1071
|
-
invariant(currentRow, "Invalid row index", {
|
|
1072
|
-
F: __dxlog_file,
|
|
1073
|
-
L: 507,
|
|
1074
|
-
S: this,
|
|
1075
|
-
A: [
|
|
1076
|
-
"currentRow",
|
|
1077
|
-
"'Invalid row index'"
|
|
1078
|
-
]
|
|
1079
|
-
});
|
|
1080
|
-
const snapshot = getSnapshot(currentRow);
|
|
1081
|
-
setValue(snapshot, field.path, transformedValue);
|
|
1082
|
-
const schema = getSchema(currentRow);
|
|
1083
|
-
invariant(schema, void 0, {
|
|
1084
|
-
F: __dxlog_file,
|
|
1085
|
-
L: 513,
|
|
1086
|
-
S: this,
|
|
1087
|
-
A: [
|
|
1088
|
-
"schema",
|
|
1089
|
-
""
|
|
1090
|
-
]
|
|
1091
|
-
});
|
|
1092
|
-
const validationResult = validateSchema(schema, snapshot);
|
|
1093
|
-
if (validationResult && validationResult.length > 0) {
|
|
1094
|
-
const error = validationResult[0];
|
|
1095
|
-
invariant(error.path === field.path, void 0, {
|
|
1096
|
-
F: __dxlog_file,
|
|
1097
|
-
L: 518,
|
|
1098
|
-
S: this,
|
|
1099
|
-
A: [
|
|
1100
|
-
"error.path === field.path",
|
|
1101
|
-
""
|
|
1102
|
-
]
|
|
1103
|
-
});
|
|
1104
|
-
return {
|
|
1105
|
-
valid: false,
|
|
1106
|
-
error: error.message
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
return {
|
|
1110
|
-
valid: true
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
1113
|
-
};
|
|
1114
|
-
this.setCellData = (cell, value) => {
|
|
1115
|
-
const { col, row, plane } = cell;
|
|
1116
|
-
const fields = this._view?.fields ?? [];
|
|
1117
|
-
if (col < 0 || col >= fields.length) {
|
|
1118
|
-
return;
|
|
1119
|
-
}
|
|
1120
|
-
const field = fields[col];
|
|
1121
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
1122
|
-
const transformedValue = editorTextToCellValue(props, value);
|
|
1123
|
-
if (props.format === FormatEnum3.Ref && !isLiveObject(value)) {
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
if (plane === "frozenRowsEnd") {
|
|
1127
|
-
if (row >= 0 && row < this._draftRows.value.length) {
|
|
1128
|
-
setValue(this._draftRows.value[row].data, field.path, transformedValue);
|
|
1129
|
-
this.validateDraftRow(row);
|
|
1130
|
-
}
|
|
1131
|
-
} else {
|
|
1132
|
-
const rowIdx = this._sorting.getDataIndex(row);
|
|
1133
|
-
setValue(this._rows.value[rowIdx], field.path, transformedValue);
|
|
1134
|
-
}
|
|
1135
|
-
};
|
|
1136
|
-
this._id = id;
|
|
1137
|
-
this._space = space;
|
|
1138
|
-
this._view = view;
|
|
1139
|
-
this._projection = projection;
|
|
1140
|
-
this._features = {
|
|
1141
|
-
...defaultFeatures,
|
|
1142
|
-
...features
|
|
1143
|
-
};
|
|
1144
|
-
invariant(!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === "single"), "Single selection is not compatible with editable tables.", {
|
|
1145
|
-
F: __dxlog_file,
|
|
1146
|
-
L: 145,
|
|
1147
|
-
S: this,
|
|
1148
|
-
A: [
|
|
1149
|
-
"!(this._features.dataEditable && this._features.selection.enabled && this._features.selection.mode === 'single')",
|
|
1150
|
-
"'Single selection is not compatible with editable tables.'"
|
|
1151
|
-
]
|
|
1152
|
-
});
|
|
1153
|
-
this._sorting = new TableSorting(this._rows, this._view, projection);
|
|
1154
|
-
if (sorting.length > 0) {
|
|
1155
|
-
const [sort] = sorting;
|
|
1156
|
-
this._sorting.setSort(sort.fieldId, sort.direction);
|
|
1157
|
-
}
|
|
1158
|
-
this._pinnedRows = pinnedRows;
|
|
1159
|
-
this._rowActions = rowActions;
|
|
1160
|
-
this._onInsertRow = onInsertRow;
|
|
1161
|
-
this._onDeleteRows = onDeleteRows;
|
|
1162
|
-
this._onDeleteColumn = onDeleteColumn;
|
|
1163
|
-
this._onCellUpdate = onCellUpdate;
|
|
1164
|
-
this._onRowOrderChange = onRowOrderChange;
|
|
1165
|
-
this._onRowAction = onRowAction;
|
|
1166
|
-
}
|
|
1167
|
-
get id() {
|
|
1168
|
-
return this._id;
|
|
1169
|
-
}
|
|
1170
|
-
get space() {
|
|
1171
|
-
return this._space;
|
|
1172
|
-
}
|
|
1173
|
-
get view() {
|
|
1174
|
-
return this._view;
|
|
1175
|
-
}
|
|
1176
|
-
get projection() {
|
|
1177
|
-
return this._projection;
|
|
1178
|
-
}
|
|
1179
|
-
get rows() {
|
|
1180
|
-
return this._sorting.sortedRows;
|
|
1181
|
-
}
|
|
1182
|
-
get features() {
|
|
1183
|
-
return this._features;
|
|
1184
|
-
}
|
|
1185
|
-
/**
|
|
1186
|
-
* @reactive
|
|
1187
|
-
*/
|
|
1188
|
-
get sorting() {
|
|
1189
|
-
return this._sorting;
|
|
1190
|
-
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Gets the row data at the specified display index.
|
|
1193
|
-
*/
|
|
1194
|
-
getRowAt(displayIndex) {
|
|
1195
|
-
if (displayIndex < 0 || displayIndex >= this._sorting.sortedRows.value.length) {
|
|
1196
|
-
return void 0;
|
|
1197
|
-
}
|
|
1198
|
-
return this._sorting.sortedRows.value[displayIndex];
|
|
1199
|
-
}
|
|
1200
|
-
get pinnedRows() {
|
|
1201
|
-
return this._pinnedRows;
|
|
1202
|
-
}
|
|
1203
|
-
get columnMeta() {
|
|
1204
|
-
invariant(this._columnMeta, void 0, {
|
|
1205
|
-
F: __dxlog_file,
|
|
1206
|
-
L: 214,
|
|
1207
|
-
S: this,
|
|
1208
|
-
A: [
|
|
1209
|
-
"this._columnMeta",
|
|
1210
|
-
""
|
|
1211
|
-
]
|
|
1212
|
-
});
|
|
1213
|
-
return this._columnMeta;
|
|
1214
|
-
}
|
|
1215
|
-
get selection() {
|
|
1216
|
-
return this._selection;
|
|
1217
|
-
}
|
|
1218
|
-
/** @reactive */
|
|
1219
|
-
get isViewDirty() {
|
|
1220
|
-
return this._sorting.isDirty;
|
|
1221
|
-
}
|
|
1222
|
-
get rowActions() {
|
|
1223
|
-
return this._rowActions;
|
|
1224
|
-
}
|
|
1225
|
-
//
|
|
1226
|
-
// Initialisation
|
|
1227
|
-
//
|
|
1228
|
-
async _open() {
|
|
1229
|
-
this.initializeColumnMeta();
|
|
1230
|
-
this.initializeEffects();
|
|
1231
|
-
this._selection = new SelectionModel(this._sorting.sortedRows, this._features.selection.mode ?? "multiple", () => this._onRowOrderChange?.());
|
|
1232
|
-
await this._selection.open(this._ctx);
|
|
1233
|
-
}
|
|
1234
|
-
initializeColumnMeta() {
|
|
1235
|
-
this._columnMeta = computed3(() => {
|
|
1236
|
-
const fields = this._view?.fields ?? [];
|
|
1237
|
-
const meta = Object.fromEntries(fields.map((field, index) => [
|
|
1238
|
-
index,
|
|
1239
|
-
{
|
|
1240
|
-
size: field?.size ?? 256,
|
|
1241
|
-
resizeable: true
|
|
1242
|
-
}
|
|
1243
|
-
]));
|
|
1244
|
-
return {
|
|
1245
|
-
grid: meta,
|
|
1246
|
-
frozenColsStart: {
|
|
1247
|
-
0: {
|
|
1248
|
-
size: 30,
|
|
1249
|
-
resizeable: false
|
|
1250
|
-
}
|
|
1251
|
-
},
|
|
1252
|
-
frozenColsEnd: {
|
|
1253
|
-
0: {
|
|
1254
|
-
size: 40,
|
|
1255
|
-
resizeable: false
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
};
|
|
1259
|
-
});
|
|
1260
|
-
}
|
|
1261
|
-
initializeEffects() {
|
|
1262
|
-
const rowOrderWatcher = effect2(() => {
|
|
1263
|
-
touch(this._sorting.sortedRows.value);
|
|
1264
|
-
this._onRowOrderChange?.();
|
|
1265
|
-
});
|
|
1266
|
-
this._ctx.onDispose(rowOrderWatcher);
|
|
1267
|
-
const rowEffectManager = effect2(() => {
|
|
1268
|
-
const { start, end } = touch(this._visibleRange.value);
|
|
1269
|
-
const rowEffects = [];
|
|
1270
|
-
for (let row = start.row; row <= end.row; row++) {
|
|
1271
|
-
rowEffects.push(effect2(() => {
|
|
1272
|
-
const obj = this._sorting.sortedRows.value[row];
|
|
1273
|
-
this._view?.fields.forEach((field) => touch(getValue2(obj, field.path)));
|
|
1274
|
-
this._onCellUpdate?.({
|
|
1275
|
-
row,
|
|
1276
|
-
col: start.col,
|
|
1277
|
-
plane: "grid"
|
|
1278
|
-
});
|
|
1279
|
-
}));
|
|
1280
|
-
}
|
|
1281
|
-
return () => rowEffects.forEach((cleanup) => cleanup());
|
|
1282
|
-
});
|
|
1283
|
-
this._ctx.onDispose(rowEffectManager);
|
|
1284
|
-
const draftRowsWatcher = effect2(() => {
|
|
1285
|
-
touch(this._draftRows.value);
|
|
1286
|
-
this._onRowOrderChange?.();
|
|
1287
|
-
});
|
|
1288
|
-
this._ctx.onDispose(draftRowsWatcher);
|
|
1289
|
-
}
|
|
1290
|
-
// TODO(ZaymonFC): Return .value instead of exposing the signal.
|
|
1291
|
-
get draftRows() {
|
|
1292
|
-
return this._draftRows;
|
|
1293
|
-
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Checks if a specific field path has validation errors for a given draft row.
|
|
1296
|
-
* @param draftRowIndex - The index of the draft row to check
|
|
1297
|
-
* @param fieldPath - The path of the field to check for validation errors
|
|
1298
|
-
* @returns true if the field has validation errors, false otherwise
|
|
1299
|
-
*/
|
|
1300
|
-
hasDraftRowValidationError(draftRowIndex, fieldPath) {
|
|
1301
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.value.length) {
|
|
1302
|
-
return false;
|
|
1303
|
-
}
|
|
1304
|
-
const draftRow = this._draftRows.value[draftRowIndex];
|
|
1305
|
-
const validationErrors = draftRow.validationErrors || [];
|
|
1306
|
-
return validationErrors.some((error) => error.path === fieldPath);
|
|
1307
|
-
}
|
|
1308
|
-
createDraftRow() {
|
|
1309
|
-
if (!this._view) {
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
const draftData = {
|
|
1313
|
-
id: ObjectId.random()
|
|
1314
|
-
};
|
|
1315
|
-
const validationErrors = this.validateDraftRowData(draftData);
|
|
1316
|
-
const draftRow = {
|
|
1317
|
-
data: draftData,
|
|
1318
|
-
valid: validationErrors.length === 0,
|
|
1319
|
-
validationErrors
|
|
1320
|
-
};
|
|
1321
|
-
this._draftRows.value = [
|
|
1322
|
-
...this._draftRows.value,
|
|
1323
|
-
draftRow
|
|
1324
|
-
];
|
|
1325
|
-
}
|
|
1326
|
-
validateDraftRow(draftRowIndex) {
|
|
1327
|
-
if (draftRowIndex < 0 || draftRowIndex >= this._draftRows.value.length) {
|
|
1328
|
-
return;
|
|
1329
|
-
}
|
|
1330
|
-
const draftRow = this._draftRows.value[draftRowIndex];
|
|
1331
|
-
const validationErrors = this.validateDraftRowData(draftRow.data);
|
|
1332
|
-
const updatedDraftRow = {
|
|
1333
|
-
...draftRow,
|
|
1334
|
-
valid: validationErrors.length === 0,
|
|
1335
|
-
validationErrors
|
|
1336
|
-
};
|
|
1337
|
-
const newDraftRows = [
|
|
1338
|
-
...this._draftRows.value
|
|
1339
|
-
];
|
|
1340
|
-
newDraftRows[draftRowIndex] = updatedDraftRow;
|
|
1341
|
-
this._draftRows.value = newDraftRows;
|
|
1342
|
-
}
|
|
1343
|
-
validateDraftRowData(data) {
|
|
1344
|
-
const schema = toEffectSchema(this._projection.schema);
|
|
1345
|
-
return validateSchema(schema, data) || [];
|
|
1346
|
-
}
|
|
1347
|
-
/**
|
|
1348
|
-
* Updates a cell's value using a transform function.
|
|
1349
|
-
* @param {DxGridPlanePosition} position - The position of the cell to update.
|
|
1350
|
-
* @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
|
|
1351
|
-
*/
|
|
1352
|
-
updateCellData({ col, row }, update) {
|
|
1353
|
-
const dataRow = this._sorting.getDataIndex(row);
|
|
1354
|
-
const fields = this._view?.fields ?? [];
|
|
1355
|
-
const field = fields[col];
|
|
1356
|
-
const value = getValue2(this._rows.value[dataRow], field.path);
|
|
1357
|
-
const updatedValue = update(value);
|
|
1358
|
-
setValue(this._rows.value[dataRow], field.path, updatedValue);
|
|
1359
|
-
}
|
|
1360
|
-
/**
|
|
1361
|
-
* Gets the domain-appropriate cell type for a given cell position.
|
|
1362
|
-
* This abstracts away the low-level grid plane concepts and provides domain-specific types.
|
|
1363
|
-
* @param cell - The cell position to classify
|
|
1364
|
-
* @returns The domain-appropriate cell type
|
|
1365
|
-
*/
|
|
1366
|
-
getCellType(cell) {
|
|
1367
|
-
switch (cell.plane) {
|
|
1368
|
-
case "frozenRowsEnd":
|
|
1369
|
-
return "draft";
|
|
1370
|
-
case "frozenRowsStart":
|
|
1371
|
-
return "header";
|
|
1372
|
-
case "grid":
|
|
1373
|
-
default:
|
|
1374
|
-
return "standard";
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Convenience method to check if a cell is a draft cell.
|
|
1379
|
-
* @param cell - The cell position to check
|
|
1380
|
-
* @returns true if the cell is a draft cell, false otherwise
|
|
1381
|
-
*/
|
|
1382
|
-
isDraftCell(cell) {
|
|
1383
|
-
return this.getCellType(cell) === "draft";
|
|
1384
|
-
}
|
|
1385
|
-
//
|
|
1386
|
-
// Column Operations
|
|
1387
|
-
//
|
|
1388
|
-
deleteColumn(fieldId) {
|
|
1389
|
-
if (!this._view) {
|
|
1390
|
-
return;
|
|
1391
|
-
}
|
|
1392
|
-
const field = this._view?.fields.find((field2) => field2.id === fieldId);
|
|
1393
|
-
if (field && this._onDeleteColumn) {
|
|
1394
|
-
this._onDeleteColumn(field.id);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
//
|
|
1398
|
-
// Resize
|
|
1399
|
-
//
|
|
1400
|
-
setColumnWidth(columnIndex, width) {
|
|
1401
|
-
const fields = this._view?.fields ?? [];
|
|
1402
|
-
if (columnIndex < fields.length) {
|
|
1403
|
-
const newWidth = Math.max(0, width);
|
|
1404
|
-
const field = fields[columnIndex];
|
|
1405
|
-
if (field) {
|
|
1406
|
-
field.size = newWidth;
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
//
|
|
1411
|
-
// Pinning
|
|
1412
|
-
//
|
|
1413
|
-
// TODO(burdon): Change to setPinned(on/off).
|
|
1414
|
-
pinRow(rowIndex, side) {
|
|
1415
|
-
this._pinnedRows[side].push(rowIndex);
|
|
1416
|
-
}
|
|
1417
|
-
unpinRow(rowIndex) {
|
|
1418
|
-
this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
|
|
1419
|
-
this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
|
|
1420
|
-
}
|
|
1421
|
-
//
|
|
1422
|
-
// View operations
|
|
1423
|
-
//
|
|
1424
|
-
saveView() {
|
|
1425
|
-
this._sorting.save();
|
|
1426
|
-
}
|
|
1427
|
-
};
|
|
1428
|
-
var editorTextToCellValue = (props, value) => {
|
|
1429
|
-
switch (props.format) {
|
|
1430
|
-
case FormatEnum3.Ref: {
|
|
1431
|
-
if (isLiveObject(value)) {
|
|
1432
|
-
return Ref3.make(value);
|
|
1433
|
-
} else {
|
|
1434
|
-
return value;
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
case FormatEnum3.SingleSelect: {
|
|
1438
|
-
const ids = extractTagIds(value);
|
|
1439
|
-
if (ids && ids.length > 0) {
|
|
1440
|
-
return ids[0];
|
|
1441
|
-
} else {
|
|
1442
|
-
return value;
|
|
35
|
+
"create stack section label": "Create table",
|
|
36
|
+
"add row label": "Add row",
|
|
37
|
+
"create comment": "Create thread",
|
|
38
|
+
"column action sort descending": "Sort Descending",
|
|
39
|
+
"column action sort ascending": "Sort Ascending",
|
|
40
|
+
"column action clear sorting": "Clear Sorting",
|
|
41
|
+
"column action settings": "Column Settings",
|
|
42
|
+
"column action delete": "Delete Column",
|
|
43
|
+
"delete row label": "Delete row",
|
|
44
|
+
"bulk delete row label": "Delete selected rows",
|
|
45
|
+
"column deleted label": "Column deleted",
|
|
46
|
+
"new column button label": "Create column",
|
|
47
|
+
"create new object label": "Create new object \u201C{{text}}\u201D",
|
|
48
|
+
"save view label": "Save changes to view"
|
|
1443
49
|
}
|
|
1444
50
|
}
|
|
1445
|
-
case FormatEnum3.MultiSelect: {
|
|
1446
|
-
const ids = extractTagIds(value);
|
|
1447
|
-
return ids || value;
|
|
1448
|
-
}
|
|
1449
|
-
default: {
|
|
1450
|
-
return parseValue({
|
|
1451
|
-
type: props.type,
|
|
1452
|
-
format: props.format,
|
|
1453
|
-
value
|
|
1454
|
-
});
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
};
|
|
1458
|
-
|
|
1459
|
-
// src/model/modal-controller.ts
|
|
1460
|
-
import { signal as signal4 } from "@preact/signals-core";
|
|
1461
|
-
import { log } from "@dxos/log";
|
|
1462
|
-
var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
|
|
1463
|
-
var ModalController = class {
|
|
1464
|
-
constructor() {
|
|
1465
|
-
this._state = signal4({
|
|
1466
|
-
type: "closed"
|
|
1467
|
-
});
|
|
1468
|
-
this._triggerRef = {
|
|
1469
|
-
current: null
|
|
1470
|
-
};
|
|
1471
|
-
this.setTrigger = (trigger) => {
|
|
1472
|
-
this._triggerRef.current = trigger;
|
|
1473
|
-
};
|
|
1474
|
-
this.showRowMenu = (rowIndex) => {
|
|
1475
|
-
this._state.value = {
|
|
1476
|
-
type: "row",
|
|
1477
|
-
rowIndex
|
|
1478
|
-
};
|
|
1479
|
-
};
|
|
1480
|
-
this.showColumnMenu = (fieldId) => {
|
|
1481
|
-
this._state.value = {
|
|
1482
|
-
type: "column",
|
|
1483
|
-
fieldId
|
|
1484
|
-
};
|
|
1485
|
-
};
|
|
1486
|
-
this.showColumnCreator = () => {
|
|
1487
|
-
this._state.value = {
|
|
1488
|
-
type: "columnSettings",
|
|
1489
|
-
mode: {
|
|
1490
|
-
type: "create"
|
|
1491
|
-
}
|
|
1492
|
-
};
|
|
1493
|
-
};
|
|
1494
|
-
this.showReferencePanel = (targetId, schemaId) => {
|
|
1495
|
-
this._state.value = {
|
|
1496
|
-
type: "refPanel",
|
|
1497
|
-
targetId,
|
|
1498
|
-
typename: schemaId
|
|
1499
|
-
};
|
|
1500
|
-
};
|
|
1501
|
-
this.openColumnSettings = () => {
|
|
1502
|
-
if (this._state.value.type === "column") {
|
|
1503
|
-
const fieldId = this._state.value.fieldId;
|
|
1504
|
-
requestAnimationFrame(() => {
|
|
1505
|
-
this._state.value = {
|
|
1506
|
-
type: "columnSettings",
|
|
1507
|
-
mode: {
|
|
1508
|
-
type: "edit",
|
|
1509
|
-
fieldId
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
});
|
|
1513
|
-
}
|
|
1514
|
-
};
|
|
1515
|
-
this.openCreateRef = (typename, anchorCell, initialValues, onCreate) => {
|
|
1516
|
-
if (anchorCell) {
|
|
1517
|
-
this._triggerRef.current = anchorCell;
|
|
1518
|
-
this._state.value = {
|
|
1519
|
-
type: "createRefPanel",
|
|
1520
|
-
typename,
|
|
1521
|
-
initialValues,
|
|
1522
|
-
onCreate
|
|
1523
|
-
};
|
|
1524
|
-
} else {
|
|
1525
|
-
log.warn("anchor cell not found while creating new ref", void 0, {
|
|
1526
|
-
F: __dxlog_file2,
|
|
1527
|
-
L: 86,
|
|
1528
|
-
S: this,
|
|
1529
|
-
C: (f, a) => f(...a)
|
|
1530
|
-
});
|
|
1531
|
-
}
|
|
1532
|
-
};
|
|
1533
|
-
this.close = () => {
|
|
1534
|
-
this._state.value = {
|
|
1535
|
-
type: "closed"
|
|
1536
|
-
};
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
get state() {
|
|
1540
|
-
return this._state;
|
|
1541
51
|
}
|
|
1542
|
-
|
|
1543
|
-
return this._triggerRef;
|
|
1544
|
-
}
|
|
1545
|
-
};
|
|
52
|
+
];
|
|
1546
53
|
|
|
1547
|
-
// src/
|
|
1548
|
-
import {
|
|
1549
|
-
import {
|
|
1550
|
-
import {
|
|
1551
|
-
import {
|
|
1552
|
-
import {
|
|
1553
|
-
import {
|
|
54
|
+
// src/components/Table/Table.tsx
|
|
55
|
+
import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
|
|
56
|
+
import React8, { forwardRef, useCallback as useCallback5, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo5, useState as useState4 } from "react";
|
|
57
|
+
import { Filter } from "@dxos/echo";
|
|
58
|
+
import { getValue } from "@dxos/echo-schema";
|
|
59
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
60
|
+
import { getSpace as getSpace2 } from "@dxos/react-client/echo";
|
|
61
|
+
import { useTranslation as useTranslation3 } from "@dxos/react-ui";
|
|
62
|
+
import { useAttention } from "@dxos/react-ui-attention";
|
|
63
|
+
import { Grid, closestCell, gridSeparatorBlockEnd, gridSeparatorInlineEnd } from "@dxos/react-ui-grid";
|
|
1554
64
|
import { mx } from "@dxos/react-ui-theme";
|
|
1555
|
-
import {
|
|
1556
|
-
var TablePresentation = class {
|
|
1557
|
-
constructor(model, _visibleRange = signal5({
|
|
1558
|
-
start: {
|
|
1559
|
-
row: 0,
|
|
1560
|
-
col: 0
|
|
1561
|
-
},
|
|
1562
|
-
end: {
|
|
1563
|
-
row: 0,
|
|
1564
|
-
col: 0
|
|
1565
|
-
}
|
|
1566
|
-
})) {
|
|
1567
|
-
this.model = model;
|
|
1568
|
-
this._visibleRange = _visibleRange;
|
|
1569
|
-
this.fieldProjectionCache = /* @__PURE__ */ new Map();
|
|
1570
|
-
}
|
|
1571
|
-
getCells(range, plane) {
|
|
1572
|
-
this.fieldProjectionCache.clear();
|
|
1573
|
-
let cells;
|
|
1574
|
-
switch (plane) {
|
|
1575
|
-
case "grid":
|
|
1576
|
-
this._visibleRange.value = range;
|
|
1577
|
-
cells = this.getMainGridCells(range);
|
|
1578
|
-
break;
|
|
1579
|
-
case "frozenRowsStart":
|
|
1580
|
-
cells = this.getHeaderCells(range);
|
|
1581
|
-
break;
|
|
1582
|
-
case "frozenColsStart":
|
|
1583
|
-
cells = this.getSelectionColumnCells(range);
|
|
1584
|
-
break;
|
|
1585
|
-
case "frozenColsEnd":
|
|
1586
|
-
cells = this.getActionColumnCells(range);
|
|
1587
|
-
break;
|
|
1588
|
-
case "fixedStartStart":
|
|
1589
|
-
cells = this.getSelectAllCell();
|
|
1590
|
-
break;
|
|
1591
|
-
case "fixedStartEnd":
|
|
1592
|
-
cells = this.getNewColumnCell();
|
|
1593
|
-
break;
|
|
1594
|
-
case "frozenRowsEnd":
|
|
1595
|
-
cells = this.getDraftRowCells(range);
|
|
1596
|
-
break;
|
|
1597
|
-
case "fixedEndEnd":
|
|
1598
|
-
cells = this.getDraftActionCells(range);
|
|
1599
|
-
break;
|
|
1600
|
-
default:
|
|
1601
|
-
cells = {};
|
|
1602
|
-
}
|
|
1603
|
-
if (plane === "grid" && this.model.features.dataEditable === false) {
|
|
1604
|
-
Object.values(cells).forEach((cell) => {
|
|
1605
|
-
cell.readonly = "text-select";
|
|
1606
|
-
});
|
|
1607
|
-
}
|
|
1608
|
-
return cells;
|
|
1609
|
-
}
|
|
1610
|
-
/**
|
|
1611
|
-
* Gets a field projection, using cache if available within the current render pass.
|
|
1612
|
-
*/
|
|
1613
|
-
getFieldProjection(fieldId) {
|
|
1614
|
-
let projection = this.fieldProjectionCache.get(fieldId);
|
|
1615
|
-
if (!projection) {
|
|
1616
|
-
projection = this.model.projection.getFieldProjection(fieldId);
|
|
1617
|
-
this.fieldProjectionCache.set(fieldId, projection);
|
|
1618
|
-
}
|
|
1619
|
-
return projection;
|
|
1620
|
-
}
|
|
1621
|
-
createDataCell(cells, obj, field, colIndex, displayIndex) {
|
|
1622
|
-
const { props } = this.getFieldProjection(field.id);
|
|
1623
|
-
const cell = {
|
|
1624
|
-
get value() {
|
|
1625
|
-
const value = getValue3(obj, field.path);
|
|
1626
|
-
if (value == null) {
|
|
1627
|
-
return "";
|
|
1628
|
-
}
|
|
1629
|
-
if (props.type === "array") {
|
|
1630
|
-
return "";
|
|
1631
|
-
}
|
|
1632
|
-
switch (props.format) {
|
|
1633
|
-
case FormatEnum4.Boolean:
|
|
1634
|
-
case FormatEnum4.SingleSelect:
|
|
1635
|
-
case FormatEnum4.MultiSelect: {
|
|
1636
|
-
return "";
|
|
1637
|
-
}
|
|
1638
|
-
case FormatEnum4.Ref: {
|
|
1639
|
-
if (!field.referencePath) {
|
|
1640
|
-
return "";
|
|
1641
|
-
}
|
|
1642
|
-
return getValue3(value.target, field.referencePath);
|
|
1643
|
-
}
|
|
1644
|
-
default: {
|
|
1645
|
-
return formatForDisplay2({
|
|
1646
|
-
type: props.type,
|
|
1647
|
-
format: props.format,
|
|
1648
|
-
value
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
};
|
|
1654
|
-
const classes = [];
|
|
1655
|
-
const formatClasses = cellClassesForFieldType({
|
|
1656
|
-
type: props.type,
|
|
1657
|
-
format: props.format
|
|
1658
|
-
});
|
|
1659
|
-
if (formatClasses) {
|
|
1660
|
-
classes.push(formatClasses);
|
|
1661
|
-
}
|
|
1662
|
-
const rowSelectionClasses = cellClassesForRowSelection(this.model.selection.isObjectSelected(obj), this.model.selection.selectionMode);
|
|
1663
|
-
if (rowSelectionClasses) {
|
|
1664
|
-
classes.push(rowSelectionClasses);
|
|
1665
|
-
}
|
|
1666
|
-
if (classes.length > 0) {
|
|
1667
|
-
cell.className = mx(classes.flat());
|
|
1668
|
-
}
|
|
1669
|
-
if (props.type === TypeEnum3.Array) {
|
|
1670
|
-
const targetArray = getValue3(obj, field.path);
|
|
1671
|
-
if (targetArray && Array.isArray(targetArray)) {
|
|
1672
|
-
const getLabel = (value) => {
|
|
1673
|
-
if (!value) {
|
|
1674
|
-
return void 0;
|
|
1675
|
-
}
|
|
1676
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1677
|
-
return String(value);
|
|
1678
|
-
}
|
|
1679
|
-
if (Array.isArray(value)) {
|
|
1680
|
-
return "Array";
|
|
1681
|
-
}
|
|
1682
|
-
if (typeof value === "object" && value.name) {
|
|
1683
|
-
return value.name;
|
|
1684
|
-
}
|
|
1685
|
-
if (typeof value === "object" && value.value) {
|
|
1686
|
-
return `${value.value}`;
|
|
1687
|
-
}
|
|
1688
|
-
return "Object";
|
|
1689
|
-
};
|
|
1690
|
-
const tags = targetArray.map(getLabel).filter(isNotNullable).map((title) => {
|
|
1691
|
-
return `<span class="dx-tag rounded-xl" data-hue="neutral">${title}</span>`;
|
|
1692
|
-
}).join("");
|
|
1693
|
-
cell.accessoryHtml = `<div role='none' class="flex flex-row gap-1 overflow-x-auto">${tags}</div>`;
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
if (props.format === FormatEnum4.Ref && props.referenceSchema) {
|
|
1697
|
-
const targetObj = getValue3(obj, field.path)?.target;
|
|
1698
|
-
if (targetObj) {
|
|
1699
|
-
const dxn = Obj2.getDXN(targetObj)?.toString();
|
|
1700
|
-
cell.accessoryHtml = `<dx-ref-tag refId=${dxn} class="dx-button is-6 pli-[3px] pbe-[2px] min-bs-0 absolute inline-end-1"><dx-icon icon="ph--link-simple--regular"/></dx-ref-tag>`;
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
if (props.format === FormatEnum4.Boolean) {
|
|
1704
|
-
const value = getValue3(obj, field.path);
|
|
1705
|
-
cell.accessoryHtml = tableControls.switch.render({
|
|
1706
|
-
colIndex,
|
|
1707
|
-
rowIndex: displayIndex,
|
|
1708
|
-
checked: value ?? false
|
|
1709
|
-
});
|
|
1710
|
-
cell.readonly = "no-text-select";
|
|
1711
|
-
}
|
|
1712
|
-
if (props.format === FormatEnum4.SingleSelect) {
|
|
1713
|
-
const value = getValue3(obj, field.path);
|
|
1714
|
-
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
1715
|
-
if (options) {
|
|
1716
|
-
const option = options.find((o) => o.id === value);
|
|
1717
|
-
if (option) {
|
|
1718
|
-
cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
if (props.format === FormatEnum4.MultiSelect) {
|
|
1723
|
-
const values = getValue3(obj, field.path);
|
|
1724
|
-
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
1725
|
-
if (options && values && values.length > 0) {
|
|
1726
|
-
const tags = values.map((value) => {
|
|
1727
|
-
const option = options.find((o) => o.id === value);
|
|
1728
|
-
if (option) {
|
|
1729
|
-
return `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
1730
|
-
}
|
|
1731
|
-
return null;
|
|
1732
|
-
}).filter(Boolean).join("");
|
|
1733
|
-
if (tags) {
|
|
1734
|
-
cell.accessoryHtml = tags;
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
const idx = toPlaneCellIndex({
|
|
1739
|
-
col: colIndex,
|
|
1740
|
-
row: displayIndex
|
|
1741
|
-
});
|
|
1742
|
-
cells[idx] = cell;
|
|
1743
|
-
}
|
|
1744
|
-
getMainGridCells(range) {
|
|
1745
|
-
const cells = {};
|
|
1746
|
-
const fields = this.model.view?.fields ?? [];
|
|
1747
|
-
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1748
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1749
|
-
const field = fields[col];
|
|
1750
|
-
if (!field) {
|
|
1751
|
-
continue;
|
|
1752
|
-
}
|
|
1753
|
-
this.createDataCell(cells, this.model.rows.value[row], field, col, row);
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
return cells;
|
|
1757
|
-
}
|
|
1758
|
-
getDraftRowCells(range) {
|
|
1759
|
-
const cells = {};
|
|
1760
|
-
const fields = this.model.view?.fields ?? [];
|
|
1761
|
-
const draftRows = this.model.draftRows.value;
|
|
1762
|
-
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
1763
|
-
const draftRow = draftRows[row];
|
|
1764
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1765
|
-
const field = fields[col];
|
|
1766
|
-
if (!field) {
|
|
1767
|
-
continue;
|
|
1768
|
-
}
|
|
1769
|
-
this.createDataCell(cells, draftRow.data, field, col, row);
|
|
1770
|
-
if (this.model.hasDraftRowValidationError(row, field.path)) {
|
|
1771
|
-
const cellIndex = toPlaneCellIndex({
|
|
1772
|
-
col,
|
|
1773
|
-
row
|
|
1774
|
-
});
|
|
1775
|
-
const cellValue = cells[cellIndex];
|
|
1776
|
-
if (cellValue) {
|
|
1777
|
-
const existingClasses = cellValue.className || "";
|
|
1778
|
-
const draftClasses = "dx-grid__cell--flagged";
|
|
1779
|
-
cellValue.className = existingClasses ? `${existingClasses} ${draftClasses}` : draftClasses;
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
return cells;
|
|
1785
|
-
}
|
|
1786
|
-
getHeaderCells(range) {
|
|
1787
|
-
const cells = {};
|
|
1788
|
-
const fields = this.model.view?.fields ?? [];
|
|
1789
|
-
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
1790
|
-
const { field, props } = this.getFieldProjection(fields[col].id);
|
|
1791
|
-
const sorting = this.model.sorting?.sorting;
|
|
1792
|
-
const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
|
|
1793
|
-
cells[toPlaneCellIndex({
|
|
1794
|
-
col,
|
|
1795
|
-
row: 0
|
|
1796
|
-
})] = {
|
|
1797
|
-
// TODO(burdon): Use same logic as form for fallback title.
|
|
1798
|
-
value: props.title ?? field.path,
|
|
1799
|
-
readonly: true,
|
|
1800
|
-
resizeHandle: "col",
|
|
1801
|
-
accessoryHtml: `
|
|
1802
|
-
${direction !== void 0 ? tableButtons.sort.render({
|
|
1803
|
-
fieldId: field.id,
|
|
1804
|
-
direction
|
|
1805
|
-
}) : ""}
|
|
1806
|
-
${tableButtons.columnSettings.render({
|
|
1807
|
-
fieldId: field.id
|
|
1808
|
-
})}
|
|
1809
|
-
`,
|
|
1810
|
-
className: "!bg-toolbarSurface"
|
|
1811
|
-
};
|
|
1812
|
-
}
|
|
1813
|
-
return cells;
|
|
1814
|
-
}
|
|
1815
|
-
getSelectionColumnCells(range) {
|
|
1816
|
-
const cells = {};
|
|
1817
|
-
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1818
|
-
const isSelected = this.model.selection.isRowIndexSelected(row);
|
|
1819
|
-
const classes = cellClassesForRowSelection(isSelected, this.model.selection.selectionMode);
|
|
1820
|
-
cells[toPlaneCellIndex({
|
|
1821
|
-
col: 0,
|
|
1822
|
-
row
|
|
1823
|
-
})] = {
|
|
1824
|
-
value: "",
|
|
1825
|
-
readonly: "no-text-select",
|
|
1826
|
-
className: classes ? mx(classes) : void 0,
|
|
1827
|
-
accessoryHtml: tableControls.checkbox.render({
|
|
1828
|
-
rowIndex: row,
|
|
1829
|
-
checked: isSelected
|
|
1830
|
-
})
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
return cells;
|
|
1834
|
-
}
|
|
1835
|
-
getActionColumnCells(range) {
|
|
1836
|
-
const cells = {};
|
|
1837
|
-
for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
|
|
1838
|
-
const isSelected = this.model.selection.isRowIndexSelected(row);
|
|
1839
|
-
const classes = cellClassesForRowSelection(isSelected, this.model.selection.selectionMode);
|
|
1840
|
-
cells[toPlaneCellIndex({
|
|
1841
|
-
col: 0,
|
|
1842
|
-
row
|
|
1843
|
-
})] = {
|
|
1844
|
-
value: "",
|
|
1845
|
-
readonly: true,
|
|
1846
|
-
className: classes ? mx(classes) : void 0,
|
|
1847
|
-
accessoryHtml: tableButtons.rowMenu.render({
|
|
1848
|
-
rowIndex: row
|
|
1849
|
-
})
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
return cells;
|
|
1853
|
-
}
|
|
1854
|
-
getSelectAllCell() {
|
|
1855
|
-
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === "single") {
|
|
1856
|
-
return {
|
|
1857
|
-
[toPlaneCellIndex({
|
|
1858
|
-
col: 0,
|
|
1859
|
-
row: 0
|
|
1860
|
-
})]: {
|
|
1861
|
-
className: "!bg-toolbarSurface",
|
|
1862
|
-
readonly: true,
|
|
1863
|
-
value: ""
|
|
1864
|
-
}
|
|
1865
|
-
};
|
|
1866
|
-
}
|
|
1867
|
-
return {
|
|
1868
|
-
[toPlaneCellIndex({
|
|
1869
|
-
col: 0,
|
|
1870
|
-
row: 0
|
|
1871
|
-
})]: {
|
|
1872
|
-
accessoryHtml: tableControls.checkbox.render({
|
|
1873
|
-
rowIndex: 0,
|
|
1874
|
-
header: true,
|
|
1875
|
-
checked: this.model.selection.allRowsSeleted.value
|
|
1876
|
-
}),
|
|
1877
|
-
className: "!bg-toolbarSurface",
|
|
1878
|
-
readonly: true,
|
|
1879
|
-
value: ""
|
|
1880
|
-
}
|
|
1881
|
-
};
|
|
1882
|
-
}
|
|
1883
|
-
getNewColumnCell() {
|
|
1884
|
-
return {
|
|
1885
|
-
[toPlaneCellIndex({
|
|
1886
|
-
col: 0,
|
|
1887
|
-
row: 0
|
|
1888
|
-
})]: {
|
|
1889
|
-
accessoryHtml: this.model.features.schemaEditable ? tableButtons.addColumn.render({
|
|
1890
|
-
disabled: (this.model.view?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT
|
|
1891
|
-
}) : void 0,
|
|
1892
|
-
className: "!bg-toolbarSurface",
|
|
1893
|
-
readonly: true,
|
|
1894
|
-
value: ""
|
|
1895
|
-
}
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1898
|
-
getDraftActionCells(range) {
|
|
1899
|
-
const cells = {};
|
|
1900
|
-
const draftRows = this.model.draftRows.value;
|
|
1901
|
-
for (let row = range.start.row; row <= range.end.row && row < draftRows.length; row++) {
|
|
1902
|
-
const draftRow = draftRows[row];
|
|
1903
|
-
const disabled = !draftRow.valid;
|
|
1904
|
-
cells[toPlaneCellIndex({
|
|
1905
|
-
col: 0,
|
|
1906
|
-
row
|
|
1907
|
-
})] = {
|
|
1908
|
-
value: "",
|
|
1909
|
-
readonly: true,
|
|
1910
|
-
accessoryHtml: tableButtons.saveDraftRow.render({
|
|
1911
|
-
rowIndex: row,
|
|
1912
|
-
disabled
|
|
1913
|
-
})
|
|
1914
|
-
};
|
|
1915
|
-
}
|
|
1916
|
-
return cells;
|
|
1917
|
-
}
|
|
1918
|
-
};
|
|
1919
|
-
var cellClassesForRowSelection = (selected, selectionMode) => {
|
|
1920
|
-
if (!selected) {
|
|
1921
|
-
if (selectionMode === "single") {
|
|
1922
|
-
return [
|
|
1923
|
-
"!cursor-pointer"
|
|
1924
|
-
];
|
|
1925
|
-
} else {
|
|
1926
|
-
return void 0;
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
switch (selectionMode) {
|
|
1930
|
-
case "single":
|
|
1931
|
-
return [
|
|
1932
|
-
"!bg-currentRelated hover:bg-hoverSurface",
|
|
1933
|
-
"!cursor-pointer"
|
|
1934
|
-
];
|
|
1935
|
-
case "multiple":
|
|
1936
|
-
return [
|
|
1937
|
-
"!bg-gridCellSelected"
|
|
1938
|
-
];
|
|
1939
|
-
}
|
|
1940
|
-
};
|
|
65
|
+
import { isNotFalsy, safeParseInt } from "@dxos/util";
|
|
1941
66
|
|
|
1942
67
|
// src/components/TableCellEditor/TableCellEditor.tsx
|
|
1943
|
-
import { useSignals as
|
|
1944
|
-
import
|
|
1945
|
-
import { FormatEnum
|
|
1946
|
-
import { invariant as
|
|
68
|
+
import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
|
|
69
|
+
import React3, { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
70
|
+
import { FormatEnum, TypeEnum } from "@dxos/echo-schema";
|
|
71
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1947
72
|
import { useThemeContext } from "@dxos/react-ui";
|
|
1948
73
|
import { createMarkdownExtensions } from "@dxos/react-ui-editor";
|
|
1949
|
-
import { cellQuery, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as
|
|
1950
|
-
import {
|
|
74
|
+
import { GridCellEditor, cellQuery, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
|
|
75
|
+
import { createLinks, tagPicker } from "@dxos/react-ui-tag-picker";
|
|
1951
76
|
|
|
1952
77
|
// src/components/TableCellEditor/CellValidationMessage.tsx
|
|
1953
|
-
import { useSignals as
|
|
1954
|
-
import
|
|
1955
|
-
import { useGridContext
|
|
78
|
+
import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
|
|
79
|
+
import React from "react";
|
|
80
|
+
import { useGridContext } from "@dxos/react-ui-grid";
|
|
1956
81
|
var CellValidationMessage = ({ validationError, variant = "error", __gridScope }) => {
|
|
1957
|
-
var _effect =
|
|
82
|
+
var _effect = _useSignals();
|
|
1958
83
|
try {
|
|
1959
|
-
const { editing, editBox: box } =
|
|
84
|
+
const { editing, editBox: box } = useGridContext("GridSheetCellEditor", __gridScope);
|
|
1960
85
|
if (!editing || !validationError) {
|
|
1961
86
|
return null;
|
|
1962
87
|
}
|
|
1963
88
|
const bgClass = variant === "error" ? "bg-errorSurface" : "bg-warningSurface";
|
|
1964
89
|
const textClass = variant === "error" ? "text-errorSurfaceText" : "text-warningSurfaceText";
|
|
1965
|
-
return /* @__PURE__ */
|
|
90
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1966
91
|
role: "none",
|
|
1967
92
|
className: `absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`,
|
|
1968
93
|
style: {
|
|
@@ -1980,107 +105,6 @@ var CellValidationMessage = ({ validationError, variant = "error", __gridScope }
|
|
|
1980
105
|
}
|
|
1981
106
|
};
|
|
1982
107
|
|
|
1983
|
-
// src/components/TableCellEditor/FormCellEditor.tsx
|
|
1984
|
-
import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
|
|
1985
|
-
import React6, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useRef } from "react";
|
|
1986
|
-
import { getSnapshot as getSnapshot2 } from "@dxos/echo-schema";
|
|
1987
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1988
|
-
import { Popover as Popover3 } from "@dxos/react-ui";
|
|
1989
|
-
import { Form as Form2 } from "@dxos/react-ui-form";
|
|
1990
|
-
import { parseCellIndex, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
|
|
1991
|
-
import { getDeep, setDeep } from "@dxos/util";
|
|
1992
|
-
var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
|
|
1993
|
-
var FormCellEditor = ({ fieldProjection, model, schema, __gridScope }) => {
|
|
1994
|
-
var _effect = _useSignals6();
|
|
1995
|
-
try {
|
|
1996
|
-
const { editing, setEditing } = useGridContext3("ArrayEditor", __gridScope);
|
|
1997
|
-
const cellRef = useRef(null);
|
|
1998
|
-
useEffect2(() => {
|
|
1999
|
-
if (editing && editing.cellElement) {
|
|
2000
|
-
cellRef.current = editing.cellElement;
|
|
2001
|
-
}
|
|
2002
|
-
}, [
|
|
2003
|
-
editing?.cellElement
|
|
2004
|
-
]);
|
|
2005
|
-
const narrowedSchema = useMemo3(() => {
|
|
2006
|
-
if (!schema) {
|
|
2007
|
-
return void 0;
|
|
2008
|
-
}
|
|
2009
|
-
return narrowSchema(schema, [
|
|
2010
|
-
fieldProjection.field.path
|
|
2011
|
-
]);
|
|
2012
|
-
}, [
|
|
2013
|
-
JSON.stringify(schema),
|
|
2014
|
-
fieldProjection.field.path
|
|
2015
|
-
]);
|
|
2016
|
-
const originalRow = useMemo3(() => {
|
|
2017
|
-
if (model && editing) {
|
|
2018
|
-
const cell = parseCellIndex(editing.index);
|
|
2019
|
-
const row = model.getRowAt(cell.row);
|
|
2020
|
-
invariant2(row, void 0, {
|
|
2021
|
-
F: __dxlog_file3,
|
|
2022
|
-
L: 47,
|
|
2023
|
-
S: void 0,
|
|
2024
|
-
A: [
|
|
2025
|
-
"row",
|
|
2026
|
-
""
|
|
2027
|
-
]
|
|
2028
|
-
});
|
|
2029
|
-
return row;
|
|
2030
|
-
}
|
|
2031
|
-
return void 0;
|
|
2032
|
-
}, [
|
|
2033
|
-
model,
|
|
2034
|
-
editing
|
|
2035
|
-
]);
|
|
2036
|
-
const formValues = useMemo3(() => {
|
|
2037
|
-
if (originalRow) {
|
|
2038
|
-
return getSnapshot2(originalRow);
|
|
2039
|
-
}
|
|
2040
|
-
}, [
|
|
2041
|
-
originalRow
|
|
2042
|
-
]);
|
|
2043
|
-
const handleSave = useCallback3((values) => {
|
|
2044
|
-
const path = fieldProjection.field.path;
|
|
2045
|
-
const value = getDeep(values, [
|
|
2046
|
-
path
|
|
2047
|
-
]);
|
|
2048
|
-
setDeep(originalRow, [
|
|
2049
|
-
path
|
|
2050
|
-
], value);
|
|
2051
|
-
setEditing(null);
|
|
2052
|
-
}, [
|
|
2053
|
-
fieldProjection.field.path,
|
|
2054
|
-
originalRow
|
|
2055
|
-
]);
|
|
2056
|
-
const handleOpenChange = useCallback3((nextOpen) => {
|
|
2057
|
-
if (nextOpen === false) {
|
|
2058
|
-
setEditing(null);
|
|
2059
|
-
}
|
|
2060
|
-
}, [
|
|
2061
|
-
setEditing
|
|
2062
|
-
]);
|
|
2063
|
-
if (!editing) {
|
|
2064
|
-
return null;
|
|
2065
|
-
}
|
|
2066
|
-
return /* @__PURE__ */ React6.createElement(Popover3.Root, {
|
|
2067
|
-
open: editing !== null,
|
|
2068
|
-
onOpenChange: handleOpenChange
|
|
2069
|
-
}, /* @__PURE__ */ React6.createElement(Popover3.VirtualTrigger, {
|
|
2070
|
-
virtualRef: cellRef
|
|
2071
|
-
}), /* @__PURE__ */ React6.createElement(Popover3.Content, {
|
|
2072
|
-
tabIndex: -1,
|
|
2073
|
-
classNames: "popover-card-width"
|
|
2074
|
-
}, /* @__PURE__ */ React6.createElement(Popover3.Arrow, null), /* @__PURE__ */ React6.createElement(Popover3.Viewport, null, /* @__PURE__ */ React6.createElement(Form2, {
|
|
2075
|
-
values: formValues,
|
|
2076
|
-
schema: narrowedSchema,
|
|
2077
|
-
onSave: handleSave
|
|
2078
|
-
}))));
|
|
2079
|
-
} finally {
|
|
2080
|
-
_effect.f();
|
|
2081
|
-
}
|
|
2082
|
-
};
|
|
2083
|
-
|
|
2084
108
|
// src/components/TableCellEditor/extension.ts
|
|
2085
109
|
import { autocompletion, pickedCompletion } from "@codemirror/autocomplete";
|
|
2086
110
|
import { EditorView } from "@codemirror/view";
|
|
@@ -2120,28 +144,136 @@ var completion = ({ debug, onQuery, onMatch }) => {
|
|
|
2120
144
|
];
|
|
2121
145
|
};
|
|
2122
146
|
|
|
147
|
+
// src/components/TableCellEditor/FormCellEditor.tsx
|
|
148
|
+
import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
|
|
149
|
+
import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
150
|
+
import { getSnapshot } from "@dxos/echo-schema";
|
|
151
|
+
import { invariant } from "@dxos/invariant";
|
|
152
|
+
import { Popover } from "@dxos/react-ui";
|
|
153
|
+
import { Form } from "@dxos/react-ui-form";
|
|
154
|
+
import { parseCellIndex, useGridContext as useGridContext2 } from "@dxos/react-ui-grid";
|
|
155
|
+
import { getDeep, setDeep } from "@dxos/util";
|
|
156
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
|
|
157
|
+
var FormCellEditor = ({ fieldProjection, model, schema, onSave, __gridScope }) => {
|
|
158
|
+
var _effect = _useSignals2();
|
|
159
|
+
try {
|
|
160
|
+
const { editing: contextEditing, setEditing } = useGridContext2("ArrayEditor", __gridScope);
|
|
161
|
+
const [editing, setLocalEditing] = useState(false);
|
|
162
|
+
const anchorRef = useRef(null);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (contextEditing && contextEditing.cellElement) {
|
|
165
|
+
anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
|
|
166
|
+
setLocalEditing(true);
|
|
167
|
+
} else {
|
|
168
|
+
anchorRef.current = null;
|
|
169
|
+
setLocalEditing(false);
|
|
170
|
+
}
|
|
171
|
+
}, [
|
|
172
|
+
contextEditing
|
|
173
|
+
]);
|
|
174
|
+
const narrowedSchema = useMemo(() => {
|
|
175
|
+
if (!schema) {
|
|
176
|
+
return void 0;
|
|
177
|
+
}
|
|
178
|
+
return narrowSchema(schema, [
|
|
179
|
+
fieldProjection.field.path
|
|
180
|
+
]);
|
|
181
|
+
}, [
|
|
182
|
+
JSON.stringify(schema),
|
|
183
|
+
fieldProjection.field.path
|
|
184
|
+
]);
|
|
185
|
+
const originalRow = useMemo(() => {
|
|
186
|
+
if (model && contextEditing) {
|
|
187
|
+
const cell = parseCellIndex(contextEditing.index);
|
|
188
|
+
const row = model.getRowAt(cell.row);
|
|
189
|
+
invariant(row, void 0, {
|
|
190
|
+
F: __dxlog_file,
|
|
191
|
+
L: 55,
|
|
192
|
+
S: void 0,
|
|
193
|
+
A: [
|
|
194
|
+
"row",
|
|
195
|
+
""
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
return row;
|
|
199
|
+
}
|
|
200
|
+
return void 0;
|
|
201
|
+
}, [
|
|
202
|
+
model,
|
|
203
|
+
contextEditing
|
|
204
|
+
]);
|
|
205
|
+
const formValues = useMemo(() => {
|
|
206
|
+
if (originalRow) {
|
|
207
|
+
return getSnapshot(originalRow);
|
|
208
|
+
}
|
|
209
|
+
}, [
|
|
210
|
+
originalRow
|
|
211
|
+
]);
|
|
212
|
+
const handleSave = useCallback((values) => {
|
|
213
|
+
const path = fieldProjection.field.path;
|
|
214
|
+
const value = getDeep(values, [
|
|
215
|
+
path
|
|
216
|
+
]);
|
|
217
|
+
setDeep(originalRow, [
|
|
218
|
+
path
|
|
219
|
+
], value);
|
|
220
|
+
setEditing(null);
|
|
221
|
+
setLocalEditing(false);
|
|
222
|
+
onSave?.();
|
|
223
|
+
}, [
|
|
224
|
+
fieldProjection.field.path,
|
|
225
|
+
originalRow
|
|
226
|
+
]);
|
|
227
|
+
const handleOpenChange = useCallback((nextOpen) => {
|
|
228
|
+
if (nextOpen === false) {
|
|
229
|
+
setEditing(null);
|
|
230
|
+
onSave?.();
|
|
231
|
+
}
|
|
232
|
+
setLocalEditing(nextOpen);
|
|
233
|
+
}, []);
|
|
234
|
+
if (!editing) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
return /* @__PURE__ */ React2.createElement(Popover.Root, {
|
|
238
|
+
open: editing,
|
|
239
|
+
onOpenChange: handleOpenChange
|
|
240
|
+
}, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
|
|
241
|
+
virtualRef: anchorRef
|
|
242
|
+
}), /* @__PURE__ */ React2.createElement(Popover.Content, {
|
|
243
|
+
tabIndex: -1,
|
|
244
|
+
classNames: "popover-card-width density-fine"
|
|
245
|
+
}, /* @__PURE__ */ React2.createElement(Popover.Arrow, null), /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(Form, {
|
|
246
|
+
values: formValues,
|
|
247
|
+
schema: narrowedSchema,
|
|
248
|
+
onSave: handleSave
|
|
249
|
+
}))));
|
|
250
|
+
} finally {
|
|
251
|
+
_effect.f();
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
2123
255
|
// src/components/TableCellEditor/TableCellEditor.tsx
|
|
2124
|
-
var
|
|
256
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
|
|
2125
257
|
var newValue = Symbol.for("newValue");
|
|
2126
258
|
var createOption = (text) => ({
|
|
2127
259
|
[newValue]: true,
|
|
2128
260
|
text
|
|
2129
261
|
});
|
|
2130
262
|
var isCreateOption = (data) => typeof data === "object" && data[newValue];
|
|
2131
|
-
var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onQuery, __gridScope }) => {
|
|
2132
|
-
var _effect =
|
|
263
|
+
var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onSave, onQuery, __gridScope }) => {
|
|
264
|
+
var _effect = _useSignals3();
|
|
2133
265
|
try {
|
|
2134
|
-
const { editing } =
|
|
2135
|
-
const fieldProjection =
|
|
266
|
+
const { editing } = useGridContext3("TableValueEditor", __gridScope);
|
|
267
|
+
const fieldProjection = useMemo2(() => {
|
|
2136
268
|
if (!model || !editing) {
|
|
2137
269
|
return;
|
|
2138
270
|
}
|
|
2139
271
|
const { col } = parseCellIndex2(editing.index);
|
|
2140
|
-
const field = model.projection.
|
|
272
|
+
const field = model.projection.fields[col];
|
|
2141
273
|
const fieldProjection2 = model.projection.getFieldProjection(field.id);
|
|
2142
|
-
|
|
2143
|
-
F:
|
|
2144
|
-
L:
|
|
274
|
+
invariant2(fieldProjection2, void 0, {
|
|
275
|
+
F: __dxlog_file2,
|
|
276
|
+
L: 76,
|
|
2145
277
|
S: void 0,
|
|
2146
278
|
A: [
|
|
2147
279
|
"fieldProjection",
|
|
@@ -2153,20 +285,22 @@ var TableValueEditor = ({ model, modals, schema, onEnter, onFocus, onQuery, __gr
|
|
|
2153
285
|
model,
|
|
2154
286
|
editing
|
|
2155
287
|
]);
|
|
2156
|
-
if (fieldProjection?.props.type ===
|
|
2157
|
-
return /* @__PURE__ */
|
|
288
|
+
if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === FormatEnum.SingleSelect) {
|
|
289
|
+
return /* @__PURE__ */ React3.createElement(FormCellEditor, {
|
|
2158
290
|
fieldProjection,
|
|
2159
291
|
model,
|
|
2160
292
|
schema,
|
|
2161
|
-
__gridScope
|
|
293
|
+
__gridScope,
|
|
294
|
+
onSave
|
|
2162
295
|
});
|
|
2163
296
|
}
|
|
2164
|
-
return /* @__PURE__ */
|
|
297
|
+
return /* @__PURE__ */ React3.createElement(TableCellEditor, {
|
|
2165
298
|
model,
|
|
2166
299
|
modals,
|
|
2167
300
|
onEnter,
|
|
2168
301
|
onFocus,
|
|
2169
302
|
onQuery,
|
|
303
|
+
onSave,
|
|
2170
304
|
__gridScope
|
|
2171
305
|
});
|
|
2172
306
|
} finally {
|
|
@@ -2178,24 +312,24 @@ var editorSlots = {
|
|
|
2178
312
|
className: "!plb-[--dx-grid-cell-editor-padding-block]"
|
|
2179
313
|
}
|
|
2180
314
|
};
|
|
2181
|
-
var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }) => {
|
|
2182
|
-
var _effect =
|
|
315
|
+
var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, onSave, __gridScope }) => {
|
|
316
|
+
var _effect = _useSignals3();
|
|
2183
317
|
try {
|
|
2184
|
-
const { id: gridId, editing, setEditing } =
|
|
318
|
+
const { id: gridId, editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
|
|
2185
319
|
const suppressNextBlur = useRef2(false);
|
|
2186
320
|
const { themeMode } = useThemeContext();
|
|
2187
|
-
const [
|
|
2188
|
-
const [
|
|
2189
|
-
const fieldProjection =
|
|
321
|
+
const [validationError, setValidationError] = useState2(null);
|
|
322
|
+
const [validationVariant, setValidationVariant] = useState2("error");
|
|
323
|
+
const fieldProjection = useMemo2(() => {
|
|
2190
324
|
if (!model || !editing) {
|
|
2191
325
|
return;
|
|
2192
326
|
}
|
|
2193
327
|
const { col } = parseCellIndex2(editing.index);
|
|
2194
|
-
const field = model.projection.
|
|
328
|
+
const field = model.projection.fields[col];
|
|
2195
329
|
const fieldProjection2 = model.projection.getFieldProjection(field.id);
|
|
2196
|
-
|
|
2197
|
-
F:
|
|
2198
|
-
L:
|
|
330
|
+
invariant2(fieldProjection2, void 0, {
|
|
331
|
+
F: __dxlog_file2,
|
|
332
|
+
L: 135,
|
|
2199
333
|
S: void 0,
|
|
2200
334
|
A: [
|
|
2201
335
|
"fieldProjection",
|
|
@@ -2207,7 +341,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2207
341
|
model,
|
|
2208
342
|
editing
|
|
2209
343
|
]);
|
|
2210
|
-
|
|
344
|
+
useEffect2(() => {
|
|
2211
345
|
if (!model || !editing || !fieldProjection) {
|
|
2212
346
|
setValidationError(null);
|
|
2213
347
|
return;
|
|
@@ -2215,15 +349,15 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2215
349
|
const cell = parseCellIndex2(editing.index);
|
|
2216
350
|
const { row, col } = cell;
|
|
2217
351
|
if (model.isDraftCell(cell)) {
|
|
2218
|
-
const field = model.projection.
|
|
352
|
+
const field = model.projection.fields[col];
|
|
2219
353
|
const hasValidationError = model.hasDraftRowValidationError(row, field.path);
|
|
2220
354
|
if (hasValidationError) {
|
|
2221
355
|
const draftRows = model.draftRows.value;
|
|
2222
356
|
if (row >= 0 && row < draftRows.length) {
|
|
2223
357
|
const draftRow = draftRows[row];
|
|
2224
|
-
const
|
|
2225
|
-
if (
|
|
2226
|
-
setValidationError(
|
|
358
|
+
const validationError2 = draftRow.validationErrors?.find((error) => error.path === field.path);
|
|
359
|
+
if (validationError2) {
|
|
360
|
+
setValidationError(validationError2.message);
|
|
2227
361
|
setValidationVariant("warning");
|
|
2228
362
|
}
|
|
2229
363
|
}
|
|
@@ -2238,7 +372,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2238
372
|
editing,
|
|
2239
373
|
fieldProjection
|
|
2240
374
|
]);
|
|
2241
|
-
const handleEnter =
|
|
375
|
+
const handleEnter = useCallback2(async (value) => {
|
|
2242
376
|
if (!model || !editing) {
|
|
2243
377
|
return;
|
|
2244
378
|
}
|
|
@@ -2250,6 +384,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2250
384
|
onEnter?.(cell);
|
|
2251
385
|
onFocus?.();
|
|
2252
386
|
setEditing(null);
|
|
387
|
+
onSave?.();
|
|
2253
388
|
} else {
|
|
2254
389
|
setValidationError(validationResult.error);
|
|
2255
390
|
setValidationVariant("error");
|
|
@@ -2261,7 +396,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2261
396
|
onFocus,
|
|
2262
397
|
setEditing
|
|
2263
398
|
]);
|
|
2264
|
-
const handleBlur =
|
|
399
|
+
const handleBlur = useCallback2(async (value) => {
|
|
2265
400
|
if (!model || !editing) {
|
|
2266
401
|
return;
|
|
2267
402
|
}
|
|
@@ -2272,7 +407,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2272
407
|
model,
|
|
2273
408
|
editing
|
|
2274
409
|
]);
|
|
2275
|
-
const handleClose =
|
|
410
|
+
const handleClose = useCallback2(async (value, event) => {
|
|
2276
411
|
if (!model || !editing || !fieldProjection) {
|
|
2277
412
|
return;
|
|
2278
413
|
}
|
|
@@ -2283,9 +418,10 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2283
418
|
setValidationError(null);
|
|
2284
419
|
model.setCellData(cell, value);
|
|
2285
420
|
setEditing(null);
|
|
421
|
+
onSave?.();
|
|
2286
422
|
onEnter?.(cell);
|
|
2287
423
|
if (event && onFocus) {
|
|
2288
|
-
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
424
|
+
onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
|
|
2289
425
|
}
|
|
2290
426
|
} else {
|
|
2291
427
|
setValidationError(result.error);
|
|
@@ -2294,6 +430,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2294
430
|
} else {
|
|
2295
431
|
setValidationError(null);
|
|
2296
432
|
setEditing(null);
|
|
433
|
+
onSave?.();
|
|
2297
434
|
if (event && onFocus) {
|
|
2298
435
|
onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
|
|
2299
436
|
}
|
|
@@ -2304,9 +441,10 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2304
441
|
onFocus,
|
|
2305
442
|
onEnter,
|
|
2306
443
|
fieldProjection,
|
|
2307
|
-
setEditing
|
|
444
|
+
setEditing,
|
|
445
|
+
onSave
|
|
2308
446
|
]);
|
|
2309
|
-
const extension =
|
|
447
|
+
const extension = useMemo2(() => {
|
|
2310
448
|
if (!fieldProjection) {
|
|
2311
449
|
return [];
|
|
2312
450
|
}
|
|
@@ -2319,12 +457,12 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2319
457
|
})
|
|
2320
458
|
];
|
|
2321
459
|
const format = fieldProjection.props.format;
|
|
2322
|
-
if (format ===
|
|
460
|
+
if (format === FormatEnum.SingleSelect || format === FormatEnum.MultiSelect) {
|
|
2323
461
|
extension2.push(createMarkdownExtensions({
|
|
2324
462
|
themeMode
|
|
2325
463
|
}));
|
|
2326
464
|
const options = fieldProjection.props.options || [];
|
|
2327
|
-
const mode = format ===
|
|
465
|
+
const mode = format === FormatEnum.SingleSelect ? "single-select" : "multi-select";
|
|
2328
466
|
extension2.push(tagPicker({
|
|
2329
467
|
mode,
|
|
2330
468
|
keymap: false,
|
|
@@ -2349,137 +487,373 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
|
|
|
2349
487
|
}
|
|
2350
488
|
}));
|
|
2351
489
|
}
|
|
2352
|
-
if (onQuery) {
|
|
2353
|
-
switch (fieldProjection.props.format) {
|
|
2354
|
-
case
|
|
2355
|
-
extension2.push([
|
|
2356
|
-
completion({
|
|
2357
|
-
onQuery: (text) => onQuery(fieldProjection, text),
|
|
2358
|
-
onMatch: (data) => {
|
|
2359
|
-
if (model && editing && modals) {
|
|
2360
|
-
if (isCreateOption(data)) {
|
|
2361
|
-
const { field, props } = fieldProjection;
|
|
2362
|
-
if (props.referenceSchema) {
|
|
2363
|
-
suppressNextBlur.current = true;
|
|
2364
|
-
modals.openCreateRef(props.referenceSchema, document.querySelector(cellQuery(editing.index, gridId)), {
|
|
2365
|
-
[field.referencePath]: data.text
|
|
2366
|
-
}, (data2) => {
|
|
2367
|
-
void handleEnter(data2);
|
|
2368
|
-
});
|
|
2369
|
-
}
|
|
2370
|
-
} else {
|
|
2371
|
-
void handleEnter(data);
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
})
|
|
2376
|
-
]);
|
|
2377
|
-
break;
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
490
|
+
if (onQuery) {
|
|
491
|
+
switch (fieldProjection.props.format) {
|
|
492
|
+
case FormatEnum.Ref: {
|
|
493
|
+
extension2.push([
|
|
494
|
+
completion({
|
|
495
|
+
onQuery: (text) => onQuery(fieldProjection, text),
|
|
496
|
+
onMatch: (data) => {
|
|
497
|
+
if (model && editing && modals) {
|
|
498
|
+
if (isCreateOption(data)) {
|
|
499
|
+
const { field, props } = fieldProjection;
|
|
500
|
+
if (props.referenceSchema) {
|
|
501
|
+
suppressNextBlur.current = true;
|
|
502
|
+
modals.openCreateRef(props.referenceSchema, document.querySelector(cellQuery(editing.index, gridId)), {
|
|
503
|
+
[field.referencePath]: data.text
|
|
504
|
+
}, (data2) => {
|
|
505
|
+
void handleEnter(data2);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
} else {
|
|
509
|
+
void handleEnter(data);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
]);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return extension2;
|
|
520
|
+
}, [
|
|
521
|
+
model,
|
|
522
|
+
modals,
|
|
523
|
+
editing,
|
|
524
|
+
fieldProjection,
|
|
525
|
+
handleClose,
|
|
526
|
+
themeMode
|
|
527
|
+
]);
|
|
528
|
+
const getCellContent = useCallback2(() => {
|
|
529
|
+
if (model && editing) {
|
|
530
|
+
const cell = parseCellIndex2(editing.index);
|
|
531
|
+
const { col } = cell;
|
|
532
|
+
const field = model.projection.fields[col];
|
|
533
|
+
const fieldProjection2 = model.projection.getFieldProjection(field.id);
|
|
534
|
+
if (fieldProjection2?.props.format === FormatEnum.SingleSelect || fieldProjection2?.props.format === FormatEnum.MultiSelect) {
|
|
535
|
+
const value2 = model.getCellData(cell);
|
|
536
|
+
if (value2 !== void 0) {
|
|
537
|
+
const options = fieldProjection2.props.options || [];
|
|
538
|
+
if (fieldProjection2.props.format === FormatEnum.MultiSelect) {
|
|
539
|
+
const tagItems = value2.split(",").map((id) => {
|
|
540
|
+
const option = options.find((o) => o.id === id);
|
|
541
|
+
if (option) {
|
|
542
|
+
return {
|
|
543
|
+
id,
|
|
544
|
+
label: option.title,
|
|
545
|
+
hue: option.color
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return void 0;
|
|
549
|
+
}).filter((item) => item !== void 0);
|
|
550
|
+
return createLinks(tagItems);
|
|
551
|
+
} else {
|
|
552
|
+
const option = options.find((o) => o.id === value2);
|
|
553
|
+
if (option) {
|
|
554
|
+
const tagItem = {
|
|
555
|
+
id: value2,
|
|
556
|
+
label: option.title,
|
|
557
|
+
hue: option.color
|
|
558
|
+
};
|
|
559
|
+
return createLinks([
|
|
560
|
+
tagItem
|
|
561
|
+
]);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return "";
|
|
566
|
+
}
|
|
567
|
+
const value = model.getCellData(cell);
|
|
568
|
+
return value !== void 0 ? String(value) : "";
|
|
569
|
+
}
|
|
570
|
+
}, [
|
|
571
|
+
model,
|
|
572
|
+
editing
|
|
573
|
+
]);
|
|
574
|
+
return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(CellValidationMessage, {
|
|
575
|
+
validationError,
|
|
576
|
+
variant: validationVariant,
|
|
577
|
+
__gridScope
|
|
578
|
+
}), /* @__PURE__ */ React3.createElement(GridCellEditor, {
|
|
579
|
+
extension,
|
|
580
|
+
getCellContent,
|
|
581
|
+
onBlur: handleBlur,
|
|
582
|
+
slots: editorSlots
|
|
583
|
+
}));
|
|
584
|
+
} finally {
|
|
585
|
+
_effect.f();
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
var determineNavigationAxis = ({ key }) => {
|
|
589
|
+
switch (key) {
|
|
590
|
+
case "ArrowUp":
|
|
591
|
+
case "ArrowDown":
|
|
592
|
+
case "Enter": {
|
|
593
|
+
return "row";
|
|
594
|
+
}
|
|
595
|
+
case "ArrowLeft":
|
|
596
|
+
case "ArrowRight":
|
|
597
|
+
case "Tab":
|
|
598
|
+
return "col";
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
var determineNavigationDelta = ({ key, shift }) => {
|
|
602
|
+
switch (key) {
|
|
603
|
+
case "ArrowUp":
|
|
604
|
+
case "ArrowLeft":
|
|
605
|
+
return -1;
|
|
606
|
+
case "ArrowDown":
|
|
607
|
+
case "ArrowRight":
|
|
608
|
+
return 1;
|
|
609
|
+
}
|
|
610
|
+
return shift ? -1 : 1;
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
// src/components/Table/ColumnActionsMenu.tsx
|
|
614
|
+
import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
|
|
615
|
+
import React4 from "react";
|
|
616
|
+
import { DropdownMenu, useTranslation } from "@dxos/react-ui";
|
|
617
|
+
var ColumnActionsMenu = ({ model, modals }) => {
|
|
618
|
+
var _effect = _useSignals4();
|
|
619
|
+
try {
|
|
620
|
+
const { t } = useTranslation(translationKey);
|
|
621
|
+
const state = modals.state.value;
|
|
622
|
+
if (state?.type !== "column") {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
const currentSort = model.sorting?.sorting;
|
|
626
|
+
const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
|
|
627
|
+
return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
|
|
628
|
+
modal: false,
|
|
629
|
+
open: true,
|
|
630
|
+
onOpenChange: modals.close
|
|
631
|
+
}, /* @__PURE__ */ React4.createElement(DropdownMenu.VirtualTrigger, {
|
|
632
|
+
virtualRef: modals.trigger
|
|
633
|
+
}), /* @__PURE__ */ React4.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React4.createElement(DropdownMenu.Content, null, /* @__PURE__ */ React4.createElement(DropdownMenu.Viewport, null, (!isCurrentColumnSorted || currentSort?.direction === "asc") && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
|
|
634
|
+
"data-testid": "column-sort-descending",
|
|
635
|
+
onClick: () => model.sorting?.setSort(state.fieldId, "desc")
|
|
636
|
+
}, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
|
|
637
|
+
"data-testid": "column-sort-ascending",
|
|
638
|
+
onClick: () => model.sorting?.setSort(state.fieldId, "asc")
|
|
639
|
+
}, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
|
|
640
|
+
"data-testid": "column-clear-sort",
|
|
641
|
+
onClick: () => model.sorting?.clearSort()
|
|
642
|
+
}, t("column action clear sorting")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
|
|
643
|
+
"data-testid": "column-delete",
|
|
644
|
+
onClick: () => model.deleteColumn(state.fieldId)
|
|
645
|
+
}, t("column action delete")), model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
|
|
646
|
+
"data-testid": "column-settings",
|
|
647
|
+
onClick: () => modals.openColumnSettings()
|
|
648
|
+
}, t("column action settings"))), /* @__PURE__ */ React4.createElement(DropdownMenu.Arrow, null))));
|
|
649
|
+
} finally {
|
|
650
|
+
_effect.f();
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
// src/components/Table/ColumnSettings.tsx
|
|
655
|
+
import { useSignals as _useSignals5 } from "@preact-signals/safe-react/tracking";
|
|
656
|
+
import React5, { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
657
|
+
import { Popover as Popover2 } from "@dxos/react-ui";
|
|
658
|
+
import { FieldEditor } from "@dxos/react-ui-form";
|
|
659
|
+
var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
|
|
660
|
+
var _effect = _useSignals5();
|
|
661
|
+
try {
|
|
662
|
+
const [newField, setNewField] = useState3();
|
|
663
|
+
const state = modals.state.value;
|
|
664
|
+
useEffect3(() => {
|
|
665
|
+
if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
|
|
666
|
+
setNewField(model.projection.createFieldProjection());
|
|
667
|
+
requestAnimationFrame(() => {
|
|
668
|
+
onNewColumn();
|
|
669
|
+
});
|
|
670
|
+
} else {
|
|
671
|
+
setNewField(void 0);
|
|
672
|
+
}
|
|
673
|
+
}, [
|
|
674
|
+
model?.projection,
|
|
675
|
+
state
|
|
676
|
+
]);
|
|
677
|
+
const existingField = useMemo3(() => {
|
|
678
|
+
if (state?.type === "columnSettings") {
|
|
679
|
+
const { mode } = state;
|
|
680
|
+
if (mode.type === "edit") {
|
|
681
|
+
return model?.projection?.fields.find((f) => f.id === mode.fieldId);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return void 0;
|
|
685
|
+
}, [
|
|
686
|
+
model?.projection?.fields,
|
|
687
|
+
state
|
|
688
|
+
]);
|
|
689
|
+
const field = existingField ?? newField;
|
|
690
|
+
const handleSave = useCallback3(() => {
|
|
691
|
+
modals.close();
|
|
692
|
+
}, [
|
|
693
|
+
modals
|
|
694
|
+
]);
|
|
695
|
+
const handleCancel = useCallback3(() => {
|
|
696
|
+
if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
|
|
697
|
+
model?.projection.deleteFieldProjection(newField.id);
|
|
698
|
+
}
|
|
699
|
+
}, [
|
|
700
|
+
model?.projection,
|
|
701
|
+
state,
|
|
702
|
+
newField
|
|
703
|
+
]);
|
|
704
|
+
if (!model?.projection || !field) {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
return /* @__PURE__ */ React5.createElement(Popover2.Root, {
|
|
708
|
+
modal: false,
|
|
709
|
+
open: state?.type === "columnSettings"
|
|
710
|
+
}, /* @__PURE__ */ React5.createElement(Popover2.VirtualTrigger, {
|
|
711
|
+
virtualRef: modals.trigger
|
|
712
|
+
}), /* @__PURE__ */ React5.createElement(Popover2.Portal, null, /* @__PURE__ */ React5.createElement(Popover2.Content, {
|
|
713
|
+
classNames: "md:is-64"
|
|
714
|
+
}, /* @__PURE__ */ React5.createElement(Popover2.Viewport, null, /* @__PURE__ */ React5.createElement(FieldEditor, {
|
|
715
|
+
projection: model.projection,
|
|
716
|
+
field,
|
|
717
|
+
registry,
|
|
718
|
+
onSave: handleSave,
|
|
719
|
+
onCancel: handleCancel
|
|
720
|
+
})), /* @__PURE__ */ React5.createElement(Popover2.Arrow, null))));
|
|
721
|
+
} finally {
|
|
722
|
+
_effect.f();
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
// src/components/Table/CreateRefPanel.tsx
|
|
727
|
+
import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
|
|
728
|
+
import { Schema } from "effect";
|
|
729
|
+
import React6, { useCallback as useCallback4, useMemo as useMemo4 } from "react";
|
|
730
|
+
import { live } from "@dxos/live-object";
|
|
731
|
+
import { getSpace } from "@dxos/react-client/echo";
|
|
732
|
+
import { Popover as Popover3 } from "@dxos/react-ui";
|
|
733
|
+
import { Form as Form2 } from "@dxos/react-ui-form";
|
|
734
|
+
import { useGridContext as useGridContext4 } from "@dxos/react-ui-grid";
|
|
735
|
+
var CreateRefPanel = ({ model, modals, __gridScope }) => {
|
|
736
|
+
var _effect = _useSignals6();
|
|
737
|
+
try {
|
|
738
|
+
const { id: gridId } = useGridContext4("TableCellEditor", __gridScope);
|
|
739
|
+
const space = model && getSpace(model?.view);
|
|
740
|
+
const state = modals.state.value;
|
|
741
|
+
const schema = useMemo4(() => {
|
|
742
|
+
if (!space || state?.type !== "createRefPanel") {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const [schema2] = space.db.schemaRegistry.query({
|
|
746
|
+
typename: state.typename
|
|
747
|
+
}).runSync();
|
|
748
|
+
if (schema2) {
|
|
749
|
+
const omit = Schema.omit("id");
|
|
750
|
+
return omit(schema2);
|
|
2380
751
|
}
|
|
2381
|
-
return extension2;
|
|
2382
752
|
}, [
|
|
2383
|
-
|
|
753
|
+
space,
|
|
754
|
+
state
|
|
755
|
+
]);
|
|
756
|
+
const handleSave = useCallback4((values) => {
|
|
757
|
+
if (!model || !space || state?.type !== "createRefPanel") {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const [schema2] = space.db.schemaRegistry.query({
|
|
761
|
+
typename: state.typename
|
|
762
|
+
}).runSync();
|
|
763
|
+
if (schema2) {
|
|
764
|
+
const obj = space.db.add(live(schema2, values));
|
|
765
|
+
state.onCreate?.(obj);
|
|
766
|
+
}
|
|
767
|
+
void modals.close();
|
|
768
|
+
}, [
|
|
2384
769
|
modals,
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
handleClose,
|
|
2388
|
-
themeMode
|
|
770
|
+
space,
|
|
771
|
+
state
|
|
2389
772
|
]);
|
|
2390
|
-
const
|
|
2391
|
-
if (model
|
|
2392
|
-
|
|
2393
|
-
const { col } = cell;
|
|
2394
|
-
const field = model.projection.view.fields[col];
|
|
2395
|
-
const fieldProjection2 = model.projection.getFieldProjection(field.id);
|
|
2396
|
-
if (fieldProjection2?.props.format === FormatEnum5.SingleSelect || fieldProjection2?.props.format === FormatEnum5.MultiSelect) {
|
|
2397
|
-
const value2 = model.getCellData(cell);
|
|
2398
|
-
if (value2 !== void 0) {
|
|
2399
|
-
const options = fieldProjection2.props.options || [];
|
|
2400
|
-
if (fieldProjection2.props.format === FormatEnum5.MultiSelect) {
|
|
2401
|
-
const tagItems = value2.split(",").map((id) => {
|
|
2402
|
-
const option = options.find((o) => o.id === id);
|
|
2403
|
-
if (option) {
|
|
2404
|
-
return {
|
|
2405
|
-
id,
|
|
2406
|
-
label: option.title,
|
|
2407
|
-
hue: option.color
|
|
2408
|
-
};
|
|
2409
|
-
}
|
|
2410
|
-
return void 0;
|
|
2411
|
-
}).filter((item) => item !== void 0);
|
|
2412
|
-
return createLinks(tagItems);
|
|
2413
|
-
} else {
|
|
2414
|
-
const option = options.find((o) => o.id === value2);
|
|
2415
|
-
if (option) {
|
|
2416
|
-
const tagItem = {
|
|
2417
|
-
id: value2,
|
|
2418
|
-
label: option.title,
|
|
2419
|
-
hue: option.color
|
|
2420
|
-
};
|
|
2421
|
-
return createLinks([
|
|
2422
|
-
tagItem
|
|
2423
|
-
]);
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
return "";
|
|
2428
|
-
}
|
|
2429
|
-
const value = model.getCellData(cell);
|
|
2430
|
-
return value !== void 0 ? String(value) : "";
|
|
773
|
+
const handleCancel = useCallback4(() => {
|
|
774
|
+
if (model) {
|
|
775
|
+
modals.close();
|
|
2431
776
|
}
|
|
2432
777
|
}, [
|
|
2433
|
-
|
|
2434
|
-
editing
|
|
778
|
+
modals
|
|
2435
779
|
]);
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
780
|
+
if (!model?.projection) {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
return /* @__PURE__ */ React6.createElement(Popover3.Root, {
|
|
784
|
+
modal: false,
|
|
785
|
+
open: state?.type === "createRefPanel" && !!schema,
|
|
786
|
+
onOpenChange: (nextOpen) => {
|
|
787
|
+
if (model && !nextOpen) {
|
|
788
|
+
return modals.close();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}, /* @__PURE__ */ React6.createElement(Popover3.VirtualTrigger, {
|
|
792
|
+
virtualRef: modals.trigger
|
|
793
|
+
}), /* @__PURE__ */ React6.createElement(Popover3.Portal, null, /* @__PURE__ */ React6.createElement(Popover3.Content, {
|
|
794
|
+
classNames: "md:is-64",
|
|
795
|
+
"data-grid": gridId
|
|
796
|
+
}, /* @__PURE__ */ React6.createElement(Popover3.Viewport, null, state?.type === "createRefPanel" && schema && /* @__PURE__ */ React6.createElement(Form2, {
|
|
797
|
+
schema,
|
|
798
|
+
values: state.initialValues ?? {},
|
|
799
|
+
onSave: handleSave,
|
|
800
|
+
onCancel: handleCancel
|
|
801
|
+
})), /* @__PURE__ */ React6.createElement(Popover3.Arrow, null))));
|
|
2446
802
|
} finally {
|
|
2447
803
|
_effect.f();
|
|
2448
804
|
}
|
|
2449
805
|
};
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
806
|
+
|
|
807
|
+
// src/components/Table/RowActionsMenu.tsx
|
|
808
|
+
import { useSignals as _useSignals7 } from "@preact-signals/safe-react/tracking";
|
|
809
|
+
import React7 from "react";
|
|
810
|
+
import { DropdownMenu as DropdownMenu2, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
|
|
811
|
+
var RowActionsMenu = ({ model, modals }) => {
|
|
812
|
+
var _effect = _useSignals7();
|
|
813
|
+
try {
|
|
814
|
+
const { t } = useTranslation2(translationKey);
|
|
815
|
+
const hasSelection = model.selection.hasSelection.value;
|
|
816
|
+
const state = modals.state.value;
|
|
817
|
+
if (state?.type !== "row") {
|
|
818
|
+
return null;
|
|
2456
819
|
}
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
820
|
+
return /* @__PURE__ */ React7.createElement(DropdownMenu2.Root, {
|
|
821
|
+
modal: false,
|
|
822
|
+
open: true,
|
|
823
|
+
onOpenChange: modals.close
|
|
824
|
+
}, /* @__PURE__ */ React7.createElement(DropdownMenu2.VirtualTrigger, {
|
|
825
|
+
virtualRef: modals.trigger
|
|
826
|
+
}), /* @__PURE__ */ React7.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React7.createElement(DropdownMenu2.Viewport, null, model.features.dataEditable !== false && /* @__PURE__ */ React7.createElement(DropdownMenu2.Item, {
|
|
827
|
+
"data-testid": "row-menu-delete",
|
|
828
|
+
onClick: () => model.deleteRow(state.rowIndex)
|
|
829
|
+
}, t(hasSelection ? "bulk delete row label" : "delete row label")), model.rowActions?.map((action) => /* @__PURE__ */ React7.createElement(DropdownMenu2.Item, {
|
|
830
|
+
key: action.id,
|
|
831
|
+
"data-testid": `row-action-${action.id}`,
|
|
832
|
+
onClick: () => {
|
|
833
|
+
modals.close();
|
|
834
|
+
model.handleRowAction(action.id, state.rowIndex);
|
|
835
|
+
}
|
|
836
|
+
}, toLocalizedString(action.label, t)))), /* @__PURE__ */ React7.createElement(DropdownMenu2.Arrow, null)));
|
|
837
|
+
} finally {
|
|
838
|
+
_effect.f();
|
|
2471
839
|
}
|
|
2472
|
-
return shift ? -1 : 1;
|
|
2473
840
|
};
|
|
2474
841
|
|
|
2475
842
|
// src/components/Table/Table.tsx
|
|
2476
|
-
var
|
|
2477
|
-
var TableRoot = ({ children, role }) => {
|
|
843
|
+
var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
|
|
844
|
+
var TableRoot = ({ children, role = "article" }) => {
|
|
2478
845
|
var _effect = _useSignals8();
|
|
2479
846
|
try {
|
|
2480
847
|
return /* @__PURE__ */ React8.createElement("div", {
|
|
2481
848
|
role: "none",
|
|
2482
|
-
className:
|
|
849
|
+
className: mx("relative !border-separator [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]", role === "card--popover" && "popover-card-height", role === "section" && "attention-surface", role === "card--intrinsic" && "[&_.dx-grid]:bs-[--dx-grid-content-block-size]", [
|
|
850
|
+
"card--popover",
|
|
851
|
+
"section",
|
|
852
|
+
"card--extrinsic"
|
|
853
|
+
].includes(role) && "overflow-hidden", [
|
|
854
|
+
"article",
|
|
855
|
+
"slide"
|
|
856
|
+
].includes(role) && "flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0")
|
|
2483
857
|
}, children);
|
|
2484
858
|
} finally {
|
|
2485
859
|
_effect.f();
|
|
@@ -2488,7 +862,7 @@ var TableRoot = ({ children, role }) => {
|
|
|
2488
862
|
var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttention, schema, client, onRowClick }, forwardedRef) => {
|
|
2489
863
|
var _effect = _useSignals8();
|
|
2490
864
|
try {
|
|
2491
|
-
const [dxGrid, setDxGrid] =
|
|
865
|
+
const [dxGrid, setDxGrid] = useState4(null);
|
|
2492
866
|
const { hasAttention } = useAttention(model?.id ?? "table");
|
|
2493
867
|
const { t } = useTranslation3(translationKey);
|
|
2494
868
|
const modals = useMemo5(() => new ModalController(), []);
|
|
@@ -2525,19 +899,42 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2525
899
|
return {
|
|
2526
900
|
update: (cell) => {
|
|
2527
901
|
if (cell) {
|
|
2528
|
-
dxGrid.updateIfWithinBounds(cell);
|
|
902
|
+
dxGrid.updateIfWithinBounds(cell, true);
|
|
2529
903
|
} else {
|
|
2530
904
|
dxGrid.updateCells(true);
|
|
2531
905
|
dxGrid.requestUpdate();
|
|
2532
906
|
}
|
|
907
|
+
},
|
|
908
|
+
focusDraft: () => {
|
|
909
|
+
requestAnimationFrame(() => {
|
|
910
|
+
dxGrid.setFocus({
|
|
911
|
+
plane: "frozenRowsEnd",
|
|
912
|
+
col: 0,
|
|
913
|
+
row: 0
|
|
914
|
+
});
|
|
915
|
+
dxGrid.refocus();
|
|
916
|
+
});
|
|
2533
917
|
}
|
|
2534
918
|
};
|
|
2535
919
|
}, [
|
|
2536
920
|
presentation,
|
|
2537
921
|
dxGrid
|
|
2538
922
|
]);
|
|
923
|
+
const handleSaveDraftRow = useCallback5((rowIndex = 0) => {
|
|
924
|
+
if (model && dxGrid) {
|
|
925
|
+
const didCommitSuccessfully = model.commitDraftRow(rowIndex);
|
|
926
|
+
if (didCommitSuccessfully) {
|
|
927
|
+
requestAnimationFrame(() => {
|
|
928
|
+
dxGrid.scrollToEndRow();
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}, [
|
|
933
|
+
model,
|
|
934
|
+
dxGrid
|
|
935
|
+
]);
|
|
2539
936
|
const handleGridClick = useCallback5((event) => {
|
|
2540
|
-
const rowIndex = safeParseInt(event.target.ariaRowIndex ?? "");
|
|
937
|
+
const rowIndex = safeParseInt(event.target.closest("[aria-rowindex]")?.ariaRowIndex ?? "");
|
|
2541
938
|
if (rowIndex != null) {
|
|
2542
939
|
if (onRowClick) {
|
|
2543
940
|
const row = model?.getRowAt(rowIndex);
|
|
@@ -2577,14 +974,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2577
974
|
break;
|
|
2578
975
|
}
|
|
2579
976
|
case "saveDraftRow": {
|
|
2580
|
-
|
|
2581
|
-
const didCommitSuccessfully = model.commitDraftRow(data.rowIndex);
|
|
2582
|
-
if (dxGrid && didCommitSuccessfully) {
|
|
2583
|
-
requestAnimationFrame(() => {
|
|
2584
|
-
dxGrid.scrollToEndRow();
|
|
2585
|
-
});
|
|
2586
|
-
}
|
|
2587
|
-
}
|
|
977
|
+
handleSaveDraftRow();
|
|
2588
978
|
break;
|
|
2589
979
|
}
|
|
2590
980
|
}
|
|
@@ -2623,29 +1013,32 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2623
1013
|
modals,
|
|
2624
1014
|
dxGrid
|
|
2625
1015
|
]);
|
|
2626
|
-
const handleFocus = useCallback5((increment, delta) => {
|
|
2627
|
-
if (dxGrid) {
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
1016
|
+
const handleFocus = useCallback5((increment, delta, cell) => {
|
|
1017
|
+
if (dxGrid && model) {
|
|
1018
|
+
if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1) {
|
|
1019
|
+
if (draftRowCount < 1) {
|
|
1020
|
+
model.insertRow();
|
|
1021
|
+
}
|
|
1022
|
+
dxGrid.setFocus({
|
|
1023
|
+
plane: "frozenRowsEnd",
|
|
1024
|
+
col: 0,
|
|
1025
|
+
row: 0
|
|
1026
|
+
});
|
|
1027
|
+
} else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
|
|
1028
|
+
handleSaveDraftRow(cell.row);
|
|
1029
|
+
model.insertRow();
|
|
1030
|
+
dxGrid.setFocus({
|
|
1031
|
+
plane: "frozenRowsEnd",
|
|
1032
|
+
col: 0,
|
|
1033
|
+
row: 0
|
|
2643
1034
|
});
|
|
1035
|
+
} else {
|
|
1036
|
+
dxGrid.refocus(increment, delta);
|
|
2644
1037
|
}
|
|
2645
1038
|
}
|
|
2646
1039
|
}, [
|
|
2647
|
-
|
|
2648
|
-
|
|
1040
|
+
dxGrid,
|
|
1041
|
+
model
|
|
2649
1042
|
]);
|
|
2650
1043
|
const handleKeyDown = useCallback5((event) => {
|
|
2651
1044
|
if (!model?.features.dataEditable) {
|
|
@@ -2658,7 +1051,11 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2658
1051
|
switch (event.key) {
|
|
2659
1052
|
case "Backspace":
|
|
2660
1053
|
case "Delete": {
|
|
2661
|
-
|
|
1054
|
+
try {
|
|
1055
|
+
model.setCellData(cell, void 0);
|
|
1056
|
+
event.preventDefault();
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
2662
1059
|
break;
|
|
2663
1060
|
}
|
|
2664
1061
|
}
|
|
@@ -2692,10 +1089,10 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2692
1089
|
]);
|
|
2693
1090
|
const handleQuery = useCallback5(async ({ field, props }, text) => {
|
|
2694
1091
|
if (model && props.referenceSchema && field.referencePath) {
|
|
2695
|
-
const space = model.
|
|
2696
|
-
|
|
2697
|
-
F:
|
|
2698
|
-
L:
|
|
1092
|
+
const space = getSpace2(model.view);
|
|
1093
|
+
invariant3(space, void 0, {
|
|
1094
|
+
F: __dxlog_file3,
|
|
1095
|
+
L: 336,
|
|
2699
1096
|
S: void 0,
|
|
2700
1097
|
A: [
|
|
2701
1098
|
"space",
|
|
@@ -2712,7 +1109,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2712
1109
|
if (schema2) {
|
|
2713
1110
|
const { objects } = await space.db.query(Filter.type(schema2)).run();
|
|
2714
1111
|
const options = objects.map((obj) => {
|
|
2715
|
-
const value =
|
|
1112
|
+
const value = getValue(obj, field.referencePath);
|
|
2716
1113
|
if (!value || typeof value !== "string") {
|
|
2717
1114
|
return void 0;
|
|
2718
1115
|
}
|
|
@@ -2738,6 +1135,12 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2738
1135
|
client,
|
|
2739
1136
|
t
|
|
2740
1137
|
]);
|
|
1138
|
+
const handleSave = useCallback5(() => {
|
|
1139
|
+
dxGrid?.updateCells(true);
|
|
1140
|
+
dxGrid?.requestUpdate();
|
|
1141
|
+
}, [
|
|
1142
|
+
dxGrid
|
|
1143
|
+
]);
|
|
2741
1144
|
if (!model || !modals) {
|
|
2742
1145
|
return /* @__PURE__ */ React8.createElement("span", {
|
|
2743
1146
|
role: "none",
|
|
@@ -2750,19 +1153,19 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
|
|
|
2750
1153
|
model,
|
|
2751
1154
|
modals,
|
|
2752
1155
|
schema,
|
|
2753
|
-
onEnter: handleEnter,
|
|
2754
1156
|
onFocus: handleFocus,
|
|
2755
|
-
onQuery: handleQuery
|
|
1157
|
+
onQuery: handleQuery,
|
|
1158
|
+
onSave: handleSave
|
|
2756
1159
|
}), /* @__PURE__ */ React8.createElement(Grid.Content, {
|
|
2757
|
-
className:
|
|
1160
|
+
className: mx("[--dx-grid-base:var(--baseSurface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
|
|
2758
1161
|
frozen,
|
|
2759
1162
|
columns: model.columnMeta.value,
|
|
2760
1163
|
limitRows: model.getRowCount() ?? 0,
|
|
2761
|
-
limitColumns: model.
|
|
1164
|
+
limitColumns: model.projection.fields.length,
|
|
2762
1165
|
overscroll: "trap",
|
|
2763
1166
|
onAxisResize: handleAxisResize,
|
|
2764
1167
|
onClick: handleGridClick,
|
|
2765
|
-
|
|
1168
|
+
onKeyDownCapture: handleKeyDown,
|
|
2766
1169
|
onWheelCapture: handleWheel,
|
|
2767
1170
|
ref: setDxGrid
|
|
2768
1171
|
}), /* @__PURE__ */ React8.createElement(RowActionsMenu, {
|
|
@@ -2789,141 +1192,38 @@ var Table = {
|
|
|
2789
1192
|
};
|
|
2790
1193
|
|
|
2791
1194
|
// src/components/Table/DynamicTable.tsx
|
|
2792
|
-
import { useSignals as _useSignals10 } from "@preact-signals/safe-react/tracking";
|
|
2793
|
-
import React9, { useRef as useRef3, useMemo as useMemo6, useCallback as useCallback7 } from "react";
|
|
2794
|
-
import { useDefaultValue } from "@dxos/react-ui";
|
|
2795
|
-
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
2796
|
-
|
|
2797
|
-
// src/hooks/useTableModel.ts
|
|
2798
1195
|
import { useSignals as _useSignals9 } from "@preact-signals/safe-react/tracking";
|
|
2799
|
-
import {
|
|
2800
|
-
import
|
|
2801
|
-
import {
|
|
2802
|
-
|
|
2803
|
-
import { useSelectionActions } from "@dxos/react-ui-attention";
|
|
2804
|
-
import { isNonNullable } from "@dxos/util";
|
|
2805
|
-
var useTableModel = ({ table, projection, rows, rowActions, features, onSelectionChanged, onRowAction, ...props }) => {
|
|
1196
|
+
import React9, { useCallback as useCallback6, useMemo as useMemo6, useRef as useRef3 } from "react";
|
|
1197
|
+
import { useDefaultValue } from "@dxos/react-ui";
|
|
1198
|
+
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
1199
|
+
var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, properties, jsonSchema: _jsonSchema, schema, rowActions, onRowClick, onRowAction, ...props }) => {
|
|
2806
1200
|
var _effect = _useSignals9();
|
|
2807
1201
|
try {
|
|
2808
|
-
const
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
const t = setTimeout(async () => {
|
|
2815
|
-
model2 = new TableModel({
|
|
2816
|
-
id: fullyQualifiedId(table),
|
|
2817
|
-
space: getSpace(table),
|
|
2818
|
-
view: table.view?.target,
|
|
2819
|
-
projection,
|
|
2820
|
-
features,
|
|
2821
|
-
rowActions,
|
|
2822
|
-
onRowAction,
|
|
2823
|
-
...props
|
|
2824
|
-
});
|
|
2825
|
-
await model2.open();
|
|
2826
|
-
setModel(model2);
|
|
2827
|
-
});
|
|
2828
|
-
return () => {
|
|
2829
|
-
clearTimeout(t);
|
|
2830
|
-
void model2?.close();
|
|
2831
|
-
};
|
|
2832
|
-
}, [
|
|
2833
|
-
table,
|
|
2834
|
-
projection,
|
|
2835
|
-
table?.view?.target,
|
|
2836
|
-
features,
|
|
2837
|
-
rowActions
|
|
2838
|
-
]);
|
|
2839
|
-
useEffect5(() => {
|
|
2840
|
-
if (rows) {
|
|
2841
|
-
const sortedRows = orderBy2(rows, [
|
|
2842
|
-
(row) => String(row.id)
|
|
2843
|
-
], [
|
|
2844
|
-
"asc"
|
|
2845
|
-
]);
|
|
2846
|
-
model?.setRows(sortedRows);
|
|
2847
|
-
}
|
|
2848
|
-
}, [
|
|
2849
|
-
model,
|
|
2850
|
-
rows
|
|
2851
|
-
]);
|
|
2852
|
-
const { multiSelect, clear } = useSelectionActions([
|
|
2853
|
-
table?.id,
|
|
2854
|
-
table?.view?.target?.query.typename
|
|
2855
|
-
].filter(isNonNullable));
|
|
2856
|
-
useEffect5(() => {
|
|
2857
|
-
if (!model) {
|
|
2858
|
-
return;
|
|
2859
|
-
}
|
|
2860
|
-
const unsubscribe = effect3(() => {
|
|
2861
|
-
const selectedItems = [
|
|
2862
|
-
...model.selection.selection.value
|
|
2863
|
-
];
|
|
2864
|
-
multiSelect(selectedItems);
|
|
2865
|
-
onSelectionChanged?.(selectedItems);
|
|
2866
|
-
});
|
|
2867
|
-
return () => {
|
|
2868
|
-
clear();
|
|
2869
|
-
unsubscribe();
|
|
2870
|
-
};
|
|
2871
|
-
}, [
|
|
2872
|
-
model,
|
|
2873
|
-
onSelectionChanged
|
|
2874
|
-
]);
|
|
2875
|
-
return model;
|
|
2876
|
-
} finally {
|
|
2877
|
-
_effect.f();
|
|
2878
|
-
}
|
|
2879
|
-
};
|
|
2880
|
-
|
|
2881
|
-
// src/hooks/useAddRow.ts
|
|
2882
|
-
import { useCallback as useCallback6 } from "react";
|
|
2883
|
-
import { Obj as Obj3 } from "@dxos/echo";
|
|
2884
|
-
var useAddRow = ({ space, schema }) => {
|
|
2885
|
-
return useCallback6((data) => {
|
|
2886
|
-
if (space && schema) {
|
|
2887
|
-
try {
|
|
2888
|
-
space.db.add(Obj3.make(schema, data ?? {}));
|
|
2889
|
-
return true;
|
|
2890
|
-
} catch (error) {
|
|
2891
|
-
return false;
|
|
2892
|
-
}
|
|
2893
|
-
}
|
|
2894
|
-
return false;
|
|
2895
|
-
}, [
|
|
2896
|
-
space,
|
|
2897
|
-
schema
|
|
2898
|
-
]);
|
|
2899
|
-
};
|
|
2900
|
-
|
|
2901
|
-
// src/components/Table/DynamicTable.tsx
|
|
2902
|
-
var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, properties, jsonSchema, schema, rowActions, onRowClick, onRowAction, ...props }) => {
|
|
2903
|
-
var _effect = _useSignals10();
|
|
2904
|
-
try {
|
|
2905
|
-
const { table, projection } = useMemo6(() => {
|
|
2906
|
-
const props2 = getBaseSchema({
|
|
2907
|
-
typename: name,
|
|
2908
|
-
properties,
|
|
2909
|
-
jsonSchema,
|
|
2910
|
-
schema
|
|
2911
|
-
});
|
|
2912
|
-
return makeDynamicTable({
|
|
2913
|
-
...props2,
|
|
2914
|
-
properties
|
|
2915
|
-
});
|
|
2916
|
-
}, [
|
|
1202
|
+
const { typename, jsonSchema } = useMemo6(() => getBaseSchema({
|
|
1203
|
+
typename: name,
|
|
1204
|
+
properties,
|
|
1205
|
+
jsonSchema: _jsonSchema,
|
|
1206
|
+
schema
|
|
1207
|
+
}), [
|
|
2917
1208
|
name,
|
|
2918
1209
|
properties,
|
|
2919
|
-
|
|
2920
|
-
|
|
1210
|
+
_jsonSchema,
|
|
1211
|
+
schema
|
|
1212
|
+
]);
|
|
1213
|
+
const { projection, view } = useMemo6(() => makeDynamicTable({
|
|
1214
|
+
typename,
|
|
1215
|
+
jsonSchema,
|
|
1216
|
+
properties
|
|
1217
|
+
}), [
|
|
1218
|
+
typename,
|
|
1219
|
+
jsonSchema,
|
|
1220
|
+
properties
|
|
2921
1221
|
]);
|
|
2922
1222
|
const tableRef = useRef3(null);
|
|
2923
|
-
const handleCellUpdate =
|
|
1223
|
+
const handleCellUpdate = useCallback6((cell) => {
|
|
2924
1224
|
tableRef.current?.update?.(cell);
|
|
2925
1225
|
}, []);
|
|
2926
|
-
const handleRowOrderChange =
|
|
1226
|
+
const handleRowOrderChange = useCallback6(() => {
|
|
2927
1227
|
tableRef.current?.update?.();
|
|
2928
1228
|
}, []);
|
|
2929
1229
|
const features = useDefaultValue(props.features, () => ({
|
|
@@ -2933,8 +1233,9 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
|
|
|
2933
1233
|
dataEditable: false
|
|
2934
1234
|
}));
|
|
2935
1235
|
const model = useTableModel({
|
|
2936
|
-
table,
|
|
2937
1236
|
rows,
|
|
1237
|
+
view,
|
|
1238
|
+
schema: jsonSchema,
|
|
2938
1239
|
projection,
|
|
2939
1240
|
features,
|
|
2940
1241
|
rowActions,
|
|
@@ -2951,7 +1252,7 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
|
|
|
2951
1252
|
]);
|
|
2952
1253
|
return /* @__PURE__ */ React9.createElement("div", {
|
|
2953
1254
|
role: "none",
|
|
2954
|
-
className:
|
|
1255
|
+
className: mx2("is-full bs-full grow grid", classNames)
|
|
2955
1256
|
}, /* @__PURE__ */ React9.createElement("div", {
|
|
2956
1257
|
role: "none",
|
|
2957
1258
|
className: "grid min-bs-0 overflow-hidden"
|
|
@@ -2968,11 +1269,11 @@ var DynamicTable = ({ classNames, name = "example.com/dynamic-table", rows, prop
|
|
|
2968
1269
|
};
|
|
2969
1270
|
|
|
2970
1271
|
// src/components/TableToolbar/TableToolbar.tsx
|
|
2971
|
-
import { useSignals as
|
|
1272
|
+
import { useSignals as _useSignals10 } from "@preact-signals/safe-react/tracking";
|
|
2972
1273
|
import { Rx } from "@effect-rx/rx-react";
|
|
2973
1274
|
import React10, { useMemo as useMemo7 } from "react";
|
|
2974
|
-
import { live as
|
|
2975
|
-
import {
|
|
1275
|
+
import { live as live2 } from "@dxos/live-object";
|
|
1276
|
+
import { MenuProvider, ToolbarMenu, createGapSeparator, createMenuAction, useMenuActions } from "@dxos/react-ui-menu";
|
|
2976
1277
|
import { rxFromSignal } from "@dxos/react-ui-menu";
|
|
2977
1278
|
var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.make((get) => {
|
|
2978
1279
|
const add = createMenuAction("add-row", onAdd, {
|
|
@@ -3020,9 +1321,9 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Rx.
|
|
|
3020
1321
|
};
|
|
3021
1322
|
});
|
|
3022
1323
|
var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, customActions }) => {
|
|
3023
|
-
var _effect =
|
|
1324
|
+
var _effect = _useSignals10();
|
|
3024
1325
|
try {
|
|
3025
|
-
const state = useMemo7(() =>
|
|
1326
|
+
const state = useMemo7(() => live2({
|
|
3026
1327
|
viewDirty
|
|
3027
1328
|
}), []);
|
|
3028
1329
|
const actionsCreator = useMemo7(() => createTableToolbarActions({
|
|
@@ -3057,12 +1358,10 @@ export {
|
|
|
3057
1358
|
Table,
|
|
3058
1359
|
TableModel,
|
|
3059
1360
|
TablePresentation,
|
|
3060
|
-
TableSchema,
|
|
3061
1361
|
TableToolbar,
|
|
3062
|
-
TableType,
|
|
3063
1362
|
cellClassesForRowSelection,
|
|
1363
|
+
createTable,
|
|
3064
1364
|
getBaseSchema,
|
|
3065
|
-
initializeTable,
|
|
3066
1365
|
makeDynamicTable,
|
|
3067
1366
|
narrowSchema,
|
|
3068
1367
|
tableButtons,
|