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