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