@dxos/react-ui-table 0.8.4-main.3c1ae3b → 0.8.4-main.52d7546f51

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.
Files changed (113) hide show
  1. package/dist/lib/browser/{chunk-V7A7ZA5E.mjs → chunk-GTGYBRJA.mjs} +606 -641
  2. package/dist/lib/browser/chunk-GTGYBRJA.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-CJQ6T4LL.mjs → chunk-OZBLY7XM.mjs} +4 -5
  4. package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +1082 -969
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +20 -16
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +1 -1
  11. package/dist/lib/node-esm/{chunk-P73ALX3N.mjs → chunk-DPNSZHU5.mjs} +606 -640
  12. package/dist/lib/node-esm/chunk-DPNSZHU5.mjs.map +7 -0
  13. package/dist/lib/node-esm/{chunk-T6PXZQ6W.mjs → chunk-WNZSKIRF.mjs} +4 -5
  14. package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +1082 -969
  16. package/dist/lib/node-esm/index.mjs.map +4 -4
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +20 -16
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +1 -1
  21. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/DynamicTable.d.ts +3 -2
  24. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +1 -1
  26. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/Relations.stories.d.ts +2 -2
  28. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/Table.stories.d.ts +2 -2
  32. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  33. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +1 -2
  34. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -1
  36. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  37. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  38. package/dist/types/src/components/testing.d.ts +22 -13
  39. package/dist/types/src/components/testing.d.ts.map +1 -1
  40. package/dist/types/src/hooks/useAddRow.d.ts +5 -6
  41. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  42. package/dist/types/src/hooks/useProjectionModel.d.ts +2 -1
  43. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  44. package/dist/types/src/hooks/useTableModel.d.ts +6 -5
  45. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  46. package/dist/types/src/model/modal-controller.d.ts +5 -1
  47. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  48. package/dist/types/src/model/selection-model.d.ts +17 -8
  49. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  50. package/dist/types/src/model/table-model.d.ts +99 -18
  51. package/dist/types/src/model/table-model.d.ts.map +1 -1
  52. package/dist/types/src/model/table-presentation.d.ts +3 -1
  53. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  54. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  55. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  56. package/dist/types/src/testing/useTestTableModel.d.ts +4 -5
  57. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  58. package/dist/types/src/translations.d.ts +2 -2
  59. package/dist/types/src/translations.d.ts.map +1 -1
  60. package/dist/types/src/types/Table.d.ts +17 -14
  61. package/dist/types/src/types/Table.d.ts.map +1 -1
  62. package/dist/types/src/util/dynamic-table.d.ts +10 -7
  63. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  64. package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
  65. package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
  66. package/dist/types/src/util/index.d.ts +2 -0
  67. package/dist/types/src/util/index.d.ts.map +1 -1
  68. package/dist/types/src/util/query-ast.d.ts +7 -0
  69. package/dist/types/src/util/query-ast.d.ts.map +1 -0
  70. package/dist/types/src/util/sort.d.ts +12 -0
  71. package/dist/types/src/util/sort.d.ts.map +1 -0
  72. package/dist/types/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +53 -54
  74. package/src/components/Table/ColumnActionsMenu.tsx +6 -5
  75. package/src/components/Table/ColumnSettings.tsx +4 -3
  76. package/src/components/Table/DynamicTable.stories.tsx +10 -8
  77. package/src/components/Table/DynamicTable.tsx +18 -18
  78. package/src/components/Table/Relations.stories.tsx +23 -19
  79. package/src/components/Table/RowActionsMenu.tsx +3 -2
  80. package/src/components/Table/Table.stories.tsx +47 -31
  81. package/src/components/Table/Table.tsx +34 -17
  82. package/src/components/TableCellEditor/FormCellEditor.tsx +49 -65
  83. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +3 -3
  84. package/src/components/TableCellEditor/TableCellEditor.tsx +2 -2
  85. package/src/components/TableToolbar/TableToolbar.tsx +18 -12
  86. package/src/components/testing.ts +37 -11
  87. package/src/hooks/useAddRow.ts +8 -10
  88. package/src/hooks/useProjectionModel.ts +28 -8
  89. package/src/hooks/useTableModel.ts +27 -23
  90. package/src/model/modal-controller.ts +23 -12
  91. package/src/model/selection-model.ts +75 -44
  92. package/src/model/table-model.test.ts +80 -52
  93. package/src/model/table-model.ts +409 -138
  94. package/src/model/table-presentation.test.ts +57 -47
  95. package/src/model/table-presentation.ts +23 -18
  96. package/src/playwright/TableManager.ts +10 -3
  97. package/src/playwright/playwright.config.ts +1 -1
  98. package/src/playwright/smoke.spec.ts +48 -4
  99. package/src/testing/useTestTableModel.ts +23 -20
  100. package/src/translations.ts +1 -1
  101. package/src/types/Table.ts +8 -8
  102. package/src/util/dynamic-table.test.ts +70 -0
  103. package/src/util/dynamic-table.ts +31 -13
  104. package/src/util/index.ts +2 -0
  105. package/src/util/query-ast.ts +19 -0
  106. package/src/util/sort.ts +90 -0
  107. package/dist/lib/browser/chunk-CJQ6T4LL.mjs.map +0 -7
  108. package/dist/lib/browser/chunk-V7A7ZA5E.mjs.map +0 -7
  109. package/dist/lib/node-esm/chunk-P73ALX3N.mjs.map +0 -7
  110. package/dist/lib/node-esm/chunk-T6PXZQ6W.mjs.map +0 -7
  111. package/dist/types/src/model/table-sorting.d.ts +0 -56
  112. package/dist/types/src/model/table-sorting.d.ts.map +0 -1
  113. package/src/model/table-sorting.ts +0 -234
@@ -3,25 +3,27 @@ import {
3
3
  BUTTON_IDENTIFIERS,
4
4
  CONTROL_IDENTIFIERS,
5
5
  CheckboxStory,
6
- ModalController,
7
6
  SwitchStory,
8
7
  TableModel,
9
8
  TablePresentation,
10
9
  cellClassesForRowSelection,
11
- getBaseSchema,
12
- makeDynamicTable,
13
- narrowSchema,
10
+ compareValues,
11
+ createDirectChangeCallback,
12
+ createEchoChangeCallback,
13
+ extractOrder,
14
+ getSortValue,
14
15
  tableButtons,
15
16
  tableControls,
16
- touch,
17
17
  useAddRow,
18
18
  useProjectionModel,
19
19
  useTableModel
20
- } from "./chunk-P73ALX3N.mjs";
21
- import "./chunk-T6PXZQ6W.mjs";
20
+ } from "./chunk-DPNSZHU5.mjs";
21
+ import {
22
+ Table_exports
23
+ } from "./chunk-WNZSKIRF.mjs";
22
24
 
23
25
  // src/translations.ts
24
- var translationKey = "react-ui-table";
26
+ var translationKey = "@dxos/react-ui-table";
25
27
  var translations = [
26
28
  {
27
29
  "en-US": {
@@ -53,64 +55,229 @@ var translations = [
53
55
  ];
54
56
 
55
57
  // src/components/Table/Table.tsx
56
- import { useSignals as _useSignals7 } from "@preact-signals/safe-react/tracking";
58
+ import { Atom as Atom2, RegistryContext, useAtomValue as useAtomValue4 } from "@effect-atom/atom-react";
57
59
  import * as String2 from "effect/String";
58
- import React7, { forwardRef, useCallback as useCallback4, useEffect as useEffect3, useImperativeHandle, useMemo as useMemo4, useState as useState4 } from "react";
60
+ import React7, { forwardRef, useCallback as useCallback4, useContext, useEffect as useEffect3, useImperativeHandle, useMemo as useMemo4, useState as useState4 } from "react";
61
+ import { log } from "@dxos/log";
59
62
  import { useAttention } from "@dxos/react-ui-attention";
60
63
  import { Grid, closestCell, gridSeparatorBlockEnd, gridSeparatorInlineEnd } from "@dxos/react-ui-grid";
61
64
  import { DxEditRequest } from "@dxos/react-ui-grid";
62
- import { mx } from "@dxos/react-ui-theme";
65
+ import { mx } from "@dxos/ui-theme";
66
+
67
+ // src/util/dynamic-table.ts
68
+ import { Filter, Obj, Order, Query, Ref, Type } from "@dxos/echo";
69
+ import { ProjectionModel, View, createEchoChangeCallback as createEchoChangeCallback2, getSchemaFromPropertyDefinitions } from "@dxos/schema";
70
+ var getBaseSchema = ({ schema, typename, properties, jsonSchema }) => {
71
+ if (typename && properties) {
72
+ const schema2 = getSchemaFromPropertyDefinitions(typename, properties);
73
+ return {
74
+ typename: schema2.typename,
75
+ jsonSchema: Type.toJsonSchema(schema2)
76
+ };
77
+ } else if (schema) {
78
+ return {
79
+ typename: Type.getTypename(schema),
80
+ jsonSchema: Type.toJsonSchema(schema)
81
+ };
82
+ } else if (typename && jsonSchema) {
83
+ return {
84
+ typename,
85
+ jsonSchema
86
+ };
87
+ } else {
88
+ throw new Error("invalid properties");
89
+ }
90
+ };
91
+ var makeDynamicTable = ({ registry, jsonSchema, properties }) => {
92
+ const view = View.make({
93
+ query: Query.select(Filter.everything()),
94
+ jsonSchema,
95
+ ...properties && {
96
+ fields: properties.map((property) => property.name)
97
+ }
98
+ });
99
+ const object = Obj.make(Table_exports.Table, {
100
+ view: Ref.make(view),
101
+ sizes: {}
102
+ });
103
+ const projection = new ProjectionModel({
104
+ registry,
105
+ view,
106
+ baseSchema: jsonSchema,
107
+ change: createEchoChangeCallback2(view, jsonSchema)
108
+ });
109
+ projection.normalizeView();
110
+ if (properties && projection.getFields()) {
111
+ setProperties(view, projection, object, properties);
112
+ }
113
+ return {
114
+ projection,
115
+ object
116
+ };
117
+ };
118
+ var setProperties = (view, projection, table, properties) => {
119
+ for (const property of properties) {
120
+ const field = projection.getFields().find((field2) => field2.path === property.name);
121
+ if (field) {
122
+ if (property.size !== void 0) {
123
+ Obj.change(table, (t) => {
124
+ t.sizes[field.path] = property.size;
125
+ });
126
+ }
127
+ if (property.title !== void 0) {
128
+ const fieldProjection = projection.getFieldProjection(field.id);
129
+ projection.setFieldProjection({
130
+ ...fieldProjection,
131
+ props: {
132
+ ...fieldProjection.props,
133
+ title: property.title
134
+ }
135
+ });
136
+ }
137
+ if (property.sort) {
138
+ const currentQuery = Query.fromAst(Obj.getSnapshot(view).query.ast);
139
+ const newQuery = currentQuery.orderBy(Order.property(field.path, property.sort));
140
+ Obj.change(view, (v) => {
141
+ v.query.ast = newQuery.ast;
142
+ });
143
+ }
144
+ }
145
+ }
146
+ };
147
+
148
+ // src/util/signals.ts
149
+ var touch = (value) => value;
150
+
151
+ // src/util/schema.ts
152
+ import * as Schema from "effect/Schema";
153
+ import * as SchemaAST from "effect/SchemaAST";
154
+ var narrowSchema = (schema, paths) => {
155
+ const ast = schema?.ast;
156
+ if (SchemaAST.isTypeLiteral(ast)) {
157
+ const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
158
+ if (propertySignatures.length > 0) {
159
+ const narrowType = new SchemaAST.TypeLiteral(propertySignatures, []);
160
+ return Schema.make(narrowType);
161
+ }
162
+ }
163
+ return void 0;
164
+ };
165
+
166
+ // src/model/modal-controller.ts
167
+ import { Atom } from "@effect-atom/atom-react";
168
+ var ModalController = class {
169
+ _registry;
170
+ _state;
171
+ _triggerRef = {
172
+ current: null
173
+ };
174
+ constructor(registry) {
175
+ this._registry = registry;
176
+ this._state = Atom.make({
177
+ type: "closed"
178
+ });
179
+ }
180
+ get state() {
181
+ return this._state;
182
+ }
183
+ get stateValue() {
184
+ return this._registry.get(this._state);
185
+ }
186
+ get trigger() {
187
+ return this._triggerRef;
188
+ }
189
+ setTrigger = (trigger) => {
190
+ this._triggerRef.current = trigger;
191
+ };
192
+ showRowMenu = (rowIndex) => {
193
+ this._registry.set(this._state, {
194
+ type: "row",
195
+ rowIndex
196
+ });
197
+ };
198
+ showColumnMenu = (fieldId) => {
199
+ this._registry.set(this._state, {
200
+ type: "column",
201
+ fieldId
202
+ });
203
+ };
204
+ showColumnCreator = () => {
205
+ this._registry.set(this._state, {
206
+ type: "columnSettings",
207
+ mode: {
208
+ type: "create"
209
+ }
210
+ });
211
+ };
212
+ showReferencePanel = (targetId, schemaId) => {
213
+ this._registry.set(this._state, {
214
+ type: "refPanel",
215
+ targetId,
216
+ typename: schemaId
217
+ });
218
+ };
219
+ openColumnSettings = () => {
220
+ const currentState = this._registry.get(this._state);
221
+ if (currentState.type === "column") {
222
+ const fieldId = currentState.fieldId;
223
+ requestAnimationFrame(() => {
224
+ this._registry.set(this._state, {
225
+ type: "columnSettings",
226
+ mode: {
227
+ type: "edit",
228
+ fieldId
229
+ }
230
+ });
231
+ });
232
+ }
233
+ };
234
+ close = () => {
235
+ this._registry.set(this._state, {
236
+ type: "closed"
237
+ });
238
+ };
239
+ };
63
240
 
64
241
  // src/components/TableCellEditor/TableCellEditor.tsx
65
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
66
242
  import { EditorView } from "@codemirror/view";
67
243
  import React3, { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
68
244
  import { debounce } from "@dxos/async";
69
- import { Format as Format2, TypeEnum } from "@dxos/echo/internal";
245
+ import { Format, TypeEnum } from "@dxos/echo/internal";
70
246
  import { invariant as invariant2 } from "@dxos/invariant";
71
247
  import { GridCellEditor, editorKeys, parseCellIndex as parseCellIndex2, useGridContext as useGridContext3 } from "@dxos/react-ui-grid";
72
248
 
73
249
  // src/components/TableCellEditor/CellValidationMessage.tsx
74
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
75
250
  import React from "react";
76
251
  import { useGridContext } from "@dxos/react-ui-grid";
77
252
  var CellValidationMessage = ({ __gridScope, validationError, variant = "error" }) => {
78
- var _effect = _useSignals();
79
- try {
80
- const { editing, editBox: box } = useGridContext("GridSheetCellEditor", __gridScope);
81
- if (!editing || !validationError) {
82
- return null;
83
- }
84
- const bgClass = variant === "error" ? "bg-errorSurface" : "bg-warningSurface";
85
- const textClass = variant === "error" ? "text-errorSurfaceText" : "text-warningSurfaceText";
86
- return /* @__PURE__ */ React.createElement("div", {
87
- role: "none",
88
- className: `absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`,
89
- style: {
90
- ...{
91
- "--dx-gridCellWidth": `${box?.inlineSize ?? 200}px`
92
- },
93
- zIndex: 10,
94
- insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
95
- insetInlineStart: box.insetInlineStart,
96
- inlineSize: box.inlineSize
97
- }
98
- }, validationError);
99
- } finally {
100
- _effect.f();
253
+ const { editing, editBox: box } = useGridContext("GridSheetCellEditor", __gridScope);
254
+ if (!editing || !validationError) {
255
+ return null;
101
256
  }
257
+ const bgClass = variant === "error" ? "bg-errorSurface" : "bg-warningSurface";
258
+ const textClass = variant === "error" ? "text-errorSurfaceText" : "text-warningSurfaceText";
259
+ return /* @__PURE__ */ React.createElement("div", {
260
+ role: "none",
261
+ className: `absolute ${bgClass} ${textClass} rounded-bs-sm text-xs p-1`,
262
+ style: {
263
+ ...{
264
+ "--dx-gridCellWidth": `${box?.inlineSize ?? 200}px`
265
+ },
266
+ zIndex: 10,
267
+ insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
268
+ insetInlineStart: box.insetInlineStart,
269
+ inlineSize: box.inlineSize
270
+ }
271
+ }, validationError);
102
272
  };
103
273
 
104
274
  // src/components/TableCellEditor/FormCellEditor.tsx
105
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
106
275
  import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
107
- import { Filter, Obj } from "@dxos/echo";
108
- import { Format, Ref, getValue } from "@dxos/echo/internal";
276
+ import { Entity } from "@dxos/echo";
277
+ import { Ref as Ref2, getValue } from "@dxos/echo/internal";
109
278
  import { invariant } from "@dxos/invariant";
110
- import { getSnapshot } from "@dxos/live-object";
111
- import { getSpace } from "@dxos/react-client/echo";
112
279
  import { Popover } from "@dxos/react-ui";
113
- import { Form, omitId } from "@dxos/react-ui-form";
280
+ import { Form } from "@dxos/react-ui-form";
114
281
  import { parseCellIndex, useGridContext as useGridContext2 } from "@dxos/react-ui-grid";
115
282
  import { getDeep, isTruthy, setDeep } from "@dxos/util";
116
283
  var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
@@ -121,205 +288,166 @@ var createOptionLabel = [
121
288
  }
122
289
  ];
123
290
  var FormCellEditor = ({ __gridScope, schema, model, fieldProjection, onSave, onCreate, ...formProps }) => {
124
- var _effect = _useSignals2();
125
- try {
126
- const anchorRef = useRef(null);
127
- const { id: _gridId, editing: contextEditing, setEditing } = useGridContext2("ArrayEditor", __gridScope);
128
- const [editing, setLocalEditing] = useState(false);
129
- useEffect(() => {
130
- if (contextEditing && contextEditing.cellElement) {
131
- anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
132
- setLocalEditing(true);
133
- } else {
134
- anchorRef.current = null;
135
- setLocalEditing(false);
136
- }
137
- }, [
138
- contextEditing
139
- ]);
140
- const narrowedSchema = useMemo(() => {
141
- if (!schema) {
142
- return void 0;
143
- }
144
- return narrowSchema(schema, [
145
- fieldProjection.field.path
146
- ]);
147
- }, [
148
- JSON.stringify(schema),
291
+ const anchorRef = useRef(null);
292
+ const { id: _gridId, editing: contextEditing, setEditing } = useGridContext2("ArrayEditor", __gridScope);
293
+ const [editing, setLocalEditing] = useState(false);
294
+ useEffect(() => {
295
+ if (contextEditing && contextEditing.cellElement) {
296
+ anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
297
+ setLocalEditing(true);
298
+ } else {
299
+ anchorRef.current = null;
300
+ setLocalEditing(false);
301
+ }
302
+ }, [
303
+ contextEditing
304
+ ]);
305
+ const narrowedSchema = useMemo(() => {
306
+ if (!schema) {
307
+ return void 0;
308
+ }
309
+ return narrowSchema(schema, [
149
310
  fieldProjection.field.path
150
311
  ]);
151
- const getSchema = useCallback(({ typename }) => {
152
- const space = getSpace(model.view);
153
- invariant(space, void 0, {
154
- F: __dxlog_file,
155
- L: 73,
156
- S: void 0,
157
- A: [
158
- "space",
159
- ""
160
- ]
161
- });
162
- const schema2 = space.db.schemaRegistry.query({
163
- typename,
164
- location: [
165
- "database",
166
- "runtime"
167
- ]
168
- }).runSync()[0];
169
- return {
170
- space,
171
- schema: schema2
172
- };
173
- }, [
174
- model
175
- ]);
176
- const refSchema = useMemo(() => {
177
- if (fieldProjection.props.format === Format.TypeFormat.Ref && fieldProjection.props.referenceSchema) {
178
- const { schema: schema2 } = getSchema({
179
- typename: fieldProjection.props.referenceSchema
312
+ }, [
313
+ JSON.stringify(schema),
314
+ fieldProjection.field.path
315
+ ]);
316
+ const originalRow = useMemo(() => {
317
+ if (model && contextEditing) {
318
+ const cell = parseCellIndex(contextEditing.index);
319
+ if (model.isDraftCell(cell)) {
320
+ const draftRow = model.getDraftRows()[cell.row];
321
+ invariant(draftRow, void 0, {
322
+ F: __dxlog_file,
323
+ L: 74,
324
+ S: void 0,
325
+ A: [
326
+ "draftRow",
327
+ ""
328
+ ]
180
329
  });
181
- return schema2;
182
- }
183
- return null;
184
- }, [
185
- fieldProjection.props.format,
186
- fieldProjection.props.referenceSchema,
187
- getSchema
188
- ]);
189
- const createSchema = useMemo(() => refSchema ? omitId(refSchema) : null, [
190
- refSchema
191
- ]);
192
- const originalRow = useMemo(() => {
193
- if (model && contextEditing) {
194
- const cell = parseCellIndex(contextEditing.index);
195
- if (model.isDraftCell(cell)) {
196
- const draftRow = model.draftRows.value[cell.row];
197
- invariant(draftRow, void 0, {
198
- F: __dxlog_file,
199
- L: 97,
200
- S: void 0,
201
- A: [
202
- "draftRow",
203
- ""
204
- ]
205
- });
206
- return draftRow.data;
207
- } else {
208
- const row = model.getRowAt(cell.row);
209
- invariant(row, void 0, {
210
- F: __dxlog_file,
211
- L: 101,
212
- S: void 0,
213
- A: [
214
- "row",
215
- ""
216
- ]
217
- });
218
- return row;
219
- }
330
+ return draftRow.data;
331
+ } else {
332
+ const row = model.getRowAt(cell.row);
333
+ invariant(row, void 0, {
334
+ F: __dxlog_file,
335
+ L: 78,
336
+ S: void 0,
337
+ A: [
338
+ "row",
339
+ ""
340
+ ]
341
+ });
342
+ return row;
220
343
  }
221
- return void 0;
222
- }, [
223
- model,
224
- contextEditing
225
- ]);
226
- const formValues = useMemo(() => originalRow ? getSnapshot(originalRow) : {}, [
227
- originalRow
344
+ }
345
+ return void 0;
346
+ }, [
347
+ model,
348
+ contextEditing
349
+ ]);
350
+ const formValues = useMemo(() => originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}, [
351
+ originalRow
352
+ ]);
353
+ const handleOpenChange = useCallback((nextOpen) => {
354
+ if (nextOpen === false) {
355
+ setEditing(null);
356
+ onSave?.();
357
+ }
358
+ setLocalEditing(nextOpen);
359
+ }, []);
360
+ const handleSave = useCallback((values) => {
361
+ if (!originalRow) {
362
+ return;
363
+ }
364
+ const path = fieldProjection.field.path;
365
+ const value = getDeep(values, [
366
+ path
228
367
  ]);
229
- const handleOpenChange = useCallback((nextOpen) => {
230
- if (nextOpen === false) {
231
- setEditing(null);
232
- onSave?.();
233
- }
234
- setLocalEditing(nextOpen);
235
- }, []);
236
- const handleSave = useCallback((values) => {
237
- const path = fieldProjection.field.path;
238
- const value = getDeep(values, [
239
- path
240
- ]);
368
+ if (model) {
369
+ model.changeRow(originalRow, (mutableRow) => {
370
+ setDeep(mutableRow, [
371
+ path
372
+ ], value);
373
+ });
374
+ } else {
241
375
  setDeep(originalRow, [
242
376
  path
243
377
  ], value);
244
- contextEditing?.cellElement?.focus();
245
- setEditing(null);
246
- setLocalEditing(false);
247
- onSave?.();
248
- }, [
249
- fieldProjection.field.path,
250
- onSave,
251
- contextEditing,
252
- originalRow
253
- ]);
254
- const handleCreate = useCallback((values) => {
255
- if (refSchema && onCreate) {
256
- const objectWithId = onCreate(refSchema, values);
257
- if (objectWithId) {
258
- const ref = Ref.make(objectWithId);
259
- const path = fieldProjection.field.path;
260
- setDeep(originalRow, [
378
+ }
379
+ contextEditing?.cellElement?.focus();
380
+ setEditing(null);
381
+ setLocalEditing(false);
382
+ onSave?.();
383
+ }, [
384
+ fieldProjection.field.path,
385
+ onSave,
386
+ contextEditing,
387
+ originalRow,
388
+ model
389
+ ]);
390
+ const handleCreate = useCallback((schema2, values) => {
391
+ const objectWithId = onCreate?.(schema2, values);
392
+ if (objectWithId && originalRow) {
393
+ const ref = Ref2.make(objectWithId);
394
+ const path = fieldProjection.field.path;
395
+ if (model) {
396
+ model.changeRow(originalRow, (mutableRow) => {
397
+ setDeep(mutableRow, [
261
398
  path
262
399
  ], ref);
263
- }
264
- }
265
- contextEditing?.cellElement?.focus();
266
- setEditing(null);
267
- setLocalEditing(false);
268
- onSave?.();
269
- }, [
270
- fieldProjection.field.path,
271
- onSave,
272
- contextEditing,
273
- originalRow,
274
- refSchema,
275
- onCreate
276
- ]);
277
- const handleQueryRefOptions = useCallback(async ({ typename }) => {
278
- const { schema: schema2, space } = getSchema({
279
- typename
280
- });
281
- if (model && schema2 && space) {
282
- const objects = await space.db.query(Filter.type(schema2)).run();
283
- return objects.map((obj) => {
284
- return {
285
- dxn: Obj.getDXN(obj),
286
- label: getValue(obj, fieldProjection.field.referencePath) || obj.id.toString()
287
- };
288
- }).filter(isTruthy);
400
+ });
401
+ } else {
402
+ setDeep(originalRow, [
403
+ path
404
+ ], ref);
289
405
  }
290
- return [];
291
- }, [
292
- model
293
- ]);
294
- if (!editing) {
295
- return null;
296
406
  }
297
- return /* @__PURE__ */ React2.createElement(Popover.Root, {
298
- open: editing,
299
- onOpenChange: handleOpenChange
300
- }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
301
- virtualRef: anchorRef
302
- }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
303
- tabIndex: -1,
304
- classNames: "popover-card-width density-fine"
305
- }, /* @__PURE__ */ React2.createElement(Popover.Arrow, null), /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(Form.Root, {
306
- ...formProps,
307
- autoFocus: true,
308
- schema: narrowedSchema,
309
- values: formValues,
310
- onSave: handleSave,
311
- onQueryRefOptions: handleQueryRefOptions,
312
- ...createSchema && {
313
- createSchema,
314
- createInitialValuePath: fieldProjection.field.referencePath,
315
- createOptionIcon: "ph--plus--regular",
316
- createOptionLabel,
317
- onCreate: handleCreate
318
- }
319
- }, /* @__PURE__ */ React2.createElement(Form.Viewport, null, /* @__PURE__ */ React2.createElement(Form.Content, null, /* @__PURE__ */ React2.createElement(Form.FieldSet, null), /* @__PURE__ */ React2.createElement(Form.Actions, null))))))));
320
- } finally {
321
- _effect.f();
407
+ contextEditing?.cellElement?.focus();
408
+ setEditing(null);
409
+ setLocalEditing(false);
410
+ onSave?.();
411
+ }, [
412
+ fieldProjection.field.path,
413
+ onSave,
414
+ contextEditing,
415
+ originalRow,
416
+ onCreate,
417
+ model
418
+ ]);
419
+ const getOptions = useCallback((results) => results.map((obj) => {
420
+ return {
421
+ id: Entity.getDXN(obj).toString(),
422
+ label: getValue(obj, fieldProjection.field.referencePath) || obj.id.toString()
423
+ };
424
+ }).filter(isTruthy), [
425
+ fieldProjection
426
+ ]);
427
+ if (!editing) {
428
+ return null;
322
429
  }
430
+ return /* @__PURE__ */ React2.createElement(Popover.Root, {
431
+ open: editing,
432
+ onOpenChange: handleOpenChange
433
+ }, /* @__PURE__ */ React2.createElement(Popover.VirtualTrigger, {
434
+ virtualRef: anchorRef
435
+ }), /* @__PURE__ */ React2.createElement(Popover.Portal, null, /* @__PURE__ */ React2.createElement(Popover.Content, {
436
+ tabIndex: -1,
437
+ classNames: "popover-card-width density-fine"
438
+ }, /* @__PURE__ */ React2.createElement(Popover.Arrow, null), /* @__PURE__ */ React2.createElement(Popover.Viewport, null, /* @__PURE__ */ React2.createElement(Form.Root, {
439
+ ...formProps,
440
+ autoFocus: true,
441
+ schema: narrowedSchema,
442
+ values: formValues,
443
+ createInitialValuePath: fieldProjection.field.referencePath,
444
+ createOptionIcon: "ph--plus--regular",
445
+ createOptionLabel,
446
+ db: model?.db,
447
+ getOptions,
448
+ onCreate: handleCreate,
449
+ onSave: handleSave
450
+ }, /* @__PURE__ */ React2.createElement(Form.Viewport, null, /* @__PURE__ */ React2.createElement(Form.Content, null, /* @__PURE__ */ React2.createElement(Form.FieldSet, null), /* @__PURE__ */ React2.createElement(Form.Actions, null))))))));
323
451
  };
324
452
 
325
453
  // src/components/TableCellEditor/TableCellEditor.tsx
@@ -330,215 +458,205 @@ var editorSlots = {
330
458
  }
331
459
  };
332
460
  var TableValueEditor = ({ __gridScope, schema, model, modals, onFocus, onSave, onCreate }) => {
333
- var _effect = _useSignals3();
334
- try {
335
- const { editing } = useGridContext3("TableValueEditor", __gridScope);
336
- const fieldProjection = useMemo2(() => {
337
- if (!model || !editing) {
338
- return;
339
- }
340
- const { col } = parseCellIndex2(editing.index);
341
- const field = model.projection.fields[col];
342
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
343
- invariant2(fieldProjection2, void 0, {
344
- F: __dxlog_file2,
345
- L: 73,
346
- S: void 0,
347
- A: [
348
- "fieldProjection",
349
- ""
350
- ]
351
- });
352
- return fieldProjection2;
353
- }, [
354
- model,
355
- editing
356
- ]);
357
- if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === Format2.TypeFormat.SingleSelect || fieldProjection?.props.format === Format2.TypeFormat.Ref) {
358
- return /* @__PURE__ */ React3.createElement(FormCellEditor, {
359
- __gridScope,
360
- schema,
361
- model,
362
- fieldProjection,
363
- modals,
364
- onSave,
365
- onCreate
366
- });
461
+ const { editing } = useGridContext3("TableValueEditor", __gridScope);
462
+ const fieldProjection = useMemo2(() => {
463
+ if (!model || !editing) {
464
+ return;
367
465
  }
368
- return /* @__PURE__ */ React3.createElement(TableCellEditor, {
466
+ const { col } = parseCellIndex2(editing.index);
467
+ const field = model.projection.getFields()[col];
468
+ const fieldProjection2 = model.projection.getFieldProjection(field.id);
469
+ invariant2(fieldProjection2, void 0, {
470
+ F: __dxlog_file2,
471
+ L: 73,
472
+ S: void 0,
473
+ A: [
474
+ "fieldProjection",
475
+ ""
476
+ ]
477
+ });
478
+ return fieldProjection2;
479
+ }, [
480
+ model,
481
+ editing
482
+ ]);
483
+ if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === Format.TypeFormat.SingleSelect || fieldProjection?.props.format === Format.TypeFormat.Ref) {
484
+ return /* @__PURE__ */ React3.createElement(FormCellEditor, {
485
+ __gridScope,
486
+ schema,
369
487
  model,
488
+ fieldProjection,
370
489
  modals,
371
- onFocus,
372
490
  onSave,
373
- __gridScope
491
+ onCreate
374
492
  });
375
- } finally {
376
- _effect.f();
377
493
  }
494
+ return /* @__PURE__ */ React3.createElement(TableCellEditor, {
495
+ model,
496
+ modals,
497
+ onFocus,
498
+ onSave,
499
+ __gridScope
500
+ });
378
501
  };
379
502
  var TableCellEditor = ({ __gridScope, model, modals, onFocus, onSave }) => {
380
- var _effect = _useSignals3();
381
- try {
382
- const { editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
383
- const suppressNextBlur = useRef2(false);
384
- const [validationError, setValidationError] = useState2(null);
385
- const [validationVariant, setValidationVariant] = useState2("error");
386
- const fieldProjection = useMemo2(() => {
387
- if (!model || !editing) {
388
- return;
389
- }
390
- const { col } = parseCellIndex2(editing.index);
391
- const field = model.projection.fields[col];
392
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
393
- invariant2(fieldProjection2, void 0, {
394
- F: __dxlog_file2,
395
- L: 120,
396
- S: void 0,
397
- A: [
398
- "fieldProjection",
399
- ""
400
- ]
401
- });
402
- return fieldProjection2;
403
- }, [
404
- model,
405
- editing
406
- ]);
407
- const handleEnter = useCallback2(async (value) => {
408
- if (!model || !editing) {
409
- return;
410
- }
411
- const cell = parseCellIndex2(editing.index);
412
- const validationResult = await model.validateCellData(cell, value);
413
- if (validationResult.valid) {
414
- setValidationError(null);
415
- model.setCellData(cell, value);
416
- onFocus?.();
417
- setEditing(null);
418
- onSave?.();
419
- } else {
420
- setValidationError(validationResult.error);
421
- setValidationVariant("error");
422
- }
423
- }, [
424
- model,
425
- editing,
426
- onFocus,
427
- setEditing
428
- ]);
429
- const handleBlur = useCallback2(async (value) => {
430
- if (suppressNextBlur.current) {
431
- suppressNextBlur.current = false;
432
- } else if (model && editing) {
433
- const cell = parseCellIndex2(editing.index);
434
- if (value !== void 0) {
435
- const result = await model.validateCellData(cell, value);
436
- if (result.valid) {
437
- setValidationError(null);
438
- model.setCellData(cell, value);
439
- setEditing(null);
440
- onSave?.();
441
- }
442
- }
443
- }
444
- }, [
445
- model,
446
- editing
447
- ]);
448
- const handleClose = useCallback2(async (value, event) => {
449
- if (!model || !editing || !fieldProjection) {
450
- return;
451
- }
503
+ const { editing, setEditing } = useGridContext3("TableCellEditor", __gridScope);
504
+ const suppressNextBlur = useRef2(false);
505
+ const [validationError, setValidationError] = useState2(null);
506
+ const [validationVariant, setValidationVariant] = useState2("error");
507
+ const fieldProjection = useMemo2(() => {
508
+ if (!model || !editing) {
509
+ return;
510
+ }
511
+ const { col } = parseCellIndex2(editing.index);
512
+ const field = model.projection.getFields()[col];
513
+ const fieldProjection2 = model.projection.getFieldProjection(field.id);
514
+ invariant2(fieldProjection2, void 0, {
515
+ F: __dxlog_file2,
516
+ L: 120,
517
+ S: void 0,
518
+ A: [
519
+ "fieldProjection",
520
+ ""
521
+ ]
522
+ });
523
+ return fieldProjection2;
524
+ }, [
525
+ model,
526
+ editing
527
+ ]);
528
+ const handleEnter = useCallback2(async (value) => {
529
+ if (!model || !editing) {
530
+ return;
531
+ }
532
+ const cell = parseCellIndex2(editing.index);
533
+ const validationResult = await model.validateCellData(cell, value);
534
+ if (validationResult.valid) {
535
+ setValidationError(null);
536
+ model.setCellData(cell, value);
537
+ onFocus?.();
538
+ setEditing(null);
539
+ onSave?.();
540
+ } else {
541
+ setValidationError(validationResult.error);
542
+ setValidationVariant("error");
543
+ }
544
+ }, [
545
+ model,
546
+ editing,
547
+ onFocus,
548
+ setEditing
549
+ ]);
550
+ const handleBlur = useCallback2(async (value) => {
551
+ if (suppressNextBlur.current) {
552
+ suppressNextBlur.current = false;
553
+ } else if (model && editing) {
452
554
  const cell = parseCellIndex2(editing.index);
453
555
  if (value !== void 0) {
454
556
  const result = await model.validateCellData(cell, value);
455
557
  if (result.valid) {
456
- suppressNextBlur.current = true;
457
558
  setValidationError(null);
458
559
  model.setCellData(cell, value);
459
560
  setEditing(null);
460
561
  onSave?.();
461
- if (event && onFocus) {
462
- onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
463
- }
464
- } else {
465
- setValidationError(result.error);
466
- setValidationVariant("error");
467
562
  }
468
- } else {
563
+ }
564
+ }
565
+ }, [
566
+ model,
567
+ editing
568
+ ]);
569
+ const handleClose = useCallback2(async (value, event) => {
570
+ if (!model || !editing || !fieldProjection) {
571
+ return;
572
+ }
573
+ const cell = parseCellIndex2(editing.index);
574
+ if (value !== void 0) {
575
+ const result = await model.validateCellData(cell, value);
576
+ if (result.valid) {
469
577
  suppressNextBlur.current = true;
470
578
  setValidationError(null);
579
+ model.setCellData(cell, value);
471
580
  setEditing(null);
472
581
  onSave?.();
473
582
  if (event && onFocus) {
474
- onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
583
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
475
584
  }
585
+ } else {
586
+ setValidationError(result.error);
587
+ setValidationVariant("error");
476
588
  }
477
- }, [
478
- model,
479
- editing,
480
- onFocus,
481
- fieldProjection,
482
- setEditing,
483
- onSave
484
- ]);
485
- const extensions = useMemo2(() => {
486
- if (!fieldProjection) {
487
- return [];
488
- }
489
- const extensions2 = [
490
- editorKeys({
491
- onClose: handleClose,
492
- ...editing?.initialContent && {
493
- onNav: handleClose
494
- }
495
- })
496
- ];
497
- if (model && editing) {
498
- extensions2.push(EditorView.updateListener.of(debounce((update) => {
499
- const content = update.state.doc.toString();
500
- const cell = parseCellIndex2(editing.index);
501
- void model.validateCellData(cell, content).then((result) => {
502
- if (result.valid) {
503
- setValidationError(null);
504
- } else {
505
- setValidationError(result.error);
506
- setValidationVariant("error");
507
- }
508
- });
509
- }, 10)));
589
+ } else {
590
+ suppressNextBlur.current = true;
591
+ setValidationError(null);
592
+ setEditing(null);
593
+ onSave?.();
594
+ if (event && onFocus) {
595
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
510
596
  }
511
- return extensions2;
512
- }, [
513
- model,
514
- modals,
515
- editing,
516
- fieldProjection,
517
- handleClose
518
- ]);
519
- const getCellContent = useCallback2(() => {
520
- if (model && editing) {
597
+ }
598
+ }, [
599
+ model,
600
+ editing,
601
+ onFocus,
602
+ fieldProjection,
603
+ setEditing,
604
+ onSave
605
+ ]);
606
+ const extensions = useMemo2(() => {
607
+ if (!fieldProjection) {
608
+ return [];
609
+ }
610
+ const extensions2 = [
611
+ editorKeys({
612
+ onClose: handleClose,
613
+ ...editing?.initialContent && {
614
+ onNav: handleClose
615
+ }
616
+ })
617
+ ];
618
+ if (model && editing) {
619
+ extensions2.push(EditorView.updateListener.of(debounce((update) => {
620
+ const content = update.state.doc.toString();
521
621
  const cell = parseCellIndex2(editing.index);
522
- const value = model.getCellData(cell);
523
- return value !== void 0 ? String(value) : "";
524
- }
525
- }, [
526
- model,
527
- editing
528
- ]);
529
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(CellValidationMessage, {
530
- validationError,
531
- variant: validationVariant,
532
- __gridScope
533
- }), /* @__PURE__ */ React3.createElement(GridCellEditor, {
534
- extensions,
535
- getCellContent,
536
- onBlur: handleBlur,
537
- slots: editorSlots
538
- }));
539
- } finally {
540
- _effect.f();
541
- }
622
+ void model.validateCellData(cell, content).then((result) => {
623
+ if (result.valid) {
624
+ setValidationError(null);
625
+ } else {
626
+ setValidationError(result.error);
627
+ setValidationVariant("error");
628
+ }
629
+ });
630
+ }, 10)));
631
+ }
632
+ return extensions2;
633
+ }, [
634
+ model,
635
+ modals,
636
+ editing,
637
+ fieldProjection,
638
+ handleClose
639
+ ]);
640
+ const getCellContent = useCallback2(() => {
641
+ if (model && editing) {
642
+ const cell = parseCellIndex2(editing.index);
643
+ const value = model.getCellData(cell);
644
+ return value !== void 0 ? String(value) : "";
645
+ }
646
+ }, [
647
+ model,
648
+ editing
649
+ ]);
650
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(CellValidationMessage, {
651
+ validationError,
652
+ variant: validationVariant,
653
+ __gridScope
654
+ }), /* @__PURE__ */ React3.createElement(GridCellEditor, {
655
+ extensions,
656
+ getCellContent,
657
+ onBlur: handleBlur,
658
+ slots: editorSlots
659
+ }));
542
660
  };
543
661
  var determineNavigationAxis = ({ key }) => {
544
662
  switch (key) {
@@ -566,154 +684,140 @@ var determineNavigationDelta = ({ key, shift }) => {
566
684
  };
567
685
 
568
686
  // src/components/Table/ColumnActionsMenu.tsx
569
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
687
+ import { useAtomValue } from "@effect-atom/atom-react";
570
688
  import React4 from "react";
571
689
  import { DropdownMenu, useTranslation } from "@dxos/react-ui";
572
690
  var ColumnActionsMenu = ({ model, modals }) => {
573
- var _effect = _useSignals4();
574
- try {
575
- const { t } = useTranslation(translationKey);
576
- const state = modals.state.value;
577
- if (state?.type !== "column") {
578
- return null;
579
- }
580
- const currentSort = model.sorting?.sorting;
581
- const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
582
- return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
583
- modal: false,
584
- open: true,
585
- onOpenChange: modals.close
586
- }, /* @__PURE__ */ React4.createElement(DropdownMenu.VirtualTrigger, {
587
- virtualRef: modals.trigger
588
- }), /* @__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, {
589
- "data-testid": "column-sort-descending",
590
- onClick: () => model.sorting?.setSort(state.fieldId, "desc")
591
- }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
592
- "data-testid": "column-sort-ascending",
593
- onClick: () => model.sorting?.setSort(state.fieldId, "asc")
594
- }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
595
- "data-testid": "column-clear-sort",
596
- onClick: () => model.sorting?.clearSort()
597
- }, t("column action clear sorting")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
598
- "data-testid": "column-delete",
599
- onClick: () => model.deleteColumn(state.fieldId)
600
- }, t("column action delete")), model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
601
- "data-testid": "column-settings",
602
- onClick: () => modals.openColumnSettings()
603
- }, t("column action settings"))), /* @__PURE__ */ React4.createElement(DropdownMenu.Arrow, null))));
604
- } finally {
605
- _effect.f();
691
+ const { t } = useTranslation(translationKey);
692
+ const state = useAtomValue(modals.state);
693
+ if (state?.type !== "column") {
694
+ return null;
606
695
  }
696
+ const currentSort = model.getSorting();
697
+ const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
698
+ return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
699
+ modal: false,
700
+ open: true,
701
+ onOpenChange: modals.close
702
+ }, /* @__PURE__ */ React4.createElement(DropdownMenu.VirtualTrigger, {
703
+ virtualRef: modals.trigger
704
+ }), /* @__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, {
705
+ "data-testid": "column-sort-descending",
706
+ onClick: () => model.setSort(state.fieldId, "desc")
707
+ }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
708
+ "data-testid": "column-sort-ascending",
709
+ onClick: () => model.setSort(state.fieldId, "asc")
710
+ }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
711
+ "data-testid": "column-clear-sort",
712
+ onClick: () => model.clearSort()
713
+ }, t("column action clear sorting")), model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
714
+ "data-testid": "column-delete",
715
+ onClick: () => model.deleteColumn(state.fieldId)
716
+ }, t("column action delete")), model.features.schemaEditable && /* @__PURE__ */ React4.createElement(DropdownMenu.Item, {
717
+ "data-testid": "column-settings",
718
+ onClick: () => modals.openColumnSettings()
719
+ }, t("column action settings"))), /* @__PURE__ */ React4.createElement(DropdownMenu.Arrow, null))));
607
720
  };
608
721
 
609
722
  // src/components/Table/ColumnSettings.tsx
610
- import { useSignals as _useSignals5 } from "@preact-signals/safe-react/tracking";
723
+ import { useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
611
724
  import React5, { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo3, useState as useState3 } from "react";
612
725
  import { Popover as Popover2 } from "@dxos/react-ui";
613
726
  import { FieldEditor } from "@dxos/react-ui-form";
614
727
  var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
615
- var _effect = _useSignals5();
616
- try {
617
- const [newField, setNewField] = useState3();
618
- const state = modals.state.value;
619
- useEffect2(() => {
620
- if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
621
- setNewField(model.projection.createFieldProjection());
622
- requestAnimationFrame(() => {
623
- onNewColumn();
624
- });
625
- } else {
626
- setNewField(void 0);
627
- }
628
- }, [
629
- model?.projection,
630
- state
631
- ]);
632
- const existingField = useMemo3(() => {
633
- if (state?.type === "columnSettings") {
634
- const { mode } = state;
635
- if (mode.type === "edit") {
636
- return model?.projection?.fields.find((f) => f.id === mode.fieldId);
637
- }
638
- }
639
- return void 0;
640
- }, [
641
- model?.projection?.fields,
642
- state
643
- ]);
644
- const field = existingField ?? newField;
645
- const handleSave = useCallback3(() => {
646
- modals.close();
647
- }, [
648
- modals
649
- ]);
650
- const handleCancel = useCallback3(() => {
651
- if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
652
- model?.projection.deleteFieldProjection(newField.id);
728
+ const [newField, setNewField] = useState3();
729
+ const state = useAtomValue2(modals.state);
730
+ useEffect2(() => {
731
+ if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
732
+ setNewField(model.projection.createFieldProjection());
733
+ requestAnimationFrame(() => {
734
+ onNewColumn();
735
+ });
736
+ } else {
737
+ setNewField(void 0);
738
+ }
739
+ }, [
740
+ model?.projection,
741
+ state
742
+ ]);
743
+ const existingField = useMemo3(() => {
744
+ if (state?.type === "columnSettings") {
745
+ const { mode } = state;
746
+ if (mode.type === "edit") {
747
+ return model?.projection?.getFields().find((f) => f.id === mode.fieldId);
653
748
  }
654
- }, [
655
- model?.projection,
656
- state,
657
- newField
658
- ]);
659
- if (!model?.projection || !field) {
660
- return null;
661
749
  }
662
- return /* @__PURE__ */ React5.createElement(Popover2.Root, {
663
- modal: false,
664
- open: state?.type === "columnSettings"
665
- }, /* @__PURE__ */ React5.createElement(Popover2.VirtualTrigger, {
666
- virtualRef: modals.trigger
667
- }), /* @__PURE__ */ React5.createElement(Popover2.Portal, null, /* @__PURE__ */ React5.createElement(Popover2.Content, {
668
- classNames: "md:is-64"
669
- }, /* @__PURE__ */ React5.createElement(Popover2.Viewport, null, /* @__PURE__ */ React5.createElement(FieldEditor, {
670
- projection: model.projection,
671
- field,
672
- registry,
673
- onSave: handleSave,
674
- onCancel: handleCancel
675
- })), /* @__PURE__ */ React5.createElement(Popover2.Arrow, null))));
676
- } finally {
677
- _effect.f();
750
+ return void 0;
751
+ }, [
752
+ model?.projection,
753
+ state
754
+ ]);
755
+ const field = existingField ?? newField;
756
+ const handleSave = useCallback3(() => {
757
+ modals.close();
758
+ }, [
759
+ modals
760
+ ]);
761
+ const handleCancel = useCallback3(() => {
762
+ if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
763
+ model?.projection.deleteFieldProjection(newField.id);
764
+ }
765
+ }, [
766
+ model?.projection,
767
+ state,
768
+ newField
769
+ ]);
770
+ if (!model?.projection || !field) {
771
+ return null;
678
772
  }
773
+ return /* @__PURE__ */ React5.createElement(Popover2.Root, {
774
+ modal: false,
775
+ open: state?.type === "columnSettings"
776
+ }, /* @__PURE__ */ React5.createElement(Popover2.VirtualTrigger, {
777
+ virtualRef: modals.trigger
778
+ }), /* @__PURE__ */ React5.createElement(Popover2.Portal, null, /* @__PURE__ */ React5.createElement(Popover2.Content, {
779
+ classNames: "md:is-64"
780
+ }, /* @__PURE__ */ React5.createElement(Popover2.Viewport, null, /* @__PURE__ */ React5.createElement(FieldEditor, {
781
+ projection: model.projection,
782
+ field,
783
+ registry,
784
+ onSave: handleSave,
785
+ onCancel: handleCancel
786
+ })), /* @__PURE__ */ React5.createElement(Popover2.Arrow, null))));
679
787
  };
680
788
 
681
789
  // src/components/Table/RowActionsMenu.tsx
682
- import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
790
+ import { useAtomValue as useAtomValue3 } from "@effect-atom/atom-react";
683
791
  import React6 from "react";
684
792
  import { DropdownMenu as DropdownMenu2, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
685
793
  var RowActionsMenu = ({ model, modals }) => {
686
- var _effect = _useSignals6();
687
- try {
688
- const { t } = useTranslation2(translationKey);
689
- const hasSelection = model.selection.hasSelection.value;
690
- const state = modals.state.value;
691
- if (state?.type !== "row") {
692
- return null;
693
- }
694
- return /* @__PURE__ */ React6.createElement(DropdownMenu2.Root, {
695
- modal: false,
696
- open: true,
697
- onOpenChange: modals.close
698
- }, /* @__PURE__ */ React6.createElement(DropdownMenu2.VirtualTrigger, {
699
- virtualRef: modals.trigger
700
- }), /* @__PURE__ */ React6.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React6.createElement(DropdownMenu2.Viewport, null, model.rowActions?.length > 0 && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(DropdownMenu2.Group, null, model.rowActions?.map((action) => /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
701
- key: action.id,
702
- "data-testid": `row-action-${action.id}`,
703
- onClick: () => {
704
- modals.close();
705
- model.handleRowAction(action.id, state.rowIndex);
706
- }
707
- }, toLocalizedString(action.label, t)))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Separator, null)), model.features.dataEditable !== false && /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
708
- "data-testid": "row-menu-delete",
709
- onClick: () => model.deleteRow(state.rowIndex)
710
- }, t(hasSelection ? "bulk delete row label" : "delete row label"))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Arrow, null)));
711
- } finally {
712
- _effect.f();
794
+ const { t } = useTranslation2(translationKey);
795
+ const hasSelection = model.selection.hasSelection;
796
+ const state = useAtomValue3(modals.state);
797
+ if (state?.type !== "row") {
798
+ return null;
713
799
  }
800
+ return /* @__PURE__ */ React6.createElement(DropdownMenu2.Root, {
801
+ modal: false,
802
+ open: true,
803
+ onOpenChange: modals.close
804
+ }, /* @__PURE__ */ React6.createElement(DropdownMenu2.VirtualTrigger, {
805
+ virtualRef: modals.trigger
806
+ }), /* @__PURE__ */ React6.createElement(DropdownMenu2.Content, null, /* @__PURE__ */ React6.createElement(DropdownMenu2.Viewport, null, model.rowActions?.length > 0 && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(DropdownMenu2.Group, null, model.rowActions?.map((action) => /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
807
+ key: action.id,
808
+ "data-testid": `row-action-${action.id}`,
809
+ onClick: () => {
810
+ modals.close();
811
+ model.handleRowAction(action.id, state.rowIndex);
812
+ }
813
+ }, toLocalizedString(action.label, t)))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Separator, null)), model.features.dataEditable !== false && /* @__PURE__ */ React6.createElement(DropdownMenu2.Item, {
814
+ "data-testid": "row-menu-delete",
815
+ onClick: () => model.deleteRow(state.rowIndex)
816
+ }, t(hasSelection ? "bulk delete row label" : "delete row label"))), /* @__PURE__ */ React6.createElement(DropdownMenu2.Arrow, null)));
714
817
  };
715
818
 
716
819
  // src/components/Table/Table.tsx
820
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
717
821
  var columnDefault = {
718
822
  grid: {
719
823
  minSize: 80,
@@ -726,365 +830,375 @@ var rowDefault = {
726
830
  focusUnfurl: false
727
831
  }
728
832
  };
833
+ var emptyColumnMeta = Atom2.make({
834
+ grid: {}
835
+ });
729
836
  var TableRoot = ({ children, role = "article" }) => {
730
- var _effect = _useSignals7();
731
- try {
732
- return /* @__PURE__ */ React7.createElement("div", {
733
- role: "none",
734
- 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]", [
735
- "card--popover",
736
- "section",
737
- "card--extrinsic"
738
- ].includes(role) && "overflow-hidden", [
739
- "article",
740
- "slide"
741
- ].includes(role) && "flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0")
742
- }, children);
743
- } finally {
744
- _effect.f();
745
- }
837
+ return /* @__PURE__ */ React7.createElement("div", {
838
+ role: "none",
839
+ 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]", [
840
+ "card--popover",
841
+ "section",
842
+ "card--extrinsic"
843
+ ].includes(role) && "overflow-hidden", [
844
+ "article",
845
+ "slide"
846
+ ].includes(role) && "flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:bs-0")
847
+ }, children);
746
848
  };
849
+ var emptyRowsAtom = Atom2.make([]);
850
+ var emptyCellUpdateAtom = Atom2.make(0);
747
851
  var TableMainInner = ({ schema, model, presentation, ignoreAttention, onCreate, onRowClick, testId }, forwardedRef) => {
748
- var _effect = _useSignals7();
749
- try {
750
- const [dxGrid, setDxGrid] = useState4(null);
751
- const { hasAttention } = useAttention(model?.id ?? "table");
752
- const modals = useMemo4(() => new ModalController(), []);
753
- const draftRowCount = model?.getDraftRowCount() ?? 0;
754
- const frozen = useMemo4(() => {
755
- const noActionColumn = model?.features.dataEditable === false && model?.features.schemaEditable === false && model.rowActions.length === 0;
756
- return {
757
- frozenRowsStart: 1,
758
- frozenRowsEnd: Math.max(1, draftRowCount),
759
- frozenColsStart: model?.features.selection.enabled ? 1 : 0,
760
- frozenColsEnd: noActionColumn ? 0 : 1
761
- };
762
- }, [
763
- model,
764
- draftRowCount
765
- ]);
766
- const getCells = useCallback4((range, plane) => presentation?.getCells(range, plane) ?? {}, [
767
- presentation
768
- ]);
769
- useEffect3(() => {
770
- const fieldId = model?.projection?.fields?.[0]?.id;
771
- if (fieldId) {
772
- model.sorting.setSort(fieldId, "asc");
773
- }
774
- }, [
775
- model
776
- ]);
777
- useEffect3(() => {
778
- if (!dxGrid || !presentation) {
779
- return;
780
- }
781
- dxGrid.getCells = getCells;
782
- }, [
783
- dxGrid,
784
- presentation,
785
- getCells
786
- ]);
787
- const handleInsertRowResult = useCallback4((insertResult) => {
788
- if (insertResult === "draft") {
789
- requestAnimationFrame(() => {
790
- dxGrid?.setFocus({
791
- plane: "frozenRowsEnd",
792
- col: 0,
793
- row: 0
794
- });
795
- dxGrid?.refocus();
852
+ const registry = useContext(RegistryContext);
853
+ const [dxGrid, setDxGrid] = useState4(null);
854
+ const { hasAttention } = useAttention(model?.id ?? "table");
855
+ const modals = useMemo4(() => new ModalController(registry), [
856
+ registry
857
+ ]);
858
+ const columnMeta = useAtomValue4(model?.columnMeta ?? emptyColumnMeta);
859
+ const rows = useAtomValue4(model?.rows ?? emptyRowsAtom);
860
+ const cellUpdateCounter = useAtomValue4(model?.cellUpdate ?? emptyCellUpdateAtom);
861
+ const columnCount = Object.keys(columnMeta.grid).length || model?.projection.getFields().length || 0;
862
+ const draftRowCount = model?.getDraftRowCount() ?? 0;
863
+ const frozen = useMemo4(() => {
864
+ const noActionColumn = model?.features.dataEditable === false && model?.features.schemaEditable === false && model.rowActions.length === 0;
865
+ return {
866
+ frozenRowsStart: 1,
867
+ frozenRowsEnd: Math.max(1, draftRowCount),
868
+ frozenColsStart: model?.features.selection.enabled ? 1 : 0,
869
+ frozenColsEnd: noActionColumn ? 0 : 1
870
+ };
871
+ }, [
872
+ model,
873
+ draftRowCount
874
+ ]);
875
+ const getCells = useCallback4((range, plane) => presentation?.getCells(range, plane) ?? {}, [
876
+ presentation
877
+ ]);
878
+ useEffect3(() => {
879
+ if (!dxGrid || !presentation) {
880
+ return;
881
+ }
882
+ dxGrid.getCells = getCells;
883
+ }, [
884
+ dxGrid,
885
+ presentation,
886
+ getCells
887
+ ]);
888
+ useEffect3(() => {
889
+ if (!dxGrid) {
890
+ return;
891
+ }
892
+ dxGrid.updateCells(true);
893
+ dxGrid.requestUpdate();
894
+ }, [
895
+ dxGrid,
896
+ rows,
897
+ cellUpdateCounter
898
+ ]);
899
+ const handleInsertRowResult = useCallback4((insertResult) => {
900
+ if (insertResult === "draft") {
901
+ requestAnimationFrame(() => {
902
+ dxGrid?.setFocus({
903
+ plane: "frozenRowsEnd",
904
+ col: 0,
905
+ row: 0
796
906
  });
797
- } else {
907
+ dxGrid?.refocus();
908
+ });
909
+ } else {
910
+ requestAnimationFrame(() => {
911
+ dxGrid?.setFocus({
912
+ plane: "grid",
913
+ col: 0,
914
+ row: model ? model.getRowCount() - 1 : 0
915
+ });
916
+ dxGrid?.refocus();
917
+ });
918
+ }
919
+ }, [
920
+ model,
921
+ dxGrid
922
+ ]);
923
+ const handleSave = useCallback4(() => {
924
+ dxGrid?.updateCells(true);
925
+ dxGrid?.requestUpdate();
926
+ }, [
927
+ dxGrid
928
+ ]);
929
+ const handleSaveDraftRow = useCallback4((rowIndex = 0) => {
930
+ if (model && dxGrid) {
931
+ const didCommitSuccessfully = model.commitDraftRow(rowIndex);
932
+ if (didCommitSuccessfully) {
798
933
  requestAnimationFrame(() => {
799
- dxGrid?.setFocus({
800
- plane: "grid",
801
- col: 0,
802
- row: model ? model.getRowCount() - 1 : 0
803
- });
804
- dxGrid?.refocus();
934
+ dxGrid.scrollToEndRow();
805
935
  });
806
936
  }
807
- }, [
808
- model,
809
- dxGrid
810
- ]);
811
- const handleSave = useCallback4(() => {
812
- dxGrid?.updateCells(true);
813
- dxGrid?.requestUpdate();
814
- }, [
815
- dxGrid
816
- ]);
817
- const handleSaveDraftRow = useCallback4((rowIndex = 0) => {
818
- if (model && dxGrid) {
819
- const didCommitSuccessfully = model.commitDraftRow(rowIndex);
820
- if (didCommitSuccessfully) {
821
- requestAnimationFrame(() => {
822
- dxGrid.scrollToEndRow();
823
- });
937
+ }
938
+ }, [
939
+ model,
940
+ dxGrid
941
+ ]);
942
+ const handleGridClick = useCallback4((event) => {
943
+ const cell = closestCell(event.target);
944
+ if (cell) {
945
+ const { row: rowIndex, plane } = cell;
946
+ if (onRowClick) {
947
+ if (plane === "grid") {
948
+ const row = model?.getRowAt(rowIndex);
949
+ row && onRowClick(row);
950
+ } else {
951
+ onRowClick(cell);
824
952
  }
825
953
  }
826
- }, [
827
- model,
828
- dxGrid
829
- ]);
830
- const handleGridClick = useCallback4((event) => {
831
- const cell = closestCell(event.target);
832
- if (cell) {
833
- const { row: rowIndex, plane } = cell;
834
- if (onRowClick) {
835
- if (plane === "grid") {
836
- const row = model?.getRowAt(rowIndex);
837
- row && onRowClick(row);
838
- } else {
839
- onRowClick(cell);
840
- }
841
- }
842
- if (model?.features.selection.enabled && model?.selection.selectionMode === "single") {
843
- model.selection.toggleSelectionForRowIndex(rowIndex);
844
- }
954
+ if (model?.features.selection.enabled && model?.selection.selectionMode === "single") {
955
+ model.selection.toggleSelectionForRowIndex(rowIndex);
845
956
  }
846
- if (!modals) {
957
+ }
958
+ if (!modals) {
959
+ return;
960
+ }
961
+ const buttonSelector = Object.values(tableButtons).map((button) => `button[${button.attr}]`).join(",");
962
+ const tableButtonElement = event.target.closest(buttonSelector);
963
+ if (tableButtonElement) {
964
+ const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
965
+ if (!button) {
847
966
  return;
848
967
  }
849
- const buttonSelector = Object.values(tableButtons).map((button) => `button[${button.attr}]`).join(",");
850
- const tableButtonElement = event.target.closest(buttonSelector);
851
- if (tableButtonElement) {
852
- const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
853
- if (!button) {
854
- return;
968
+ modals.setTrigger(tableButtonElement);
969
+ const data = button.getData(tableButtonElement);
970
+ switch (data.type) {
971
+ case "rowMenu": {
972
+ modals.showRowMenu(data.rowIndex);
973
+ break;
855
974
  }
856
- modals.setTrigger(tableButtonElement);
857
- const data = button.getData(tableButtonElement);
858
- switch (data.type) {
859
- case "rowMenu": {
860
- modals.showRowMenu(data.rowIndex);
861
- break;
862
- }
863
- case "columnSettings": {
864
- modals.showColumnMenu(data.fieldId);
865
- break;
866
- }
867
- case "newColumn": {
868
- modals.showColumnCreator();
869
- break;
870
- }
871
- case "sort": {
872
- model?.sorting?.toggleSort(data.fieldId);
873
- break;
874
- }
875
- case "saveDraftRow": {
876
- handleSaveDraftRow();
877
- break;
878
- }
975
+ case "columnSettings": {
976
+ modals.showColumnMenu(data.fieldId);
977
+ break;
879
978
  }
880
- return;
881
- }
882
- const controlSelector = Object.values(tableControls).map((control) => `input[${control.attr}]`).join(",");
883
- const controlElement = event.target.closest(controlSelector);
884
- if (controlElement) {
885
- const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
886
- if (!control) {
887
- return;
979
+ case "newColumn": {
980
+ modals.showColumnCreator();
981
+ break;
888
982
  }
889
- const data = control.getData(controlElement);
890
- switch (data.type) {
891
- case "checkbox": {
892
- if (data.header) {
893
- model?.selection.setSelection(model.selection.allRowsSeleted.value ? "none" : "all");
894
- } else {
895
- model?.selection.toggleSelectionForRowIndex(data.rowIndex);
896
- }
897
- break;
898
- }
899
- case "switch": {
900
- if (model) {
901
- model.updateCellData({
902
- row: data.rowIndex,
903
- col: data.colIndex
904
- }, (value) => !value);
905
- }
906
- break;
907
- }
983
+ case "sort": {
984
+ model?.toggleSort(data.fieldId);
985
+ break;
908
986
  }
909
- }
910
- }, [
911
- model,
912
- modals,
913
- dxGrid
914
- ]);
915
- const handleFocus = useCallback4((increment, delta, cell) => {
916
- if (dxGrid && model) {
917
- if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1 && increment !== "col") {
918
- handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : "final");
919
- } else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
920
- handleSaveDraftRow(cell.row);
921
- handleInsertRowResult(model.insertRow());
922
- } else {
923
- dxGrid.refocus(increment, delta);
987
+ case "saveDraftRow": {
988
+ handleSaveDraftRow();
989
+ break;
924
990
  }
925
991
  }
926
- }, [
927
- dxGrid,
928
- model
929
- ]);
930
- const handleKeyDown = useCallback4((event) => {
931
- if (!model?.features.dataEditable) {
932
- return;
933
- }
934
- const cell = closestCell(event.target);
935
- if (!model || !cell) {
992
+ return;
993
+ }
994
+ const controlSelector = Object.values(tableControls).map((control) => `input[${control.attr}]`).join(",");
995
+ const controlElement = event.target.closest(controlSelector);
996
+ if (controlElement) {
997
+ const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
998
+ if (!control) {
936
999
  return;
937
1000
  }
938
- if (event.metaKey || event.ctrlKey) {
939
- switch (event.key) {
940
- case "c": {
941
- try {
942
- const cellData = model.getCellData(cell);
943
- const textContent = cellData?.toString() ?? "";
944
- void navigator.clipboard.writeText(textContent);
945
- event.preventDefault();
946
- } catch (error) {
947
- console.warn("Failed to copy cell content:", error);
948
- }
949
- break;
1001
+ const data = control.getData(controlElement);
1002
+ switch (data.type) {
1003
+ case "checkbox": {
1004
+ if (data.header) {
1005
+ model?.selection.setSelection(model.selection.allRowsSelected ? "none" : "all");
1006
+ } else {
1007
+ model?.selection.toggleSelectionForRowIndex(data.rowIndex);
950
1008
  }
951
- case "v": {
952
- event.preventDefault();
953
- void navigator.clipboard.readText().then((clipboardText) => {
954
- try {
955
- model.setCellData(cell, String2.trim(clipboardText).replace(/[\n\r]+/, " "));
956
- handleSave();
957
- } catch {
958
- const cellElement = event.target.closest('[data-dx-grid-action="cell"]');
959
- if (cellElement) {
960
- const rect = cellElement.getBoundingClientRect();
961
- const editRequest = new DxEditRequest({
962
- cellIndex: `${cell.plane},${cell.col},${cell.row}`,
963
- cellBox: {
964
- insetInlineStart: rect.left,
965
- insetBlockStart: rect.top,
966
- inlineSize: rect.width,
967
- blockSize: rect.height
968
- },
969
- cellElement,
970
- initialContent: clipboardText
971
- });
972
- cellElement.dispatchEvent(editRequest);
973
- }
974
- }
975
- });
976
- break;
1009
+ break;
1010
+ }
1011
+ case "switch": {
1012
+ if (model) {
1013
+ model.updateCellData({
1014
+ row: data.rowIndex,
1015
+ col: data.colIndex
1016
+ }, (value) => !value);
977
1017
  }
1018
+ break;
978
1019
  }
979
1020
  }
1021
+ }
1022
+ }, [
1023
+ model,
1024
+ modals,
1025
+ dxGrid
1026
+ ]);
1027
+ const handleFocus = useCallback4((increment, delta, cell) => {
1028
+ if (dxGrid && model) {
1029
+ if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1 && increment !== "col") {
1030
+ handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : "final");
1031
+ } else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
1032
+ handleSaveDraftRow(cell.row);
1033
+ handleInsertRowResult(model.insertRow());
1034
+ } else {
1035
+ dxGrid.refocus(increment, delta);
1036
+ }
1037
+ }
1038
+ }, [
1039
+ dxGrid,
1040
+ model
1041
+ ]);
1042
+ const handleKeyDown = useCallback4((event) => {
1043
+ if (!model?.features.dataEditable) {
1044
+ return;
1045
+ }
1046
+ const cell = closestCell(event.target);
1047
+ if (!model || !cell) {
1048
+ return;
1049
+ }
1050
+ if (event.metaKey || event.ctrlKey) {
980
1051
  switch (event.key) {
981
- case "Backspace":
982
- case "Delete": {
1052
+ case "c": {
983
1053
  try {
984
- model.setCellData(cell, void 0);
1054
+ const cellData = model.getCellData(cell);
1055
+ const textContent = cellData?.toString() ?? "";
1056
+ void navigator.clipboard.writeText(textContent);
985
1057
  event.preventDefault();
986
- handleSave();
987
- } catch {
1058
+ } catch (error) {
1059
+ log.catch(error, void 0, {
1060
+ F: __dxlog_file3,
1061
+ L: 322,
1062
+ S: void 0,
1063
+ C: (f, a) => f(...a)
1064
+ });
988
1065
  }
989
1066
  break;
990
1067
  }
1068
+ case "v": {
1069
+ event.preventDefault();
1070
+ void navigator.clipboard.readText().then((clipboardText) => {
1071
+ try {
1072
+ model.setCellData(cell, String2.trim(clipboardText).replace(/[\n\r]+/, " "));
1073
+ handleSave();
1074
+ } catch {
1075
+ const cellElement = event.target.closest('[data-dx-grid-action="cell"]');
1076
+ if (cellElement) {
1077
+ const rect = cellElement.getBoundingClientRect();
1078
+ const editRequest = new DxEditRequest({
1079
+ cellIndex: `${cell.plane},${cell.col},${cell.row}`,
1080
+ cellBox: {
1081
+ insetInlineStart: rect.left,
1082
+ insetBlockStart: rect.top,
1083
+ inlineSize: rect.width,
1084
+ blockSize: rect.height
1085
+ },
1086
+ cellElement,
1087
+ initialContent: clipboardText
1088
+ });
1089
+ cellElement.dispatchEvent(editRequest);
1090
+ }
1091
+ }
1092
+ });
1093
+ break;
1094
+ }
991
1095
  }
992
- }, [
993
- model,
994
- dxGrid,
995
- handleSave
996
- ]);
997
- const handleAxisResize = useCallback4((event) => {
998
- if (event.axis === "col") {
999
- const columnIndex = parseInt(event.index, 10);
1000
- model?.setColumnWidth(columnIndex, event.size);
1001
- }
1002
- }, [
1003
- model
1004
- ]);
1005
- const handleWheel = useCallback4((event) => {
1006
- if (!ignoreAttention && !hasAttention) {
1007
- event.stopPropagation();
1008
- }
1009
- }, [
1010
- hasAttention,
1011
- ignoreAttention
1012
- ]);
1013
- const handleNewColumn = useCallback4(() => {
1014
- const columns = model?.getColumnCount();
1015
- if (dxGrid && columns) {
1016
- dxGrid.scrollToColumn(columns - 1);
1017
- }
1018
- }, [
1019
- model,
1020
- dxGrid
1021
- ]);
1022
- useImperativeHandle(forwardedRef, () => {
1023
- if (!dxGrid || !presentation) {
1024
- return {};
1096
+ }
1097
+ switch (event.key) {
1098
+ case "Backspace":
1099
+ case "Delete": {
1100
+ try {
1101
+ model.setCellData(cell, void 0);
1102
+ event.preventDefault();
1103
+ handleSave();
1104
+ } catch {
1105
+ }
1106
+ break;
1025
1107
  }
1026
- return {
1027
- update: (cell) => {
1028
- if (cell) {
1029
- dxGrid.updateIfWithinBounds(cell, true);
1030
- } else {
1031
- dxGrid.updateCells(true);
1032
- dxGrid.requestUpdate();
1033
- }
1034
- },
1035
- handleInsertRowResult
1036
- };
1037
- }, [
1038
- dxGrid,
1039
- model,
1040
- presentation
1041
- ]);
1042
- if (!model || !modals) {
1043
- return /* @__PURE__ */ React7.createElement("span", {
1044
- role: "none",
1045
- className: "attention-surface"
1046
- });
1047
1108
  }
1048
- return /* @__PURE__ */ React7.createElement(Grid.Root, {
1049
- id: model.id ?? "table-grid"
1050
- }, /* @__PURE__ */ React7.createElement(TableValueEditor, {
1051
- model,
1052
- modals,
1053
- schema,
1054
- onFocus: handleFocus,
1055
- onSave: handleSave,
1056
- onCreate
1057
- }), /* @__PURE__ */ React7.createElement(Grid.Content, {
1058
- className: mx("[--dx-grid-base:var(--baseSurface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
1059
- frozen,
1060
- columns: model.columnMeta.value,
1061
- columnDefault,
1062
- rowDefault,
1063
- limitRows: model.getRowCount() ?? 0,
1064
- limitColumns: model.projection.fields.length,
1065
- overscroll: "trap",
1066
- onAxisResize: handleAxisResize,
1067
- onClick: handleGridClick,
1068
- onKeyDownCapture: handleKeyDown,
1069
- onWheelCapture: handleWheel,
1070
- ...testId && {
1071
- "data-testid": testId
1109
+ }, [
1110
+ model,
1111
+ dxGrid,
1112
+ handleSave
1113
+ ]);
1114
+ const handleAxisResize = useCallback4((event) => {
1115
+ if (event.axis === "col") {
1116
+ const columnIndex = parseInt(event.index, 10);
1117
+ model?.setColumnWidth(columnIndex, event.size);
1118
+ }
1119
+ }, [
1120
+ model
1121
+ ]);
1122
+ const handleWheel = useCallback4((event) => {
1123
+ if (!ignoreAttention && !hasAttention) {
1124
+ event.stopPropagation();
1125
+ }
1126
+ }, [
1127
+ hasAttention,
1128
+ ignoreAttention
1129
+ ]);
1130
+ const handleNewColumn = useCallback4(() => {
1131
+ const columns = model?.getColumnCount();
1132
+ if (dxGrid && columns) {
1133
+ dxGrid.scrollToColumn(columns - 1);
1134
+ }
1135
+ }, [
1136
+ model,
1137
+ dxGrid
1138
+ ]);
1139
+ useImperativeHandle(forwardedRef, () => {
1140
+ if (!dxGrid || !presentation) {
1141
+ return {};
1142
+ }
1143
+ return {
1144
+ update: (cell) => {
1145
+ if (cell) {
1146
+ dxGrid.updateIfWithinBounds(cell, true);
1147
+ } else {
1148
+ dxGrid.updateCells(true);
1149
+ dxGrid.requestUpdate();
1150
+ }
1072
1151
  },
1073
- ref: setDxGrid
1074
- }), /* @__PURE__ */ React7.createElement(RowActionsMenu, {
1075
- model,
1076
- modals
1077
- }), /* @__PURE__ */ React7.createElement(ColumnActionsMenu, {
1078
- model,
1079
- modals
1080
- }), /* @__PURE__ */ React7.createElement(ColumnSettings, {
1081
- model,
1082
- modals,
1083
- onNewColumn: handleNewColumn
1084
- }));
1085
- } finally {
1086
- _effect.f();
1152
+ handleInsertRowResult
1153
+ };
1154
+ }, [
1155
+ dxGrid,
1156
+ model,
1157
+ presentation
1158
+ ]);
1159
+ if (!model || !modals) {
1160
+ return /* @__PURE__ */ React7.createElement("span", {
1161
+ role: "none",
1162
+ className: "attention-surface"
1163
+ });
1087
1164
  }
1165
+ return /* @__PURE__ */ React7.createElement(Grid.Root, {
1166
+ id: model.id ?? "table-grid"
1167
+ }, /* @__PURE__ */ React7.createElement(TableValueEditor, {
1168
+ model,
1169
+ modals,
1170
+ schema,
1171
+ onFocus: handleFocus,
1172
+ onSave: handleSave,
1173
+ onCreate
1174
+ }), /* @__PURE__ */ React7.createElement(Grid.Content, {
1175
+ className: mx("[--dx-grid-base:var(--baseSurface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
1176
+ frozen,
1177
+ columns: columnMeta,
1178
+ columnDefault,
1179
+ rowDefault,
1180
+ limitRows: rows.length,
1181
+ limitColumns: columnCount,
1182
+ overscroll: "trap",
1183
+ onAxisResize: handleAxisResize,
1184
+ onClick: handleGridClick,
1185
+ onKeyDownCapture: handleKeyDown,
1186
+ onWheelCapture: handleWheel,
1187
+ ...testId && {
1188
+ "data-testid": testId
1189
+ },
1190
+ ref: setDxGrid
1191
+ }), /* @__PURE__ */ React7.createElement(RowActionsMenu, {
1192
+ model,
1193
+ modals
1194
+ }), /* @__PURE__ */ React7.createElement(ColumnActionsMenu, {
1195
+ model,
1196
+ modals
1197
+ }), /* @__PURE__ */ React7.createElement(ColumnSettings, {
1198
+ model,
1199
+ modals,
1200
+ onNewColumn: handleNewColumn
1201
+ }));
1088
1202
  };
1089
1203
  var TableMain = /* @__PURE__ */ forwardRef(TableMainInner);
1090
1204
  var Table = {
@@ -1093,98 +1207,86 @@ var Table = {
1093
1207
  };
1094
1208
 
1095
1209
  // src/components/Table/DynamicTable.tsx
1096
- import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
1097
- import React8, { useCallback as useCallback5, useMemo as useMemo5, useRef as useRef3 } from "react";
1210
+ import { RegistryContext as RegistryContext2 } from "@effect-atom/atom-react";
1211
+ import React8, { useCallback as useCallback5, useContext as useContext2, useEffect as useEffect4, useMemo as useMemo5, useRef as useRef3, useState as useState5 } from "react";
1098
1212
  import { useDefaultValue } from "@dxos/react-ui";
1099
- import { mx as mx2 } from "@dxos/react-ui-theme";
1100
- import { ProjectionModel } from "@dxos/schema";
1213
+ import { mx as mx2 } from "@dxos/ui-theme";
1101
1214
  var DynamicTable = ({ classNames, schema, name = "example.com/dynamic-table", rows, properties, jsonSchema: _jsonSchema, rowActions, onRowClick, onRowAction, ...props }) => {
1102
- var _effect = _useSignals8();
1103
- try {
1104
- const { jsonSchema } = useMemo5(() => getBaseSchema({
1105
- typename: name,
1106
- properties,
1107
- jsonSchema: _jsonSchema,
1108
- schema
1109
- }), [
1110
- name,
1111
- properties,
1112
- _jsonSchema,
1113
- schema
1114
- ]);
1115
- const { object, projection } = useMemo5(() => makeDynamicTable({
1116
- jsonSchema,
1117
- properties
1118
- }), [
1215
+ const registry = useContext2(RegistryContext2);
1216
+ const [dynamicTable, setDynamicTable] = useState5();
1217
+ const { jsonSchema } = useMemo5(() => getBaseSchema({
1218
+ typename: name,
1219
+ properties,
1220
+ jsonSchema: _jsonSchema,
1221
+ schema
1222
+ }), [
1223
+ name,
1224
+ properties,
1225
+ _jsonSchema,
1226
+ schema
1227
+ ]);
1228
+ useEffect4(() => {
1229
+ setDynamicTable(makeDynamicTable({
1230
+ registry,
1119
1231
  jsonSchema,
1120
1232
  properties
1121
- ]);
1122
- const tableRef = useRef3(null);
1123
- const handleCellUpdate = useCallback5((cell) => {
1124
- tableRef.current?.update?.(cell);
1125
- }, []);
1126
- const handleRowOrderChange = useCallback5(() => {
1127
- tableRef.current?.update?.();
1128
- }, []);
1129
- const features = useDefaultValue(props.features, () => ({
1130
- selection: {
1131
- enabled: false
1132
- },
1133
- dataEditable: false
1134
1233
  }));
1135
- const projectionModel = useMemo5(() => {
1136
- if (jsonSchema && projection) {
1137
- const model2 = new ProjectionModel(jsonSchema, projection);
1138
- model2.normalizeView();
1139
- return model2;
1140
- }
1141
- }, [
1142
- jsonSchema,
1143
- projection
1144
- ]);
1145
- const model = useTableModel({
1146
- rows,
1147
- object,
1148
- projection: projectionModel,
1149
- features,
1150
- rowActions,
1151
- onCellUpdate: handleCellUpdate,
1152
- onRowOrderChange: handleRowOrderChange,
1153
- onRowAction
1154
- });
1155
- const presentation = useMemo5(() => {
1156
- if (model) {
1157
- return new TablePresentation(model);
1158
- }
1159
- }, [
1160
- model
1161
- ]);
1162
- return /* @__PURE__ */ React8.createElement("div", {
1163
- role: "none",
1164
- className: mx2("is-full bs-full grow grid", classNames)
1165
- }, /* @__PURE__ */ React8.createElement("div", {
1166
- role: "none",
1167
- className: "grid min-bs-0 overflow-hidden"
1168
- }, /* @__PURE__ */ React8.createElement(Table.Root, null, /* @__PURE__ */ React8.createElement(Table.Main, {
1169
- ref: tableRef,
1170
- model,
1171
- presentation,
1172
- ignoreAttention: true,
1173
- onRowClick
1174
- }))));
1175
- } finally {
1176
- _effect.f();
1177
- }
1234
+ }, [
1235
+ registry,
1236
+ jsonSchema,
1237
+ properties
1238
+ ]);
1239
+ const tableRef = useRef3(null);
1240
+ const handleCellUpdate = useCallback5((cell) => {
1241
+ tableRef.current?.update?.(cell);
1242
+ }, []);
1243
+ const handleRowOrderChange = useCallback5(() => {
1244
+ tableRef.current?.update?.();
1245
+ }, []);
1246
+ const features = useDefaultValue(props.features, () => ({
1247
+ selection: {
1248
+ enabled: false
1249
+ },
1250
+ dataEditable: false
1251
+ }));
1252
+ const model = useTableModel({
1253
+ rows,
1254
+ object: dynamicTable?.object,
1255
+ projection: dynamicTable?.projection,
1256
+ features,
1257
+ rowActions,
1258
+ onCellUpdate: handleCellUpdate,
1259
+ onRowOrderChange: handleRowOrderChange,
1260
+ onRowAction
1261
+ });
1262
+ const presentation = useMemo5(() => {
1263
+ if (model) {
1264
+ return new TablePresentation(registry, model);
1265
+ }
1266
+ }, [
1267
+ registry,
1268
+ model
1269
+ ]);
1270
+ return /* @__PURE__ */ React8.createElement("div", {
1271
+ role: "none",
1272
+ className: mx2("is-full bs-full grow grid", classNames)
1273
+ }, /* @__PURE__ */ React8.createElement("div", {
1274
+ role: "none",
1275
+ className: "grid min-bs-0 overflow-hidden"
1276
+ }, /* @__PURE__ */ React8.createElement(Table.Root, null, /* @__PURE__ */ React8.createElement(Table.Main, {
1277
+ ref: tableRef,
1278
+ model,
1279
+ presentation,
1280
+ ignoreAttention: true,
1281
+ onRowClick
1282
+ }))));
1178
1283
  };
1179
1284
 
1180
1285
  // src/components/TableToolbar/TableToolbar.tsx
1181
- import { useSignals as _useSignals9 } from "@preact-signals/safe-react/tracking";
1182
- import { Atom } from "@effect-atom/atom-react";
1183
- import React9, { useMemo as useMemo6 } from "react";
1184
- import { live } from "@dxos/live-object";
1286
+ import { Atom as Atom3, RegistryContext as RegistryContext3 } from "@effect-atom/atom-react";
1287
+ import React9, { useContext as useContext3, useEffect as useEffect5, useMemo as useMemo6 } from "react";
1185
1288
  import { MenuProvider, ToolbarMenu, createGapSeparator, createMenuAction, useMenuActions } from "@dxos/react-ui-menu";
1186
- import { atomFromSignal } from "@dxos/react-ui-menu";
1187
- var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Atom.make((get) => {
1289
+ var createTableToolbarActions = ({ stateAtom, onAdd, onSave, customActions }) => Atom3.make((get) => {
1188
1290
  const nodes = [];
1189
1291
  if (onAdd) {
1190
1292
  const add = createMenuAction("add-row", onAdd, {
@@ -1201,6 +1303,7 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Ato
1201
1303
  nodes.push(add);
1202
1304
  }
1203
1305
  if (onSave) {
1306
+ const state = get(stateAtom);
1204
1307
  const save = createMenuAction("save-view", onSave, {
1205
1308
  type: "save-view",
1206
1309
  icon: "ph--floppy-disk--regular",
@@ -1212,7 +1315,7 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Ato
1212
1315
  ],
1213
1316
  testId: "table.toolbar.save-view",
1214
1317
  iconOnly: false,
1215
- hidden: get(atomFromSignal(() => !state.viewDirty))
1318
+ hidden: !state.viewDirty
1216
1319
  });
1217
1320
  nodes.push(save);
1218
1321
  }
@@ -1233,32 +1336,37 @@ var createTableToolbarActions = ({ state, onAdd, onSave, customActions }) => Ato
1233
1336
  };
1234
1337
  });
1235
1338
  var TableToolbar = ({ classNames, viewDirty, attendableId, onAdd, onSave, customActions }) => {
1236
- var _effect = _useSignals9();
1237
- try {
1238
- const state = useMemo6(() => live({
1339
+ const registry = useContext3(RegistryContext3);
1340
+ const stateAtom = useMemo6(() => Atom3.make({
1341
+ viewDirty
1342
+ }), []);
1343
+ useEffect5(() => {
1344
+ registry.set(stateAtom, {
1239
1345
  viewDirty
1240
- }), []);
1241
- const actionsCreator = useMemo6(() => createTableToolbarActions({
1242
- state,
1243
- onAdd,
1244
- onSave,
1245
- customActions
1246
- }), [
1247
- state,
1248
- onAdd,
1249
- onSave,
1250
- customActions
1251
- ]);
1252
- const menu = useMenuActions(actionsCreator);
1253
- return /* @__PURE__ */ React9.createElement(MenuProvider, {
1254
- ...menu,
1255
- attendableId
1256
- }, /* @__PURE__ */ React9.createElement(ToolbarMenu, {
1257
- classNames
1258
- }));
1259
- } finally {
1260
- _effect.f();
1261
- }
1346
+ });
1347
+ }, [
1348
+ registry,
1349
+ stateAtom,
1350
+ viewDirty
1351
+ ]);
1352
+ const actionsCreator = useMemo6(() => createTableToolbarActions({
1353
+ stateAtom,
1354
+ onAdd,
1355
+ onSave,
1356
+ customActions
1357
+ }), [
1358
+ stateAtom,
1359
+ onAdd,
1360
+ onSave,
1361
+ customActions
1362
+ ]);
1363
+ const menu = useMenuActions(actionsCreator);
1364
+ return /* @__PURE__ */ React9.createElement(MenuProvider, {
1365
+ ...menu,
1366
+ attendableId
1367
+ }, /* @__PURE__ */ React9.createElement(ToolbarMenu, {
1368
+ classNames
1369
+ }));
1262
1370
  };
1263
1371
  export {
1264
1372
  BUTTON_IDENTIFIERS,
@@ -1272,7 +1380,12 @@ export {
1272
1380
  TablePresentation,
1273
1381
  TableToolbar,
1274
1382
  cellClassesForRowSelection,
1383
+ compareValues,
1384
+ createDirectChangeCallback,
1385
+ createEchoChangeCallback,
1386
+ extractOrder,
1275
1387
  getBaseSchema,
1388
+ getSortValue,
1276
1389
  makeDynamicTable,
1277
1390
  narrowSchema,
1278
1391
  tableButtons,