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