@dxos/react-ui-table 0.8.4-main.f5c0578 → 0.8.4-main.fcfe5033a5

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