@dxos/react-ui-table 0.7.4 → 0.7.5-labs.401163d

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 (120) hide show
  1. package/dist/lib/browser/{chunk-IYCSJDMB.mjs → chunk-3FR6NHPD.mjs} +7 -5
  2. package/dist/lib/browser/chunk-3FR6NHPD.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1488 -1112
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +1 -1
  7. package/dist/lib/node/{chunk-MHBMVCFZ.cjs → chunk-NB3SFHGA.cjs} +9 -7
  8. package/dist/lib/node/chunk-NB3SFHGA.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +1493 -1097
  10. package/dist/lib/node/index.cjs.map +4 -4
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/types/index.cjs +3 -3
  13. package/dist/lib/node/types/index.cjs.map +1 -1
  14. package/dist/lib/node-esm/{chunk-BMWGPXPL.mjs → chunk-RLVVOZW5.mjs} +7 -5
  15. package/dist/lib/node-esm/chunk-RLVVOZW5.mjs.map +7 -0
  16. package/dist/lib/node-esm/index.mjs +1488 -1112
  17. package/dist/lib/node-esm/index.mjs.map +4 -4
  18. package/dist/lib/node-esm/meta.json +1 -1
  19. package/dist/lib/node-esm/types/index.mjs +1 -1
  20. package/dist/types/src/_deprecated/hooks/useColumnSorting.d.ts +1 -1
  21. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +4 -3
  22. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/ColumnSettings.d.ts +3 -2
  24. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/CreateRefPanel.d.ts +3 -2
  26. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/RefPanel.d.ts +4 -3
  28. package/dist/types/src/components/Table/RefPanel.d.ts.map +1 -1
  29. package/dist/types/src/components/Table/RowActionsMenu.d.ts +3 -2
  30. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  31. package/dist/types/src/components/Table/Table.d.ts +2 -1
  32. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  33. package/dist/types/src/components/Table/Table.stories.d.ts +1 -4
  34. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +3 -2
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  37. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +18 -0
  39. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -0
  40. package/dist/types/src/components/TableToolbar/index.d.ts +2 -0
  41. package/dist/types/src/components/TableToolbar/index.d.ts.map +1 -0
  42. package/dist/types/src/components/index.d.ts +1 -1
  43. package/dist/types/src/components/index.d.ts.map +1 -1
  44. package/dist/types/src/components/testing.d.ts +31 -71
  45. package/dist/types/src/components/testing.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  47. package/dist/types/src/index.d.ts +1 -0
  48. package/dist/types/src/index.d.ts.map +1 -1
  49. package/dist/types/src/model/index.d.ts +1 -0
  50. package/dist/types/src/model/index.d.ts.map +1 -1
  51. package/dist/types/src/model/modal-controller.d.ts +6 -2
  52. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  53. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  54. package/dist/types/src/model/table-model.d.ts +19 -31
  55. package/dist/types/src/model/table-model.d.ts.map +1 -1
  56. package/dist/types/src/model/table-presentation.d.ts +22 -0
  57. package/dist/types/src/model/table-presentation.d.ts.map +1 -0
  58. package/dist/types/src/model/table-presentation.test.d.ts +2 -0
  59. package/dist/types/src/model/table-presentation.test.d.ts.map +1 -0
  60. package/dist/types/src/model/table-sorting.d.ts +53 -0
  61. package/dist/types/src/model/table-sorting.d.ts.map +1 -0
  62. package/dist/types/src/playwright/TableManager.d.ts +2 -1
  63. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  64. package/dist/types/src/translations.d.ts +2 -1
  65. package/dist/types/src/translations.d.ts.map +1 -1
  66. package/dist/types/src/types/table.d.ts +46 -154
  67. package/dist/types/src/types/table.d.ts.map +1 -1
  68. package/dist/types/src/util/index.d.ts +1 -0
  69. package/dist/types/src/util/index.d.ts.map +1 -1
  70. package/dist/types/src/util/table-buttons.d.ts +63 -19
  71. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  72. package/dist/types/src/util/table-controls.d.ts +32 -9
  73. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  74. package/dist/types/src/util/table.d.ts +3 -2
  75. package/dist/types/src/util/table.d.ts.map +1 -1
  76. package/dist/types/tsconfig.tsbuildinfo +1 -0
  77. package/package.json +35 -34
  78. package/src/_deprecated/schema/Schema.stories.tsx +1 -1
  79. package/src/components/Table/ColumnActionsMenu.tsx +12 -12
  80. package/src/components/Table/ColumnSettings.tsx +11 -11
  81. package/src/components/Table/CreateRefPanel.tsx +13 -14
  82. package/src/components/Table/RefPanel.tsx +9 -9
  83. package/src/components/Table/RowActionsMenu.tsx +6 -7
  84. package/src/components/Table/Table.stories.tsx +84 -56
  85. package/src/components/Table/Table.tsx +239 -148
  86. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +13 -12
  87. package/src/components/TableCellEditor/TableCellEditor.tsx +6 -4
  88. package/src/components/TableToolbar/TableToolbar.tsx +72 -0
  89. package/src/components/{Toolbar → TableToolbar}/index.ts +1 -1
  90. package/src/components/index.ts +1 -1
  91. package/src/components/testing.ts +10 -8
  92. package/src/hooks/useTableModel.ts +19 -0
  93. package/src/index.ts +2 -0
  94. package/src/model/index.ts +1 -0
  95. package/src/model/modal-controller.ts +15 -48
  96. package/src/model/selection-model.ts +12 -1
  97. package/src/model/table-model.test.ts +4 -76
  98. package/src/model/table-model.ts +73 -304
  99. package/src/model/table-presentation.test.ts +102 -0
  100. package/src/model/table-presentation.ts +235 -0
  101. package/src/model/table-sorting.ts +215 -0
  102. package/src/playwright/TableManager.ts +5 -1
  103. package/src/playwright/smoke.spec.ts +94 -9
  104. package/src/translations.ts +2 -1
  105. package/src/types/table.ts +4 -4
  106. package/src/util/index.ts +1 -0
  107. package/src/util/table-buttons.ts +127 -48
  108. package/src/util/table-controls.ts +71 -7
  109. package/src/util/table.ts +93 -52
  110. package/dist/lib/browser/chunk-IYCSJDMB.mjs.map +0 -7
  111. package/dist/lib/node/chunk-MHBMVCFZ.cjs.map +0 -7
  112. package/dist/lib/node-esm/chunk-BMWGPXPL.mjs.map +0 -7
  113. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -21
  114. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  115. package/dist/types/src/components/Toolbar/common.d.ts +0 -14
  116. package/dist/types/src/components/Toolbar/common.d.ts.map +0 -1
  117. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  118. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  119. package/src/components/Toolbar/Toolbar.tsx +0 -87
  120. package/src/components/Toolbar/common.tsx +0 -71
@@ -28,19 +28,26 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var node_exports = {};
30
30
  __export(node_exports, {
31
+ BUTTON_IDENTIFIERS: () => BUTTON_IDENTIFIERS,
32
+ CONTROL_IDENTIFIERS: () => CONTROL_IDENTIFIERS,
33
+ ModalController: () => ModalController,
31
34
  Table: () => Table,
32
- TableSchema: () => import_chunk_MHBMVCFZ.TableSchema,
33
- TableType: () => import_chunk_MHBMVCFZ.TableType,
34
- Toolbar: () => Toolbar,
35
+ TableModel: () => TableModel,
36
+ TablePresentation: () => TablePresentation,
37
+ TableSchema: () => import_chunk_NB3SFHGA.TableSchema,
38
+ TableToolbar: () => TableToolbar,
39
+ TableType: () => import_chunk_NB3SFHGA.TableType,
35
40
  initializeTable: () => initializeTable,
41
+ renderCheckbox: () => renderCheckbox,
42
+ renderSwitch: () => renderSwitch,
36
43
  tableButtons: () => tableButtons,
44
+ tableControls: () => tableControls,
37
45
  touch: () => touch,
38
46
  translations: () => translations_default,
39
- useTableModel: () => useTableModel,
40
- useToolbarContext: () => useToolbarContext
47
+ useTableModel: () => useTableModel
41
48
  });
42
49
  module.exports = __toCommonJS(node_exports);
43
- var import_chunk_MHBMVCFZ = require("./chunk-MHBMVCFZ.cjs");
50
+ var import_chunk_NB3SFHGA = require("./chunk-NB3SFHGA.cjs");
44
51
  var import_react = __toESM(require("react"));
45
52
  var import_echo_schema = require("@dxos/echo-schema");
46
53
  var import_invariant = require("@dxos/invariant");
@@ -68,37 +75,43 @@ var import_react_ui5 = require("@dxos/react-ui");
68
75
  var import_react_ui_form3 = require("@dxos/react-ui-form");
69
76
  var import_react6 = __toESM(require("react"));
70
77
  var import_react_ui6 = require("@dxos/react-ui");
71
- var import_react7 = __toESM(require("react"));
72
- var import_echo_schema3 = require("@dxos/echo-schema");
73
- var import_invariant2 = require("@dxos/invariant");
74
- var import_react_ui_grid3 = require("@dxos/react-ui-grid");
75
- var import_autocomplete = require("@codemirror/autocomplete");
76
- var import_view = require("@codemirror/view");
77
- var import_react_context = require("@radix-ui/react-context");
78
- var import_react8 = __toESM(require("react"));
79
- var import_react_ui7 = require("@dxos/react-ui");
80
- var import_react9 = __toESM(require("react"));
81
- var import_react_ui8 = require("@dxos/react-ui");
82
- var import_react10 = require("react");
83
78
  var import_signals_core = require("@preact/signals-core");
84
- var import_lodash = __toESM(require("lodash.orderby"));
85
79
  var import_context = require("@dxos/context");
86
- var import_echo_schema4 = require("@dxos/echo-schema");
87
- var import_invariant3 = require("@dxos/invariant");
80
+ var import_echo_schema3 = require("@dxos/echo-schema");
81
+ var import_invariant2 = require("@dxos/invariant");
82
+ var import_live_object = require("@dxos/live-object");
88
83
  var import_react_client = require("@dxos/react-client");
89
84
  var import_react_ui_form4 = require("@dxos/react-ui-form");
90
- var import_react_ui_grid4 = require("@dxos/react-ui-grid");
91
- var import_react_ui_theme2 = require("@dxos/react-ui-theme");
92
- var import_schema = require("@dxos/schema");
93
85
  var import_signals_core2 = require("@preact/signals-core");
94
- var import_log = require("@dxos/log");
95
- var import_echo_schema5 = require("@dxos/echo-schema");
96
- var import_log2 = require("@dxos/log");
86
+ var import_context2 = require("@dxos/context");
87
+ var import_echo_schema4 = require("@dxos/echo-schema");
97
88
  var import_react_client2 = require("@dxos/react-client");
98
89
  var import_echo5 = require("@dxos/react-client/echo");
99
- var import_schema2 = require("@dxos/schema");
90
+ var import_schema = require("@dxos/schema");
100
91
  var import_signals_core3 = require("@preact/signals-core");
101
- var import_context2 = require("@dxos/context");
92
+ var import_lodash = __toESM(require("lodash.orderby"));
93
+ var import_echo_schema5 = require("@dxos/echo-schema");
94
+ var import_react_ui_form5 = require("@dxos/react-ui-form");
95
+ var import_signals_core4 = require("@preact/signals-core");
96
+ var import_log = require("@dxos/log");
97
+ var import_signals_core5 = require("@preact/signals-core");
98
+ var import_echo_schema6 = require("@dxos/echo-schema");
99
+ var import_react_ui_form6 = require("@dxos/react-ui-form");
100
+ var import_react_ui_grid3 = require("@dxos/react-ui-grid");
101
+ var import_react_ui_theme2 = require("@dxos/react-ui-theme");
102
+ var import_schema2 = require("@dxos/schema");
103
+ var import_react7 = __toESM(require("react"));
104
+ var import_echo_schema7 = require("@dxos/echo-schema");
105
+ var import_invariant3 = require("@dxos/invariant");
106
+ var import_react_ui_grid4 = require("@dxos/react-ui-grid");
107
+ var import_autocomplete = require("@codemirror/autocomplete");
108
+ var import_view = require("@codemirror/view");
109
+ var import_react8 = __toESM(require("react"));
110
+ var import_live_object2 = require("@dxos/live-object");
111
+ var import_react_ui_menu = require("@dxos/react-ui-menu");
112
+ var import_signals_core6 = require("@preact/signals-core");
113
+ var import_react9 = require("react");
114
+ var import_react_ui_attention2 = require("@dxos/react-ui-attention");
102
115
  var translationKey = "table";
103
116
  var translations_default = [
104
117
  {
@@ -112,7 +125,7 @@ var translations_default = [
112
125
  "new schema": "New schema",
113
126
  "continue label": "Continue",
114
127
  "create stack section label": "Create table",
115
- "add row": "Add row",
128
+ "add row label": "Add row",
116
129
  "create comment": "Create thread",
117
130
  "column action sort descending": "Sort Descending",
118
131
  "column action sort ascending": "Sort Ascending",
@@ -123,45 +136,46 @@ var translations_default = [
123
136
  "bulk delete row label": "Delete selected rows",
124
137
  "column deleted label": "Column deleted",
125
138
  "new column button label": "Create column",
126
- "create new object label": "Create new object \u201C{{text}}\u201D"
139
+ "create new object label": "Create new object \u201C{{text}}\u201D",
140
+ "save view label": "Save changes to view"
127
141
  }
128
142
  }
129
143
  }
130
144
  ];
131
- var ColumnActionsMenu = ({ model }) => {
145
+ var ColumnActionsMenu = ({ model, modals }) => {
132
146
  const { t } = (0, import_react_ui2.useTranslation)(translationKey);
133
- const state = model?.modalController.state.value;
134
- if (!model || state?.type !== "column") {
147
+ const state = modals.state.value;
148
+ if (state?.type !== "column") {
135
149
  return null;
136
150
  }
137
- const currentSort = model.sorting;
151
+ const currentSort = model.sorting?.sorting;
138
152
  const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
139
153
  return /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Root, {
140
154
  modal: false,
141
- open: state?.type === "column",
142
- onOpenChange: model.modalController.close
155
+ open: true,
156
+ onOpenChange: modals.close
143
157
  }, /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.VirtualTrigger, {
144
- virtualRef: model.modalController.trigger
158
+ virtualRef: modals.trigger
145
159
  }), /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Portal, null, /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Content, null, /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Viewport, null, (!isCurrentColumnSorted || currentSort?.direction === "asc") && /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Item, {
146
160
  "data-testid": "column-sort-descending",
147
- onClick: () => model.setSort(state.fieldId, "desc")
161
+ onClick: () => model.sorting?.setSort(state.fieldId, "desc")
148
162
  }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Item, {
149
163
  "data-testid": "column-sort-ascending",
150
- onClick: () => model.setSort(state.fieldId, "asc")
164
+ onClick: () => model.sorting?.setSort(state.fieldId, "asc")
151
165
  }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Item, {
152
166
  "data-testid": "column-clear-sort",
153
- onClick: () => model.clearSort()
167
+ onClick: () => model.sorting?.clearSort()
154
168
  }, t("column action clear sorting")), model.getColumnCount() > 1 && /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Item, {
155
169
  "data-testid": "column-delete",
156
170
  onClick: () => model.deleteColumn(state.fieldId)
157
171
  }, t("column action delete")), /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Item, {
158
172
  "data-testid": "column-settings",
159
- onClick: () => model.modalController.openColumnSettings()
173
+ onClick: () => modals.openColumnSettings()
160
174
  }, t("column action settings"))), /* @__PURE__ */ import_react2.default.createElement(import_react_ui2.DropdownMenu.Arrow, null))));
161
175
  };
162
- var ColumnSettings = ({ model, onNewColumn }) => {
176
+ var ColumnSettings = ({ model, modals, onNewColumn }) => {
163
177
  const [newField, setNewField] = (0, import_react3.useState)();
164
- const state = model?.modalController.state.value;
178
+ const state = modals.state.value;
165
179
  const space = (0, import_echo2.getSpace)(model?.table);
166
180
  (0, import_react3.useEffect)(() => {
167
181
  if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
@@ -180,19 +194,19 @@ var ColumnSettings = ({ model, onNewColumn }) => {
180
194
  if (state?.type === "columnSettings") {
181
195
  const { mode } = state;
182
196
  if (mode.type === "edit") {
183
- return model?.table?.view?.fields.find((f) => f.id === mode.fieldId);
197
+ return model?.table?.view?.target?.fields.find((f) => f.id === mode.fieldId);
184
198
  }
185
199
  }
186
200
  return void 0;
187
201
  }, [
188
- model?.table?.view?.fields,
202
+ model?.table?.view?.target?.fields,
189
203
  state
190
204
  ]);
191
205
  const field = existingField ?? newField;
192
206
  const handleSave = (0, import_react3.useCallback)(() => {
193
- model?.modalController.close();
207
+ modals.close();
194
208
  }, [
195
- model?.modalController
209
+ modals
196
210
  ]);
197
211
  const handleCancel = (0, import_react3.useCallback)(() => {
198
212
  if (state?.type === "columnSettings" && state.mode.type === "create" && newField) {
@@ -203,18 +217,18 @@ var ColumnSettings = ({ model, onNewColumn }) => {
203
217
  state,
204
218
  newField
205
219
  ]);
206
- if (!model?.table?.view || !model.projection || !field) {
220
+ if (!model?.table?.view?.target || !model.projection || !field) {
207
221
  return null;
208
222
  }
209
223
  return /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.DropdownMenu.Root, {
210
224
  modal: false,
211
225
  open: state?.type === "columnSettings"
212
226
  }, /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.DropdownMenu.VirtualTrigger, {
213
- virtualRef: model.modalController.trigger
227
+ virtualRef: modals.trigger
214
228
  }), /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.DropdownMenu.Portal, null, /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.DropdownMenu.Content, {
215
229
  classNames: "md:is-64"
216
230
  }, /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.DropdownMenu.Viewport, null, /* @__PURE__ */ import_react3.default.createElement(import_react_ui_form.FieldEditor, {
217
- view: model.table.view,
231
+ view: model.table.view.target,
218
232
  projection: model.projection,
219
233
  field,
220
234
  registry: space?.db.schemaRegistry,
@@ -222,15 +236,17 @@ var ColumnSettings = ({ model, onNewColumn }) => {
222
236
  onCancel: handleCancel
223
237
  })), /* @__PURE__ */ import_react3.default.createElement(import_react_ui3.DropdownMenu.Arrow, null))));
224
238
  };
225
- var CreateRefPanel = ({ model, __gridScope }) => {
239
+ var CreateRefPanel = ({ model, modals, __gridScope }) => {
226
240
  const { id: gridId } = (0, import_react_ui_grid2.useGridContext)("TableCellEditor", __gridScope);
227
241
  const space = (0, import_echo3.getSpace)(model?.table);
228
- const state = model?.modalController.state.value;
242
+ const state = modals.state.value;
229
243
  const schema = (0, import_react4.useMemo)(() => {
230
244
  if (!space || state?.type !== "createRefPanel") {
231
245
  return;
232
246
  }
233
- const schema2 = space.db.schemaRegistry.getSchema(state.typename)?.schema;
247
+ const [schema2] = space.db.schemaRegistry.query({
248
+ typename: state.typename
249
+ }).runSync();
234
250
  if (schema2) {
235
251
  const omit = import_echo_schema2.S.omit("id");
236
252
  return omit(schema2);
@@ -243,23 +259,25 @@ var CreateRefPanel = ({ model, __gridScope }) => {
243
259
  if (!model || !space || state?.type !== "createRefPanel") {
244
260
  return;
245
261
  }
246
- const schema2 = space.db.schemaRegistry.getSchema(state.typename)?.schema;
262
+ const [schema2] = space.db.schemaRegistry.query({
263
+ typename: state.typename
264
+ }).runSync();
247
265
  if (schema2) {
248
266
  const obj = space.db.add((0, import_echo3.create)(schema2, values));
249
267
  state.onCreate?.(obj);
250
268
  }
251
- void model.modalController.close();
269
+ void modals.close();
252
270
  }, [
253
- model?.modalController,
271
+ modals,
254
272
  space,
255
273
  state
256
274
  ]);
257
275
  const handleCancel = (0, import_react4.useCallback)(() => {
258
276
  if (model) {
259
- model.modalController.close();
277
+ modals.close();
260
278
  }
261
279
  }, [
262
- model?.modalController
280
+ modals
263
281
  ]);
264
282
  if (!model?.table?.view || !model.projection) {
265
283
  return null;
@@ -269,11 +287,11 @@ var CreateRefPanel = ({ model, __gridScope }) => {
269
287
  open: state?.type === "createRefPanel" && !!schema,
270
288
  onOpenChange: (nextOpen) => {
271
289
  if (model && !nextOpen) {
272
- return model.modalController.close();
290
+ return modals.close();
273
291
  }
274
292
  }
275
293
  }, /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Popover.VirtualTrigger, {
276
- virtualRef: model.modalController.trigger
294
+ virtualRef: modals.trigger
277
295
  }), /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Popover.Portal, null, /* @__PURE__ */ import_react4.default.createElement(import_react_ui4.Popover.Content, {
278
296
  classNames: "md:is-64",
279
297
  "data-grid": gridId
@@ -291,24 +309,24 @@ var getRefTargetObj = (space, id) => {
291
309
  return void 0;
292
310
  }
293
311
  };
294
- var RefPanel = ({ model }) => {
295
- const state = model?.modalController.state.value;
296
- const space = (0, import_echo4.getSpace)(model?.table);
312
+ var RefPanel = ({ model, modals }) => {
313
+ const state = modals.state.value;
314
+ const space = (0, import_echo4.getSpace)(model.table);
297
315
  const targetObj = getRefTargetObj(space, state?.type === "refPanel" ? state.targetId : void 0);
298
316
  const schema = space && state?.type === "refPanel" ? space.db.schemaRegistry.getSchema(state.typename) : void 0;
299
- if (!model?.table?.view || !model.projection) {
317
+ if (!model.table?.view || !model.projection) {
300
318
  return null;
301
319
  }
302
320
  return /* @__PURE__ */ import_react5.default.createElement(import_react_ui5.Popover.Root, {
303
321
  modal: false,
304
322
  open: state?.type === "refPanel" && !!targetObj && !!schema,
305
323
  onOpenChange: (nextOpen) => {
306
- if (model && !nextOpen) {
307
- return model.modalController.close();
324
+ if (!nextOpen) {
325
+ return modals.close();
308
326
  }
309
327
  }
310
328
  }, /* @__PURE__ */ import_react5.default.createElement(import_react_ui5.Popover.VirtualTrigger, {
311
- virtualRef: model.modalController.trigger
329
+ virtualRef: modals.trigger
312
330
  }), /* @__PURE__ */ import_react5.default.createElement(import_react_ui5.Popover.Portal, null, /* @__PURE__ */ import_react5.default.createElement(import_react_ui5.Popover.Content, {
313
331
  classNames: "md:is-64"
314
332
  }, targetObj && schema && /* @__PURE__ */ import_react5.default.createElement(import_react_ui_form3.Form, {
@@ -317,731 +335,331 @@ var RefPanel = ({ model }) => {
317
335
  schema
318
336
  }), /* @__PURE__ */ import_react5.default.createElement(import_react_ui5.Popover.Arrow, null))));
319
337
  };
320
- var RowActionsMenu = ({ model }) => {
338
+ var RowActionsMenu = ({ model, modals }) => {
321
339
  const { t } = (0, import_react_ui6.useTranslation)(translationKey);
322
340
  const hasSelection = model.selection.hasSelection.value;
323
- const state = model.modalController.state.value;
341
+ const state = modals.state.value;
324
342
  if (state?.type !== "row") {
325
343
  return null;
326
344
  }
327
345
  return /* @__PURE__ */ import_react6.default.createElement(import_react_ui6.DropdownMenu.Root, {
328
346
  modal: false,
329
347
  open: true,
330
- onOpenChange: model.modalController.close
348
+ onOpenChange: modals.close
331
349
  }, /* @__PURE__ */ import_react6.default.createElement(import_react_ui6.DropdownMenu.VirtualTrigger, {
332
- virtualRef: model.modalController.trigger
350
+ virtualRef: modals.trigger
333
351
  }), /* @__PURE__ */ import_react6.default.createElement(import_react_ui6.DropdownMenu.Content, null, /* @__PURE__ */ import_react6.default.createElement(import_react_ui6.DropdownMenu.Viewport, null, /* @__PURE__ */ import_react6.default.createElement(import_react_ui6.DropdownMenu.Item, {
334
352
  "data-testid": "row-menu-delete",
335
353
  onClick: () => model.deleteRow(state.rowIndex)
336
354
  }, t(hasSelection ? "bulk delete row label" : "delete row label"))), /* @__PURE__ */ import_react6.default.createElement(import_react_ui6.DropdownMenu.Arrow, null)));
337
355
  };
338
- var completion = ({ debug, onQuery, onMatch }) => {
339
- return [
340
- import_view.EditorView.theme({
341
- ".cm-completionDialog": {
342
- width: "var(--dx-gridCellWidth)"
343
- }
344
- }),
345
- (0, import_autocomplete.autocompletion)({
346
- icons: false,
347
- activateOnTyping: true,
348
- closeOnBlur: !debug,
349
- override: [
350
- async (context) => {
351
- const text = context.state.doc.toString();
352
- return {
353
- from: 0,
354
- options: await onQuery(text)
355
- };
356
- }
357
- ],
358
- // TODO(burdon): Consts from grid (to line-up with grid).
359
- tooltipClass: () => "cm-completionDialog !mt-[8px] !-ml-[4px] [&>ul]:!max-h-[264px]",
360
- optionClass: () => "flex h-[33px] items-center"
361
- }),
362
- // Check for accepted completion metadata in the transaction.
363
- import_view.EditorView.updateListener.of((update) => {
364
- update.transactions.forEach((tr) => {
365
- const completion2 = tr.annotation(import_autocomplete.pickedCompletion);
366
- if (completion2) {
367
- onMatch(completion2.data);
368
- }
369
- });
370
- })
371
- ];
372
- };
373
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
374
- var newValue = Symbol.for("newValue");
375
- var createOption = (text) => ({
376
- [newValue]: true,
377
- text
378
- });
379
- var isCreateOption = (data) => typeof data === "object" && data[newValue];
380
- var TableCellEditor = ({ model, onEnter, onFocus, onQuery, __gridScope }) => {
381
- const { id: gridId, editing, setEditing } = (0, import_react_ui_grid3.useGridContext)("TableCellEditor", __gridScope);
382
- const suppressNextBlur = (0, import_react7.useRef)(false);
383
- const fieldProjection = (0, import_react7.useMemo)(() => {
384
- if (!model || !editing) {
385
- return;
356
+ var touch = (value) => value;
357
+ var initializeTable = async ({ space, table, initialRow = true, initialSchema }) => {
358
+ if (initialSchema) {
359
+ const schema = await space.db.schemaRegistry.query({
360
+ typename: initialSchema
361
+ }).firstOrUndefined();
362
+ if (!schema) {
363
+ throw new Error(`Schema not found: ${initialSchema}`);
386
364
  }
387
- const { col } = (0, import_react_ui_grid3.parseCellIndex)(editing.index);
388
- const field = model.projection.view.fields[col];
389
- const fieldProjection2 = model.projection.getFieldProjection(field.id);
390
- (0, import_invariant2.invariant)(fieldProjection2, void 0, {
391
- F: __dxlog_file,
392
- L: 65,
393
- S: void 0,
394
- A: [
395
- "fieldProjection",
396
- ""
365
+ const fields = (0, import_schema.getSchemaProperties)(schema.ast).filter((prop) => prop.type !== "object" || prop.format === import_echo_schema4.FormatEnum.Ref).map((prop) => prop.name);
366
+ table.view = (0, import_echo5.makeRef)((0, import_schema.createView)({
367
+ // TODO(ZaymonFC): Don't hardcode name?
368
+ name: "View",
369
+ typename: schema.typename,
370
+ jsonSchema: schema.jsonSchema,
371
+ fields
372
+ }));
373
+ return schema;
374
+ } else {
375
+ const ContactSchema = (0, import_echo_schema4.TypedObject)({
376
+ typename: `example.com/type/${import_react_client2.PublicKey.random().truncate()}`,
377
+ version: "0.1.0"
378
+ })({
379
+ name: import_echo_schema4.S.optional(import_echo_schema4.S.String).annotations({
380
+ [import_echo_schema4.AST.TitleAnnotationId]: "Name"
381
+ }),
382
+ active: import_echo_schema4.S.optional(import_echo_schema4.S.Boolean),
383
+ email: import_echo_schema4.S.optional(import_echo_schema4.Format.Email),
384
+ salary: import_echo_schema4.S.optional(import_echo_schema4.Format.Currency()).annotations({
385
+ [import_echo_schema4.AST.TitleAnnotationId]: "Salary"
386
+ })
387
+ });
388
+ const [contactSchema] = await space.db.schemaRegistry.register([
389
+ ContactSchema
390
+ ]);
391
+ table.view = (0, import_echo5.makeRef)((0, import_schema.createView)({
392
+ name: "View",
393
+ typename: contactSchema.typename,
394
+ jsonSchema: contactSchema.jsonSchema,
395
+ fields: [
396
+ "name",
397
+ "email",
398
+ "salary",
399
+ "active"
397
400
  ]
401
+ }));
402
+ const projection = new import_schema.ViewProjection(contactSchema, table.view.target);
403
+ projection.setFieldProjection({
404
+ field: {
405
+ id: table.view.target.fields.find((f) => f.path === "salary").id,
406
+ path: "salary",
407
+ size: 150
408
+ }
398
409
  });
399
- return fieldProjection2;
400
- }, [
401
- model,
402
- editing
403
- ]);
404
- const handleEnter = (0, import_react7.useCallback)((value) => {
405
- if (!model || !editing) {
406
- return;
407
- }
408
- const cell = (0, import_react_ui_grid3.parseCellIndex)(editing.index);
409
- model.setCellData(cell, value);
410
- onEnter?.(cell);
411
- onFocus?.();
412
- setEditing(null);
413
- }, [
414
- model,
415
- editing
416
- ]);
417
- const handleBlur = (0, import_react7.useCallback)((value) => {
418
- if (!model || !editing) {
419
- return;
420
- }
421
- if (suppressNextBlur.current) {
422
- suppressNextBlur.current = false;
423
- return;
424
- }
425
- const cell = (0, import_react_ui_grid3.parseCellIndex)(editing.index);
426
- if (value !== void 0) {
427
- model.setCellData(cell, value);
428
- }
429
- }, [
430
- model,
431
- editing
432
- ]);
433
- const handleClose = (0, import_react7.useCallback)((value, event) => {
434
- if (!model || !editing || !fieldProjection) {
435
- return;
436
- }
437
- const cell = (0, import_react_ui_grid3.parseCellIndex)(editing.index);
438
- onEnter?.(cell);
439
- if (event && onFocus) {
440
- onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
441
- }
442
- }, [
443
- model,
444
- editing,
445
- onFocus,
446
- onEnter,
447
- fieldProjection,
448
- determineNavigationAxis,
449
- determineNavigationDelta
450
- ]);
451
- const extension = (0, import_react7.useMemo)(() => {
452
- if (!fieldProjection) {
453
- return [];
454
- }
455
- const extension2 = [
456
- (0, import_react_ui_grid3.editorKeys)({
457
- onClose: handleClose,
458
- ...editing?.initialContent && {
459
- onNav: handleClose
460
- }
461
- })
462
- ];
463
- if (onQuery) {
464
- switch (fieldProjection.props.format) {
465
- case import_echo_schema3.FormatEnum.Ref: {
466
- extension2.push([
467
- completion({
468
- onQuery: (text) => onQuery(fieldProjection, text),
469
- onMatch: (data) => {
470
- if (model && editing) {
471
- if (isCreateOption(data)) {
472
- const { field, props } = fieldProjection;
473
- if (props.referenceSchema) {
474
- suppressNextBlur.current = true;
475
- model.modalController.openCreateRef(props.referenceSchema, document.querySelector((0, import_react_ui_grid3.cellQuery)(editing.index, gridId)), {
476
- [field.referencePath]: data.text
477
- }, (data2) => {
478
- handleEnter(data2);
479
- });
480
- }
481
- } else {
482
- handleEnter(data);
483
- }
484
- }
485
- }
486
- })
487
- ]);
488
- break;
489
- }
410
+ projection.setFieldProjection({
411
+ field: {
412
+ id: table.view.target.fields.find((f) => f.path === "active").id,
413
+ path: "active",
414
+ size: 100
490
415
  }
416
+ });
417
+ projection.setFieldProjection({
418
+ field: {
419
+ id: (0, import_schema.createFieldId)(),
420
+ path: "manager",
421
+ referencePath: "name"
422
+ },
423
+ props: {
424
+ property: "manager",
425
+ type: import_echo_schema4.TypeEnum.Ref,
426
+ format: import_echo_schema4.FormatEnum.Ref,
427
+ referenceSchema: contactSchema.typename,
428
+ title: "Manager"
429
+ }
430
+ });
431
+ if (initialRow) {
432
+ space.db.add((0, import_echo5.create)(contactSchema, {}));
491
433
  }
492
- return extension2;
493
- }, [
494
- model,
495
- editing,
496
- fieldProjection,
497
- handleClose
498
- ]);
499
- const getCellContent = (0, import_react7.useCallback)(() => {
500
- if (model && editing) {
501
- const value = model.getCellData((0, import_react_ui_grid3.parseCellIndex)(editing.index));
502
- return value !== void 0 ? String(value) : "";
503
- }
504
- }, [
505
- model,
506
- editing
507
- ]);
508
- return /* @__PURE__ */ import_react7.default.createElement(import_react_ui_grid3.GridCellEditor, {
509
- extension,
510
- getCellContent,
511
- onBlur: handleBlur
512
- });
513
- };
514
- var determineNavigationAxis = ({ key }) => {
515
- switch (key) {
516
- case "ArrowUp":
517
- case "ArrowDown":
518
- case "Enter": {
519
- return "row";
520
- }
521
- case "ArrowLeft":
522
- case "ArrowRight":
523
- case "Tab":
524
- return "col";
434
+ return contactSchema;
525
435
  }
526
436
  };
527
- var determineNavigationDelta = ({ key, shift }) => {
528
- switch (key) {
529
- case "ArrowUp":
530
- case "ArrowLeft":
531
- return -1;
532
- case "ArrowDown":
533
- case "ArrowRight":
534
- return 1;
535
- }
536
- return shift ? -1 : 1;
437
+ var BUTTON_IDENTIFIERS = {
438
+ columnSettings: "data-table-column-settings-button",
439
+ newColumn: "data-table-new-column-button",
440
+ referencedCell: "data-table-ref-cell-button",
441
+ rowMenu: "data-table-row-menu-button",
442
+ sort: "data-table-sort-button"
537
443
  };
538
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
539
- var inlineEndLine = "[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-block-0 [&>.dx-grid]:after:-inline-end-px [&>.dx-grid]:after:is-px [&>.dx-grid]:after:bg-separator";
540
- var blockEndLine = "[&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-inline-0 [&>.dx-grid]:before:-block-end-px [&>.dx-grid]:before:bs-px [&>.dx-grid]:before:bg-separator";
541
- var frozen = {
542
- frozenRowsStart: 1,
543
- frozenColsStart: 1,
544
- frozenColsEnd: 1
444
+ var createButton = ({ attr, data, disabled = false, icon, testId, type = "primary" }) => {
445
+ const dataAttrs = Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
446
+ const positionClass = type === "primary" ? "inline-end-2" : "inline-end-9";
447
+ return `<button ${attr} data-testid="${testId}" class="dx-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 ${positionClass}" ${dataAttrs} ${disabled ? "disabled" : ""}><svg><use href="/icons.svg#${icon}"/></svg></button>`;
545
448
  };
546
- var TableRoot = ({ children, role }) => {
547
- return /* @__PURE__ */ import_react.default.createElement("div", {
548
- role: "none",
549
- className: (0, import_react_ui_theme.mx)("relative border-bs !border-separator", role === "section" ? "attention-surface overflow-hidden [&_.dx-grid]:max-is-[--dx-grid-content-inline-size]" : "flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:bs-0 [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]")
550
- }, children);
449
+ var addColumnButton = {
450
+ attr: BUTTON_IDENTIFIERS.newColumn,
451
+ icon: "ph--plus--regular",
452
+ render: ({ disabled }) => {
453
+ return createButton({
454
+ attr: BUTTON_IDENTIFIERS.newColumn,
455
+ icon: addColumnButton.icon,
456
+ disabled,
457
+ data: {},
458
+ testId: "table-new-column-button"
459
+ });
460
+ },
461
+ getData: (_el) => ({
462
+ type: "newColumn"
463
+ })
551
464
  };
552
- var TableMain = /* @__PURE__ */ (0, import_react.forwardRef)(({ model, ignoreAttention }, forwardedRef) => {
553
- const [dxGrid, setDxGrid] = (0, import_react.useState)(null);
554
- const { hasAttention } = (0, import_react_ui_attention.useAttention)(model?.table ? (0, import_echo.fullyQualifiedId)(model.table) : "table");
555
- const { t } = (0, import_react_ui.useTranslation)(translationKey);
556
- (0, import_react.useImperativeHandle)(forwardedRef, () => {
557
- if (!model || !dxGrid) {
558
- return {};
559
- }
560
- dxGrid.getCells = model.getCells.bind(model);
561
- return {
562
- update: (cell) => {
563
- if (cell) {
564
- dxGrid.updateIfWithinBounds(cell);
565
- } else {
566
- dxGrid.updateCells(true);
567
- }
568
- }
569
- };
570
- }, [
571
- model,
572
- dxGrid
573
- ]);
574
- const handleFocus = (0, import_react.useCallback)((increment, delta) => {
575
- if (dxGrid) {
576
- dxGrid.refocus(increment, delta);
577
- }
578
- }, [
579
- dxGrid
580
- ]);
581
- const handleEnter = (0, import_react.useCallback)((cell) => {
582
- if (model && cell.row === model.getRowCount() - 1) {
583
- model.insertRow(cell.row);
584
- if (dxGrid) {
585
- requestAnimationFrame(() => {
586
- dxGrid?.scrollToRow(cell.row + 1);
587
- dxGrid?.refocus("row", 1);
588
- });
589
- }
590
- }
591
- }, [
592
- model,
593
- dxGrid
594
- ]);
595
- const handleKeyDown = (0, import_react.useCallback)((event) => {
596
- const cell = (0, import_react_ui_grid.closestCell)(event.target);
597
- if (!model || !cell) {
598
- return;
599
- }
600
- switch (event.key) {
601
- case "Backspace":
602
- case "Delete": {
603
- model.setCellData(cell, void 0);
604
- break;
605
- }
606
- }
607
- }, [
608
- model
609
- ]);
610
- const handleAxisResize = (0, import_react.useCallback)((event) => {
611
- if (event.axis === "col") {
612
- const columnIndex = parseInt(event.index, 10);
613
- model?.setColumnWidth(columnIndex, event.size);
614
- }
615
- }, [
616
- model
617
- ]);
618
- const handleWheel = (0, import_react.useCallback)((event) => {
619
- if (!ignoreAttention && !hasAttention) {
620
- event.stopPropagation();
621
- }
622
- }, [
623
- hasAttention,
624
- ignoreAttention
625
- ]);
626
- const handleNewColumn = (0, import_react.useCallback)(() => {
627
- const columns = model?.getColumnCount();
628
- if (dxGrid && columns) {
629
- dxGrid.scrollToColumn(columns - 1);
630
- }
631
- }, [
632
- model,
633
- dxGrid
634
- ]);
635
- const handleQuery = (0, import_react.useCallback)(async ({ field, props }, text) => {
636
- if (model && props.referenceSchema && field.referencePath) {
637
- const space = (0, import_echo.getSpace)(model.table);
638
- (0, import_invariant.invariant)(space, void 0, {
639
- F: __dxlog_file2,
640
- L: 180,
641
- S: void 0,
642
- A: [
643
- "space",
644
- ""
645
- ]
646
- });
647
- const schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
648
- if (schema) {
649
- const { objects } = await space.db.query(import_echo.Filter.schema(schema)).run();
650
- const options = objects.map((obj) => {
651
- const value = (0, import_echo_schema.getValue)(obj, field.referencePath);
652
- if (!value || typeof value !== "string") {
653
- return void 0;
654
- }
655
- return {
656
- label: value,
657
- data: obj
658
- };
659
- }).filter(import_util.isNotFalsy);
660
- return [
661
- ...options,
662
- {
663
- label: t("create new object label", {
664
- text
665
- }),
666
- data: createOption(text)
667
- }
668
- ];
669
- }
670
- }
671
- return [];
672
- }, [
673
- model
674
- ]);
675
- if (!model) {
676
- return /* @__PURE__ */ import_react.default.createElement("span", {
677
- role: "none",
678
- className: "attention-surface"
465
+ var columnSettingsButton = {
466
+ attr: BUTTON_IDENTIFIERS.columnSettings,
467
+ icon: "ph--caret-down--regular",
468
+ render: ({ fieldId }) => {
469
+ return createButton({
470
+ attr: BUTTON_IDENTIFIERS.columnSettings,
471
+ icon: columnSettingsButton.icon,
472
+ data: {
473
+ "data-field-id": fieldId
474
+ },
475
+ testId: "table-column-settings-button"
679
476
  });
680
- }
681
- return /* @__PURE__ */ import_react.default.createElement(import_react_ui_grid.Grid.Root, {
682
- id: model.table.id ?? "table-grid"
683
- }, /* @__PURE__ */ import_react.default.createElement(TableCellEditor, {
684
- model,
685
- onEnter: handleEnter,
686
- onFocus: handleFocus,
687
- onQuery: handleQuery
688
- }), /* @__PURE__ */ import_react.default.createElement(import_react_ui_grid.Grid.Content, {
689
- onWheelCapture: handleWheel,
690
- className: (0, import_react_ui_theme.mx)("[--dx-grid-base:var(--surface-bg)]", inlineEndLine, blockEndLine),
691
- frozen,
692
- columns: model.columnMeta.value,
693
- limitRows: model.getRowCount() ?? 0,
694
- limitColumns: model.table.view?.fields?.length ?? 0,
695
- onAxisResize: handleAxisResize,
696
- onClick: model?.handleGridClick,
697
- onKeyDown: handleKeyDown,
698
- overscroll: "trap",
699
- ref: setDxGrid
700
- }), /* @__PURE__ */ import_react.default.createElement(RowActionsMenu, {
701
- model
702
- }), /* @__PURE__ */ import_react.default.createElement(ColumnActionsMenu, {
703
- model
704
- }), /* @__PURE__ */ import_react.default.createElement(ColumnSettings, {
705
- model,
706
- onNewColumn: handleNewColumn
707
- }), /* @__PURE__ */ import_react.default.createElement(RefPanel, {
708
- model
709
- }), /* @__PURE__ */ import_react.default.createElement(CreateRefPanel, {
710
- model
711
- }));
712
- });
713
- var Table = {
714
- Root: TableRoot,
715
- Main: TableMain
477
+ },
478
+ getData: (el) => ({
479
+ type: "columnSettings",
480
+ fieldId: el.getAttribute("data-field-id")
481
+ })
716
482
  };
717
- var buttonStyles = "min-bs-0 p-2";
718
- var tooltipProps = {
719
- side: "top",
720
- classNames: "z-10"
483
+ var referencedCellButton = {
484
+ attr: BUTTON_IDENTIFIERS.referencedCell,
485
+ icon: "ph--link-simple-horizontal--regular",
486
+ render: ({ targetId, schemaId }) => {
487
+ return createButton({
488
+ attr: BUTTON_IDENTIFIERS.referencedCell,
489
+ icon: referencedCellButton.icon,
490
+ data: {
491
+ "data-target-id": targetId,
492
+ "data-schema-id": schemaId
493
+ },
494
+ testId: "table-ref-cell-button"
495
+ });
496
+ },
497
+ getData: (el) => ({
498
+ type: "referencedCell",
499
+ targetId: el.getAttribute("data-target-id"),
500
+ schemaId: el.getAttribute("data-schema-id")
501
+ })
721
502
  };
722
- var ToolbarSeparator = () => /* @__PURE__ */ import_react9.default.createElement("div", {
723
- role: "separator",
724
- className: "grow"
725
- });
726
- var ToolbarButton = ({ icon, children, ...props }) => {
727
- return /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Tooltip.Root, null, /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Tooltip.Trigger, {
728
- asChild: true
729
- }, /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Toolbar.Button, {
730
- variant: "ghost",
731
- ...props,
732
- classNames: buttonStyles
733
- }, /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Icon, {
734
- icon,
735
- size: 5
736
- }), /* @__PURE__ */ import_react9.default.createElement("span", {
737
- className: "sr-only"
738
- }, children))), /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Tooltip.Portal, null, /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Tooltip.Content, tooltipProps, children, /* @__PURE__ */ import_react9.default.createElement(import_react_ui8.Tooltip.Arrow, null))));
503
+ var rowMenuButton = {
504
+ attr: BUTTON_IDENTIFIERS.rowMenu,
505
+ icon: "ph--dots-three--regular",
506
+ render: ({ rowIndex }) => {
507
+ return createButton({
508
+ attr: BUTTON_IDENTIFIERS.rowMenu,
509
+ icon: rowMenuButton.icon,
510
+ data: {
511
+ "data-row-index": rowIndex.toString()
512
+ },
513
+ testId: "table-row-menu-button"
514
+ });
515
+ },
516
+ getData: (el) => ({
517
+ type: "rowMenu",
518
+ rowIndex: Number(el.getAttribute("data-row-index"))
519
+ })
739
520
  };
740
- var [ToolbarContextProvider, useToolbarContext] = (0, import_react_context.createContext)("Toolbar");
741
- var ToolbarRoot = ({ classNames, children, onAction }) => {
742
- return /* @__PURE__ */ import_react8.default.createElement(ToolbarContextProvider, {
743
- onAction
744
- }, /* @__PURE__ */ import_react8.default.createElement(import_react_ui7.Toolbar.Root, {
745
- classNames: [
746
- "is-full shrink-0 overflow-x-auto overflow-y-hidden p-1 attention-surface",
747
- classNames
748
- ]
749
- }, children));
521
+ var sortButton = {
522
+ attr: BUTTON_IDENTIFIERS.sort,
523
+ icon: "ph--sort-ascending--regular",
524
+ render: ({ fieldId, direction }) => {
525
+ return createButton({
526
+ attr: BUTTON_IDENTIFIERS.sort,
527
+ icon: direction === "desc" ? "ph--sort-descending--regular" : "ph--sort-ascending--regular",
528
+ data: {
529
+ "data-field-id": fieldId,
530
+ "data-direction": direction ?? ""
531
+ },
532
+ testId: "table-sort-button",
533
+ // TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
534
+ // This is a bit hacky.
535
+ type: "secondary"
536
+ });
537
+ },
538
+ getData: (el) => ({
539
+ type: "sort",
540
+ fieldId: el.getAttribute("data-field-id"),
541
+ direction: el.getAttribute("data-direction")
542
+ })
750
543
  };
751
- var Editing = () => {
752
- const { onAction } = useToolbarContext("Editing");
753
- const { t } = (0, import_react_ui7.useTranslation)(translationKey);
754
- return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(ToolbarButton, {
755
- value: "add-row",
756
- icon: "ph--plus--regular",
757
- "data-testid": "table.toolbar.add-row",
758
- onClick: () => onAction?.({
759
- type: "add-row"
760
- })
761
- }, t("add row")));
544
+ var tableButtons = {
545
+ addColumn: addColumnButton,
546
+ columnSettings: columnSettingsButton,
547
+ referencedCell: referencedCellButton,
548
+ rowMenu: rowMenuButton,
549
+ sort: sortButton
762
550
  };
763
- var Actions = () => {
764
- const { onAction } = useToolbarContext("Actions");
765
- const { t } = (0, import_react_ui7.useTranslation)(translationKey);
766
- return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null, /* @__PURE__ */ import_react8.default.createElement(ToolbarButton, {
767
- value: "comment",
768
- icon: "ph--chat-text--regular",
769
- "data-testid": "table.toolbar.comment",
770
- onClick: () => onAction?.({
771
- type: "comment"
772
- })
773
- }, t("create comment")));
551
+ var CONTROL_IDENTIFIERS = {
552
+ checkbox: "data-table-checkbox",
553
+ switch: "data-table-switch"
774
554
  };
775
- var Toolbar = {
776
- Root: ToolbarRoot,
777
- Editing,
778
- Actions,
779
- Separator: ToolbarSeparator
555
+ var BASE_CLASSES = {
556
+ checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] dx-checkbox",
557
+ switch: "absolute inset-block-[.25rem] inline-end-[.25rem] dx-checkbox--switch"
780
558
  };
781
- var touch = (value) => value;
782
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/util/table.ts";
783
- var initializeTable = ({ space, table, initialRow = true }) => {
784
- import_log2.log.info("initializeTable", {
785
- table
786
- }, {
787
- F: __dxlog_file3,
788
- L: 32,
789
- S: void 0,
790
- C: (f, a) => f(...a)
791
- });
792
- const ContactSchema = (0, import_echo_schema5.TypedObject)({
793
- typename: `example.com/type/${import_react_client2.PublicKey.random().truncate()}`,
794
- version: "0.1.0"
795
- })({
796
- name: import_echo_schema5.S.optional(import_echo_schema5.S.String).annotations({
797
- [import_echo_schema5.AST.TitleAnnotationId]: "Name"
798
- }),
799
- email: import_echo_schema5.S.optional(import_echo_schema5.Format.Email),
800
- salary: import_echo_schema5.S.optional(import_echo_schema5.Format.Currency()).annotations({
801
- [import_echo_schema5.AST.TitleAnnotationId]: "Salary"
802
- })
803
- });
804
- const contactSchema = space.db.schemaRegistry.addSchema(ContactSchema);
805
- table.view = (0, import_schema2.createView)({
806
- name: "Test",
807
- typename: contactSchema.typename,
808
- jsonSchema: contactSchema.jsonSchema,
809
- fields: [
810
- "name",
811
- "email",
812
- "salary"
813
- ]
814
- });
815
- const projection = new import_schema2.ViewProjection(contactSchema, table.view);
816
- projection.setFieldProjection({
817
- field: {
818
- id: table.view.fields[2].id,
819
- path: "salary",
820
- size: 150
821
- }
822
- });
823
- projection.setFieldProjection({
824
- field: {
825
- id: (0, import_schema2.createFieldId)(),
826
- path: "manager",
827
- referencePath: "name"
828
- },
829
- props: {
830
- property: "manager",
831
- type: import_echo_schema5.TypeEnum.Ref,
832
- format: import_echo_schema5.FormatEnum.Ref,
833
- referenceSchema: contactSchema.typename,
834
- title: "Manager"
835
- }
836
- });
837
- if (initialRow) {
838
- space.db.add((0, import_echo5.create)(contactSchema, {}));
839
- }
840
- return contactSchema;
559
+ var renderAttributes = (data) => {
560
+ return Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
841
561
  };
842
- var TABLE_ATTRS = {
843
- referencedCell: "data-table-ref-cell-button",
844
- columnSettings: "data-table-column-settings-button",
845
- rowMenu: "data-table-row-menu-button",
846
- newColumn: "data-table-new-column-button"
562
+ var renderInput = (baseClass, attrs, checked = false, disabled = false, preventToggle = false, testId) => {
563
+ return `<input type="checkbox" class="${baseClass}" ${renderAttributes(attrs)} ${checked ? "checked" : ""} ${preventToggle ? 'onclick="return false"' : ""} ${disabled ? "disabled" : ""} data-testid="${testId}"/>`;
847
564
  };
848
- var ICONS = {
849
- referencedCell: "ph--link-simple-horizontal--regular",
850
- columnSettings: "ph--caret-down--regular",
851
- rowMenu: "ph--dots-three--regular",
852
- newColumn: "ph--plus--regular"
565
+ var renderCheckbox = ({ rowIndex, header = false, checked = false, disabled = false }) => {
566
+ const attrs = {
567
+ [CONTROL_IDENTIFIERS.checkbox]: "",
568
+ "data-row-index": rowIndex.toString(),
569
+ ...header && {
570
+ "data-header": ""
571
+ }
572
+ };
573
+ return renderInput(BASE_CLASSES.checkbox, attrs, checked, disabled, true, "table-selection");
853
574
  };
854
- var createButtonHtml = ({ button, value = "", disabled = false, testId }) => {
855
- const buttonClasses = "ch-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 inline-end-2";
856
- return `<button class="${buttonClasses}" ${TABLE_ATTRS[button]}="${value}" data-testid="${testId}" ${disabled ? "disabled" : ""}><svg><use href="/icons.svg#${ICONS[button]}"/></svg></button>`;
575
+ var renderSwitch = ({ colIndex, rowIndex, checked = false, disabled = false }) => {
576
+ const attrs = {
577
+ [CONTROL_IDENTIFIERS.switch]: "",
578
+ "data-row-index": rowIndex.toString(),
579
+ "data-col-index": colIndex.toString()
580
+ };
581
+ return renderInput(BASE_CLASSES.switch, attrs, checked, disabled, true, "table-switch");
857
582
  };
858
- var tableButtons = {
859
- referencedCell: {
860
- attr: TABLE_ATTRS.referencedCell,
861
- icon: ICONS.referencedCell,
862
- render: ({ targetId, schemaId }) => createButtonHtml({
863
- button: "referencedCell",
864
- value: `${schemaId}#${targetId}`,
865
- testId: "table-ref-cell-button"
866
- })
867
- },
868
- columnSettings: {
869
- attr: TABLE_ATTRS.columnSettings,
870
- icon: ICONS.columnSettings,
871
- render: ({ fieldId }) => createButtonHtml({
872
- button: "columnSettings",
873
- value: fieldId,
874
- testId: "table-column-settings-button"
875
- })
876
- },
877
- rowMenu: {
878
- attr: TABLE_ATTRS.rowMenu,
879
- icon: ICONS.rowMenu,
880
- render: ({ rowIndex }) => createButtonHtml({
881
- button: "rowMenu",
882
- value: rowIndex.toString(),
883
- testId: "table-row-menu-button"
583
+ var tableControls = {
584
+ checkbox: {
585
+ attr: CONTROL_IDENTIFIERS.checkbox,
586
+ render: renderCheckbox,
587
+ getData: (el) => ({
588
+ type: "checkbox",
589
+ rowIndex: Number(el.getAttribute("data-row-index")),
590
+ header: el.hasAttribute("data-header")
884
591
  })
885
592
  },
886
- addColumn: {
887
- attr: TABLE_ATTRS.newColumn,
888
- icon: ICONS.newColumn,
889
- render: ({ disabled } = {}) => createButtonHtml({
890
- button: "newColumn",
891
- disabled,
892
- testId: "table-new-column-button"
593
+ switch: {
594
+ attr: CONTROL_IDENTIFIERS.switch,
595
+ render: renderSwitch,
596
+ getData: (el) => ({
597
+ type: "switch",
598
+ rowIndex: Number(el.getAttribute("data-row-index")),
599
+ colIndex: Number(el.getAttribute("data-col-index"))
893
600
  })
894
601
  }
895
602
  };
896
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
897
- var ModalController = class {
898
- constructor() {
899
- this._state = (0, import_signals_core2.signal)({
900
- type: "closed"
901
- });
902
- this._triggerRef = {
903
- current: null
904
- };
905
- this.handleClick = (event) => {
906
- const target = event.target;
907
- const rowButton = target.closest(`button[${tableButtons.rowMenu.attr}]`);
908
- if (rowButton) {
909
- this._triggerRef.current = rowButton;
910
- this._state.value = {
911
- type: "row",
912
- rowIndex: Number(rowButton.getAttribute(tableButtons.rowMenu.attr))
913
- };
914
- return true;
603
+ var SelectionModel = class extends import_context2.Resource {
604
+ constructor(_rows, _onSelectionChanged) {
605
+ super();
606
+ this._rows = _rows;
607
+ this._onSelectionChanged = _onSelectionChanged;
608
+ this._selection = (0, import_signals_core2.signal)(/* @__PURE__ */ new Set());
609
+ this._validSelectedIds = (0, import_signals_core2.computed)(() => {
610
+ const rows = this._rows.value;
611
+ if (!rows) {
612
+ return /* @__PURE__ */ new Set();
915
613
  }
916
- const columnButton = target.closest(`button[${tableButtons.columnSettings.attr}]`);
917
- if (columnButton) {
918
- const fieldId = columnButton.getAttribute(tableButtons.columnSettings.attr);
919
- this._triggerRef.current = columnButton;
920
- this._state.value = {
921
- type: "column",
922
- fieldId
923
- };
924
- return true;
614
+ const validIds = new Set(rows.map((row) => row.id));
615
+ return new Set([
616
+ ...this._selection.value
617
+ ].filter((id) => validIds.has(id)));
618
+ });
619
+ this._hasSelection = (0, import_signals_core2.computed)(() => this._validSelectedIds.value.size > 0);
620
+ this._allSelected = (0, import_signals_core2.computed)(() => {
621
+ const rows = this._rows.value;
622
+ if (rows.length === 0) {
623
+ return false;
925
624
  }
926
- const addColumnButton = target.closest(`button[${tableButtons.addColumn.attr}]`);
927
- if (addColumnButton) {
928
- this._triggerRef.current = addColumnButton;
929
- this._state.value = {
930
- type: "columnSettings",
931
- mode: {
932
- type: "create"
933
- }
934
- };
935
- return true;
625
+ return rows.every((row) => this._selection.value.has(row.id));
626
+ });
627
+ this.getSelectedRows = () => {
628
+ const selectedIds = this._validSelectedIds.value;
629
+ return this._rows.value.filter((row) => selectedIds.has(row.id));
630
+ };
631
+ this.isRowIndexSelected = (rowIndex) => {
632
+ const row = this._rows.value[rowIndex];
633
+ return this._selection.value.has(row.id);
634
+ };
635
+ this.isObjectSelected = (object) => {
636
+ return this._selection.value.has(object.id);
637
+ };
638
+ this.toggleSelectionForRowIndex = (rowIndex) => {
639
+ const row = this._rows.value[rowIndex];
640
+ const newSelection = new Set(this._selection.value);
641
+ if (newSelection.has(row.id)) {
642
+ newSelection.delete(row.id);
643
+ } else {
644
+ newSelection.add(row.id);
936
645
  }
937
- const refButton = target.closest(`button[${tableButtons.referencedCell.attr}]`);
938
- if (refButton) {
939
- this._triggerRef.current = refButton;
940
- const [schemaId, targetId] = refButton.getAttribute(tableButtons.referencedCell.attr).split("#");
941
- this._state.value = {
942
- type: "refPanel",
943
- targetId,
944
- typename: schemaId
945
- };
646
+ this._selection.value = newSelection;
647
+ };
648
+ this.setSelection = (mode) => {
649
+ switch (mode) {
650
+ case "all": {
651
+ this._selection.value = new Set(this._rows.value.map((row) => row.id));
652
+ break;
653
+ }
654
+ case "none": {
655
+ this._selection.value = /* @__PURE__ */ new Set();
656
+ break;
657
+ }
946
658
  }
947
- return false;
948
- };
949
- this.openColumnSettings = () => {
950
- if (this._state.value.type === "column") {
951
- const fieldId = this._state.value.fieldId;
952
- requestAnimationFrame(() => {
953
- this._state.value = {
954
- type: "columnSettings",
955
- mode: {
956
- type: "edit",
957
- fieldId
958
- }
959
- };
960
- });
961
- }
962
- };
963
- this.openCreateRef = (typename, anchorCell, initialValues, onCreate) => {
964
- if (anchorCell) {
965
- this._triggerRef.current = anchorCell;
966
- this._state.value = {
967
- type: "createRefPanel",
968
- typename,
969
- initialValues,
970
- onCreate
971
- };
972
- } else {
973
- import_log.log.warn("anchor cell not found while creating new ref", void 0, {
974
- F: __dxlog_file4,
975
- L: 119,
976
- S: this,
977
- C: (f, a) => f(...a)
978
- });
979
- }
980
- };
981
- this.close = () => {
982
- this._state.value = {
983
- type: "closed"
984
- };
985
- };
986
- }
987
- get state() {
988
- return this._state;
989
- }
990
- get trigger() {
991
- return this._triggerRef;
992
- }
993
- };
994
- var SelectionModel = class extends import_context2.Resource {
995
- constructor(_rows, _onSelectionChanged) {
996
- super();
997
- this._rows = _rows;
998
- this._onSelectionChanged = _onSelectionChanged;
999
- this._selection = (0, import_signals_core3.signal)(/* @__PURE__ */ new Set());
1000
- this._validSelectedIds = (0, import_signals_core3.computed)(() => {
1001
- const rows = this._rows.value;
1002
- if (!rows) {
1003
- return /* @__PURE__ */ new Set();
1004
- }
1005
- const validIds = new Set(rows.map((row) => row.id));
1006
- return new Set([
1007
- ...this._selection.value
1008
- ].filter((id) => validIds.has(id)));
1009
- });
1010
- this._hasSelection = (0, import_signals_core3.computed)(() => this._validSelectedIds.value.size > 0);
1011
- this._allSelected = (0, import_signals_core3.computed)(() => {
1012
- const rows = this._rows.value;
1013
- if (rows.length === 0) {
1014
- return false;
1015
- }
1016
- return rows.every((row) => this._selection.value.has(row.id));
1017
- });
1018
- this.getSelectedRows = () => {
1019
- const selectedIds = this._validSelectedIds.value;
1020
- return this._rows.value.filter((row) => selectedIds.has(row.id));
1021
- };
1022
- this.isRowIndexSelected = (rowIndex) => {
1023
- const row = this._rows.value[rowIndex];
1024
- return this._selection.value.has(row.id);
1025
- };
1026
- this.isObjectSelected = (object) => {
1027
- return this._selection.value.has(object.id);
1028
- };
1029
- this.toggleSelectionForRowIndex = (rowIndex) => {
1030
- const row = this._rows.value[rowIndex];
1031
- const newSelection = new Set(this._selection.value);
1032
- if (newSelection.has(row.id)) {
1033
- newSelection.delete(row.id);
1034
- } else {
1035
- newSelection.add(row.id);
1036
- }
1037
- this._selection.value = newSelection;
1038
- };
1039
- this.setSelection = (mode) => {
1040
- this._selection.value = mode === "all" ? new Set(this._rows.value.map((row) => row.id)) : /* @__PURE__ */ new Set();
1041
659
  };
1042
660
  }
1043
661
  async _open() {
1044
- const selectionWatcher = (0, import_signals_core3.effect)(() => {
662
+ const selectionWatcher = (0, import_signals_core2.effect)(() => {
1045
663
  touch(this._selection.value);
1046
664
  this._onSelectionChanged?.();
1047
665
  });
@@ -1060,22 +678,166 @@ var SelectionModel = class extends import_context2.Resource {
1060
678
  return this._allSelected;
1061
679
  }
1062
680
  };
1063
- var checkboxAttributes = {
1064
- checkbox: "data-table-checkbox",
1065
- header: "data-table-header"
1066
- };
1067
- var renderCheckbox = ({ rowIndex, header = false, checked = false, disabled = false }) => {
1068
- const baseClasses = "absolute inset-block-[6px] inline-end-[6px] ch-checkbox";
1069
- const { attributes } = tableControls.checkbox;
1070
- return `<input type="checkbox" class="${baseClasses}" ${attributes.checkbox}="${rowIndex}" ${header ? attributes.header : ""} ${checked ? "checked" : ""} ${disabled ? "disabled" : ""} data-testid="table-selection"/>`;
1071
- };
1072
- var tableControls = {
1073
- checkbox: {
1074
- attributes: checkboxAttributes,
1075
- render: renderCheckbox
681
+ var TableSorting = class {
682
+ constructor(rows, _view, _projection) {
683
+ this._view = _view;
684
+ this._projection = _projection;
685
+ this._displayToDataIndex = /* @__PURE__ */ new Map();
686
+ this._localSort = (0, import_signals_core3.signal)(void 0);
687
+ this._rows = rows;
688
+ this._isDirty = (0, import_signals_core3.computed)(() => {
689
+ const local = this._localSort.value;
690
+ const viewSort = this._view?.query.sort?.[0];
691
+ if (local?.type === "cleared") {
692
+ return viewSort !== void 0;
693
+ }
694
+ if (local?.type === "active") {
695
+ if (!viewSort) {
696
+ return true;
697
+ }
698
+ return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
699
+ }
700
+ return false;
701
+ });
702
+ this.sortedRows = this.initialiseSortedRows();
703
+ }
704
+ /**
705
+ * @reactive
706
+ * @returns Local sort if present, falls back to view sort.
707
+ */
708
+ get sorting() {
709
+ const local = this._localSort.value;
710
+ if (local?.type === "cleared") {
711
+ return void 0;
712
+ }
713
+ if (local?.type === "active") {
714
+ return local.sort;
715
+ }
716
+ return this._view?.query.sort?.[0];
717
+ }
718
+ /**
719
+ * @reactive
720
+ * @returns Whether local sort differs from view sort.
721
+ */
722
+ get isDirty() {
723
+ return this._isDirty.value;
724
+ }
725
+ getDataIndex(displayIndex) {
726
+ return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
727
+ }
728
+ setSort(fieldId, direction) {
729
+ this._localSort.value = {
730
+ type: "active",
731
+ sort: {
732
+ fieldId,
733
+ direction
734
+ }
735
+ };
736
+ }
737
+ toggleSort(fieldId) {
738
+ if (!this._view || !this.sorting || this.sorting.fieldId !== fieldId) {
739
+ return;
740
+ }
741
+ switch (this.sorting.direction) {
742
+ case "asc": {
743
+ this.setSort(fieldId, "desc");
744
+ break;
745
+ }
746
+ case "desc": {
747
+ this.setSort(fieldId, "asc");
748
+ break;
749
+ }
750
+ }
751
+ }
752
+ clearSort() {
753
+ this._localSort.value = {
754
+ type: "cleared"
755
+ };
756
+ }
757
+ save() {
758
+ if (this._view && this._localSort.value !== void 0) {
759
+ if (this._localSort.value.type === "active") {
760
+ this._view.query.sort = [
761
+ this._localSort.value.sort
762
+ ];
763
+ } else {
764
+ this._view.query.sort = [];
765
+ }
766
+ this._localSort.value = void 0;
767
+ }
768
+ }
769
+ //
770
+ // Initialisation.
771
+ //
772
+ initialiseSortedRows() {
773
+ return (0, import_signals_core3.computed)(() => {
774
+ this._displayToDataIndex.clear();
775
+ const sort = this.sorting;
776
+ if (!sort || !this._view) {
777
+ return this._rows.value;
778
+ }
779
+ const field = this._view.fields.find((f) => f.id === sort.fieldId);
780
+ if (!field) {
781
+ return this._rows.value;
782
+ }
783
+ const { props } = this._projection.getFieldProjection(field.id);
784
+ const dataWithIndices = this._rows.value.map((item, index) => {
785
+ const sortValue = this.getSortValue(props, field, item);
786
+ const isEmpty = !(props.type === import_echo_schema5.TypeEnum.Boolean) && (sortValue == null || sortValue === "");
787
+ return {
788
+ item,
789
+ index,
790
+ value: sortValue,
791
+ isEmpty
792
+ };
793
+ });
794
+ const ordered = (0, import_lodash.default)(dataWithIndices, [
795
+ "isEmpty",
796
+ "value"
797
+ ], [
798
+ "asc",
799
+ sort.direction
800
+ ]);
801
+ ordered.forEach(({ index: dataIndex }, displayIndex) => {
802
+ if (displayIndex !== dataIndex) {
803
+ this._displayToDataIndex.set(displayIndex, dataIndex);
804
+ }
805
+ });
806
+ return ordered.map(({ item }) => item);
807
+ });
808
+ }
809
+ getSortValue(props, field, item) {
810
+ const { type, format } = props;
811
+ const value = (0, import_echo_schema5.getValue)(item, field.path);
812
+ if (type === import_echo_schema5.TypeEnum.Boolean) {
813
+ return !!value;
814
+ }
815
+ if (type === import_echo_schema5.TypeEnum.Number) {
816
+ return value;
817
+ }
818
+ if (value == null) {
819
+ return value;
820
+ }
821
+ if (format === import_echo_schema5.FormatEnum.SingleSelect) {
822
+ const options = props.options;
823
+ if (!options) {
824
+ return void 0;
825
+ }
826
+ const option = options.find((option2) => option2.id === value);
827
+ return option ? options.indexOf(option) : void 0;
828
+ }
829
+ if (format === import_echo_schema5.FormatEnum.Ref && field.referencePath) {
830
+ const refValue = (0, import_echo_schema5.getValue)(value.target, field.referencePath);
831
+ return refValue;
832
+ }
833
+ return (0, import_react_ui_form5.formatForDisplay)({
834
+ type,
835
+ format,
836
+ value
837
+ });
1076
838
  }
1077
839
  };
1078
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
840
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
1079
841
  var TableModel = class extends import_context.Resource {
1080
842
  constructor({ table, projection, sorting = [], pinnedRows = {
1081
843
  top: [],
@@ -1093,203 +855,18 @@ var TableModel = class extends import_context.Resource {
1093
855
  col: 0
1094
856
  }
1095
857
  });
1096
- this._displayToDataIndex = /* @__PURE__ */ new Map();
1097
- this._sorting = (0, import_signals_core.signal)(void 0);
1098
858
  this._rows = (0, import_signals_core.signal)([]);
1099
- this._modalController = new ModalController();
1100
- this.getCells = (range, plane) => {
1101
- switch (plane) {
1102
- case "grid": {
1103
- this._visibleRange.value = range;
1104
- return this.getMainGridCells(range);
1105
- }
1106
- case "frozenRowsStart": {
1107
- return this.getHeaderCells(range);
1108
- }
1109
- case "frozenColsStart": {
1110
- return this.getSelectionColumnCells(range);
1111
- }
1112
- case "frozenColsEnd": {
1113
- return this.getActionColumnCells(range);
1114
- }
1115
- case "fixedStartStart": {
1116
- return this.getSelectAllCell();
1117
- }
1118
- case "fixedStartEnd": {
1119
- return this.getNewColumnCell();
1120
- }
1121
- default: {
1122
- return {};
1123
- }
1124
- }
1125
- };
1126
- this.getMainGridCells = (range) => {
1127
- const cells = {};
1128
- const fields = this._table.view?.fields ?? [];
1129
- const addCell = (obj, field, colIndex, displayIndex) => {
1130
- const { props } = this._projection.getFieldProjection(field.id);
1131
- const cell = {
1132
- get value() {
1133
- const value = (0, import_echo_schema4.getValue)(obj, field.path);
1134
- if (value == null) {
1135
- return "";
1136
- }
1137
- switch (props.format) {
1138
- case import_echo_schema4.FormatEnum.Ref: {
1139
- if (!field.referencePath) {
1140
- return "";
1141
- }
1142
- return (0, import_echo_schema4.getValue)(value, field.referencePath);
1143
- }
1144
- default: {
1145
- return (0, import_react_ui_form4.formatForDisplay)({
1146
- type: props.type,
1147
- format: props.format,
1148
- value
1149
- });
1150
- }
1151
- }
1152
- }
1153
- };
1154
- const classes = [];
1155
- const formatClasses = (0, import_react_ui_form4.cellClassesForFieldType)({
1156
- type: props.type,
1157
- format: props.format
1158
- });
1159
- if (formatClasses) {
1160
- classes.push(formatClasses);
1161
- }
1162
- const rowSelectionClasses = (0, import_react_ui_form4.cellClassesForRowSelection)(this._selection.isObjectSelected(obj));
1163
- if (rowSelectionClasses) {
1164
- classes.push(rowSelectionClasses);
1165
- }
1166
- if (classes.length > 0) {
1167
- cell.className = (0, import_react_ui_theme2.mx)(classes.flat());
1168
- }
1169
- if (cell.value && props.format === import_echo_schema4.FormatEnum.Ref && props.referenceSchema) {
1170
- const targetObj = (0, import_echo_schema4.getValue)(obj, field.path);
1171
- cell.accessoryHtml = tableButtons.referencedCell.render({
1172
- targetId: targetObj.id,
1173
- schemaId: props.referenceSchema
1174
- });
1175
- }
1176
- const idx = (0, import_react_ui_grid4.toPlaneCellIndex)({
1177
- col: colIndex,
1178
- row: displayIndex
1179
- });
1180
- cells[idx] = cell;
1181
- };
1182
- for (let row = range.start.row; row <= range.end.row && row < this._sortedRows.value.length; row++) {
1183
- for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1184
- const field = fields[col];
1185
- if (!field) {
1186
- continue;
1187
- }
1188
- addCell(this._sortedRows.value[row], field, col, row);
1189
- }
1190
- }
1191
- return cells;
1192
- };
1193
- this.getHeaderCells = (range) => {
1194
- const cells = {};
1195
- const fields = this.table.view?.fields ?? [];
1196
- for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1197
- const { field, props } = this._projection.getFieldProjection(fields[col].id);
1198
- cells[(0, import_react_ui_grid4.toPlaneCellIndex)({
1199
- col,
1200
- row: 0
1201
- })] = {
1202
- // TODO(burdon): Use same logic as form for fallback title.
1203
- value: props.title ?? field.path,
1204
- readonly: true,
1205
- resizeHandle: "col",
1206
- accessoryHtml: tableButtons.columnSettings.render({
1207
- fieldId: field.id
1208
- })
1209
- };
1210
- }
1211
- return cells;
1212
- };
1213
- this.getSelectionColumnCells = (range) => {
1214
- const cells = {};
1215
- for (let row = range.start.row; row <= range.end.row && row < this._rows.value.length; row++) {
1216
- const isSelected = this._selection.isRowIndexSelected(row);
1217
- const classes = (0, import_react_ui_form4.cellClassesForRowSelection)(isSelected);
1218
- cells[(0, import_react_ui_grid4.toPlaneCellIndex)({
1219
- col: 0,
1220
- row
1221
- })] = {
1222
- value: "",
1223
- readonly: true,
1224
- className: classes ? (0, import_react_ui_theme2.mx)(classes) : void 0,
1225
- accessoryHtml: tableControls.checkbox.render({
1226
- rowIndex: row,
1227
- checked: isSelected
1228
- })
1229
- };
1230
- }
1231
- return cells;
1232
- };
1233
- this.getActionColumnCells = (range) => {
1234
- const cells = {};
1235
- for (let row = range.start.row; row <= range.end.row && row < this._rows.value.length; row++) {
1236
- const isSelected = this._selection.isRowIndexSelected(row);
1237
- const classes = (0, import_react_ui_form4.cellClassesForRowSelection)(isSelected);
1238
- cells[(0, import_react_ui_grid4.toPlaneCellIndex)({
1239
- col: 0,
1240
- row
1241
- })] = {
1242
- value: "",
1243
- readonly: true,
1244
- className: classes ? (0, import_react_ui_theme2.mx)(classes) : void 0,
1245
- accessoryHtml: tableButtons.rowMenu.render({
1246
- rowIndex: row
1247
- })
1248
- };
1249
- }
1250
- return cells;
1251
- };
1252
- this.getSelectAllCell = () => {
1253
- return {
1254
- [(0, import_react_ui_grid4.toPlaneCellIndex)({
1255
- col: 0,
1256
- row: 0
1257
- })]: {
1258
- value: "",
1259
- accessoryHtml: tableControls.checkbox.render({
1260
- rowIndex: 0,
1261
- header: true,
1262
- checked: this._selection.allRowsSeleted.value
1263
- }),
1264
- readonly: true
1265
- }
1266
- };
1267
- };
1268
- this.getNewColumnCell = () => {
1269
- return {
1270
- [(0, import_react_ui_grid4.toPlaneCellIndex)({
1271
- col: 0,
1272
- row: 0
1273
- })]: {
1274
- value: "",
1275
- accessoryHtml: tableButtons.addColumn.render({
1276
- disabled: (this._table.view?.fields?.length ?? 0) >= import_schema.VIEW_FIELD_LIMIT
1277
- }),
1278
- readonly: true
1279
- }
1280
- };
1281
- };
1282
859
  this.setRows = (obj) => {
1283
860
  this._rows.value = obj;
1284
861
  };
1285
862
  this.getRowCount = () => this._rows.value.length;
1286
- this.getColumnCount = () => this.table.view?.fields.length ?? 0;
863
+ this.getColumnCount = () => this.table.view?.target?.fields.length ?? 0;
1287
864
  this.insertRow = (rowIndex) => {
1288
- const row = rowIndex !== void 0 ? this._displayToDataIndex.get(rowIndex) ?? rowIndex : this._rows.value.length;
865
+ const row = rowIndex !== void 0 ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
1289
866
  this._onInsertRow?.(row);
1290
867
  };
1291
868
  this.deleteRow = (rowIndex) => {
1292
- const row = this._displayToDataIndex.get(rowIndex) ?? rowIndex;
869
+ const row = this._sorting.getDataIndex(rowIndex);
1293
870
  const obj = this._rows.value[row];
1294
871
  const objectsToDelete = [];
1295
872
  if (this._selection.hasSelection.value) {
@@ -1301,23 +878,23 @@ var TableModel = class extends import_context.Resource {
1301
878
  this._onDeleteRows?.(row, objectsToDelete);
1302
879
  };
1303
880
  this.getCellData = ({ col, row }) => {
1304
- const fields = this.table.view?.fields ?? [];
881
+ const fields = this.table.view?.target?.fields ?? [];
1305
882
  if (col < 0 || col >= fields.length) {
1306
883
  return void 0;
1307
884
  }
1308
885
  const field = fields[col];
1309
- const dataIndex = this._displayToDataIndex.get(row) ?? row;
1310
- const value = (0, import_echo_schema4.getValue)(this._rows.value[dataIndex], field.path);
886
+ const dataIndex = this._sorting.getDataIndex(row);
887
+ const value = (0, import_echo_schema3.getValue)(this._rows.value[dataIndex], field.path);
1311
888
  if (value == null) {
1312
889
  return "";
1313
890
  }
1314
891
  const { props } = this._projection.getFieldProjection(field.id);
1315
892
  switch (props.format) {
1316
- case import_echo_schema4.FormatEnum.Ref: {
893
+ case import_echo_schema3.FormatEnum.Ref: {
1317
894
  if (!field.referencePath) {
1318
895
  return "";
1319
896
  }
1320
- return (0, import_echo_schema4.getValue)(value, field.referencePath);
897
+ return (0, import_echo_schema3.getValue)(value.target, field.referencePath);
1321
898
  }
1322
899
  default: {
1323
900
  return (0, import_react_ui_form4.formatForEditing)({
@@ -1329,20 +906,22 @@ var TableModel = class extends import_context.Resource {
1329
906
  }
1330
907
  };
1331
908
  this.setCellData = ({ col, row }, value) => {
1332
- const rowIdx = this._displayToDataIndex.get(row) ?? row;
1333
- const fields = this.table.view?.fields ?? [];
909
+ const rowIdx = this._sorting.getDataIndex(row);
910
+ const fields = this.table.view?.target?.fields ?? [];
1334
911
  if (col < 0 || col >= fields.length) {
1335
912
  return;
1336
913
  }
1337
914
  const field = fields[col];
1338
915
  const { props } = this._projection.getFieldProjection(field.id);
1339
916
  switch (props.format) {
1340
- case import_echo_schema4.FormatEnum.Ref: {
1341
- (0, import_echo_schema4.setValue)(this._rows.value[rowIdx], field.path, value);
917
+ case import_echo_schema3.FormatEnum.Ref: {
918
+ if ((0, import_live_object.isReactiveObject)(value)) {
919
+ (0, import_echo_schema3.setValue)(this._rows.value[rowIdx], field.path, (0, import_live_object.makeRef)(value));
920
+ }
1342
921
  break;
1343
922
  }
1344
923
  default: {
1345
- (0, import_echo_schema4.setValue)(this._rows.value[rowIdx], field.path, (0, import_react_ui_form4.parseValue)({
924
+ (0, import_echo_schema3.setValue)(this._rows.value[rowIdx], field.path, (0, import_react_ui_form4.parseValue)({
1346
925
  type: props.type,
1347
926
  format: props.format,
1348
927
  value
@@ -1351,28 +930,13 @@ var TableModel = class extends import_context.Resource {
1351
930
  }
1352
931
  }
1353
932
  };
1354
- this.handleGridClick = (event) => {
1355
- if (!this._modalController.handleClick(event)) {
1356
- const target = event.target;
1357
- const selectionCheckbox = target.closest(`input[${tableControls.checkbox.attributes.checkbox}]`);
1358
- if (selectionCheckbox) {
1359
- const isHeader = selectionCheckbox.hasAttribute(tableControls.checkbox.attributes.header);
1360
- if (isHeader) {
1361
- if (this._selection.allRowsSeleted.value) {
1362
- this._selection.setSelection("none");
1363
- } else {
1364
- this._selection.setSelection("all");
1365
- }
1366
- } else {
1367
- const rowIndex = Number(selectionCheckbox.getAttribute(tableControls.checkbox.attributes.checkbox));
1368
- this._selection.toggleSelectionForRowIndex(rowIndex);
1369
- }
1370
- }
1371
- }
1372
- };
1373
933
  this._table = table;
1374
934
  this._projection = projection;
1375
- this._sorting.value = sorting.at(0);
935
+ this._sorting = new TableSorting(this._rows, table.view?.target, projection);
936
+ if (sorting.length > 0) {
937
+ const [sort] = sorting;
938
+ this._sorting.setSort(sort.fieldId, sort.direction);
939
+ }
1376
940
  this._pinnedRows = pinnedRows;
1377
941
  this._onInsertRow = onInsertRow;
1378
942
  this._onDeleteRows = onDeleteRows;
@@ -1386,13 +950,22 @@ var TableModel = class extends import_context.Resource {
1386
950
  get projection() {
1387
951
  return this._projection;
1388
952
  }
953
+ get rows() {
954
+ return this._sorting.sortedRows;
955
+ }
956
+ /**
957
+ * @reactive
958
+ */
959
+ get sorting() {
960
+ return this._sorting;
961
+ }
1389
962
  get pinnedRows() {
1390
963
  return this._pinnedRows;
1391
964
  }
1392
965
  get columnMeta() {
1393
- (0, import_invariant3.invariant)(this._columnMeta, void 0, {
1394
- F: __dxlog_file5,
1395
- L: 126,
966
+ (0, import_invariant2.invariant)(this._columnMeta, void 0, {
967
+ F: __dxlog_file,
968
+ L: 117,
1396
969
  S: this,
1397
970
  A: [
1398
971
  "this._columnMeta",
@@ -1401,28 +974,25 @@ var TableModel = class extends import_context.Resource {
1401
974
  });
1402
975
  return this._columnMeta;
1403
976
  }
1404
- get sorting() {
1405
- return this._sorting.value;
1406
- }
1407
- get modalController() {
1408
- return this._modalController;
1409
- }
1410
977
  get selection() {
1411
978
  return this._selection;
1412
979
  }
980
+ /** @reactive */
981
+ get isViewDirty() {
982
+ return this._sorting.isDirty;
983
+ }
1413
984
  //
1414
985
  // Initialisation
1415
986
  //
1416
987
  async _open() {
1417
988
  this.initializeColumnMeta();
1418
- this.initializeSorting();
1419
989
  this.initializeEffects();
1420
- this._selection = new SelectionModel(this._sortedRows, () => this._onRowOrderChanged?.());
990
+ this._selection = new SelectionModel(this._sorting.sortedRows, () => this._onRowOrderChanged?.());
1421
991
  await this._selection.open(this._ctx);
1422
992
  }
1423
993
  initializeColumnMeta() {
1424
994
  this._columnMeta = (0, import_signals_core.computed)(() => {
1425
- const fields = this._table.view?.fields ?? [];
995
+ const fields = this._table.view?.target?.fields ?? [];
1426
996
  const meta = Object.fromEntries(fields.map((field, index) => [
1427
997
  index,
1428
998
  {
@@ -1444,124 +1014,926 @@ var TableModel = class extends import_context.Resource {
1444
1014
  resizeable: false
1445
1015
  }
1446
1016
  }
1447
- };
1448
- });
1449
- }
1450
- initializeSorting() {
1451
- this._sortedRows = (0, import_signals_core.computed)(() => {
1452
- this._displayToDataIndex.clear();
1453
- const sort = this._sorting.value;
1454
- if (!sort) {
1455
- return this._rows.value;
1017
+ };
1018
+ });
1019
+ }
1020
+ initializeEffects() {
1021
+ const rowOrderWatcher = (0, import_signals_core.effect)(() => {
1022
+ touch(this._sorting.sortedRows.value);
1023
+ this._onRowOrderChanged?.();
1024
+ });
1025
+ this._ctx.onDispose(rowOrderWatcher);
1026
+ const rowEffectManager = (0, import_signals_core.effect)(() => {
1027
+ const { start, end } = touch(this._visibleRange.value);
1028
+ const rowEffects = [];
1029
+ for (let row = start.row; row <= end.row; row++) {
1030
+ rowEffects.push((0, import_signals_core.effect)(() => {
1031
+ const obj = this._sorting.sortedRows.value[row];
1032
+ this?._table?.view?.target?.fields.forEach((field) => touch((0, import_echo_schema3.getValue)(obj, field.path)));
1033
+ this._onCellUpdate?.({
1034
+ row,
1035
+ col: start.col,
1036
+ plane: "grid"
1037
+ });
1038
+ }));
1039
+ }
1040
+ return () => rowEffects.forEach((cleanup) => cleanup());
1041
+ });
1042
+ this._ctx.onDispose(rowEffectManager);
1043
+ }
1044
+ /**
1045
+ * Updates a cell's value using a transform function.
1046
+ * @param {DxGridPlanePosition} position - The position of the cell to update.
1047
+ * @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
1048
+ */
1049
+ updateCellData({ col, row }, update) {
1050
+ const dataRow = this._sorting.getDataIndex(row);
1051
+ const fields = this.table.view?.target?.fields ?? [];
1052
+ const field = fields[col];
1053
+ const value = (0, import_echo_schema3.getValue)(this._rows.value[dataRow], field.path);
1054
+ const updatedValue = update(value);
1055
+ (0, import_echo_schema3.setValue)(this._rows.value[dataRow], field.path, updatedValue);
1056
+ }
1057
+ //
1058
+ // Column Operations
1059
+ //
1060
+ deleteColumn(fieldId) {
1061
+ if (!this.table.view) {
1062
+ return;
1063
+ }
1064
+ const field = this.table.view.target?.fields.find((field2) => field2.id === fieldId);
1065
+ if (field && this._onDeleteColumn) {
1066
+ this._onDeleteColumn(field.id);
1067
+ }
1068
+ }
1069
+ //
1070
+ // Resize
1071
+ //
1072
+ setColumnWidth(columnIndex, width) {
1073
+ const fields = this.table.view?.target?.fields ?? [];
1074
+ if (columnIndex < fields.length) {
1075
+ const newWidth = Math.max(0, width);
1076
+ const field = fields[columnIndex];
1077
+ if (field) {
1078
+ field.size = newWidth;
1079
+ }
1080
+ }
1081
+ }
1082
+ //
1083
+ // Pinning
1084
+ //
1085
+ // TODO(burdon): Change to setPinned(on/off).
1086
+ pinRow(rowIndex, side) {
1087
+ this._pinnedRows[side].push(rowIndex);
1088
+ }
1089
+ unpinRow(rowIndex) {
1090
+ this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
1091
+ this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
1092
+ }
1093
+ //
1094
+ // View operations
1095
+ //
1096
+ saveView() {
1097
+ this._sorting.save();
1098
+ }
1099
+ };
1100
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
1101
+ var ModalController = class {
1102
+ constructor() {
1103
+ this._state = (0, import_signals_core4.signal)({
1104
+ type: "closed"
1105
+ });
1106
+ this._triggerRef = {
1107
+ current: null
1108
+ };
1109
+ this.setTrigger = (trigger) => {
1110
+ this._triggerRef.current = trigger;
1111
+ };
1112
+ this.showRowMenu = (rowIndex) => {
1113
+ this._state.value = {
1114
+ type: "row",
1115
+ rowIndex
1116
+ };
1117
+ };
1118
+ this.showColumnMenu = (fieldId) => {
1119
+ this._state.value = {
1120
+ type: "column",
1121
+ fieldId
1122
+ };
1123
+ };
1124
+ this.showColumnCreator = () => {
1125
+ this._state.value = {
1126
+ type: "columnSettings",
1127
+ mode: {
1128
+ type: "create"
1129
+ }
1130
+ };
1131
+ };
1132
+ this.showReferencePanel = (targetId, schemaId) => {
1133
+ this._state.value = {
1134
+ type: "refPanel",
1135
+ targetId,
1136
+ typename: schemaId
1137
+ };
1138
+ };
1139
+ this.openColumnSettings = () => {
1140
+ if (this._state.value.type === "column") {
1141
+ const fieldId = this._state.value.fieldId;
1142
+ requestAnimationFrame(() => {
1143
+ this._state.value = {
1144
+ type: "columnSettings",
1145
+ mode: {
1146
+ type: "edit",
1147
+ fieldId
1148
+ }
1149
+ };
1150
+ });
1151
+ }
1152
+ };
1153
+ this.openCreateRef = (typename, anchorCell, initialValues, onCreate) => {
1154
+ if (anchorCell) {
1155
+ this._triggerRef.current = anchorCell;
1156
+ this._state.value = {
1157
+ type: "createRefPanel",
1158
+ typename,
1159
+ initialValues,
1160
+ onCreate
1161
+ };
1162
+ } else {
1163
+ import_log.log.warn("anchor cell not found while creating new ref", void 0, {
1164
+ F: __dxlog_file2,
1165
+ L: 86,
1166
+ S: this,
1167
+ C: (f, a) => f(...a)
1168
+ });
1169
+ }
1170
+ };
1171
+ this.close = () => {
1172
+ this._state.value = {
1173
+ type: "closed"
1174
+ };
1175
+ };
1176
+ }
1177
+ get state() {
1178
+ return this._state;
1179
+ }
1180
+ get trigger() {
1181
+ return this._triggerRef;
1182
+ }
1183
+ };
1184
+ var TablePresentation = class {
1185
+ constructor(model, _visibleRange = (0, import_signals_core5.signal)({
1186
+ start: {
1187
+ row: 0,
1188
+ col: 0
1189
+ },
1190
+ end: {
1191
+ row: 0,
1192
+ col: 0
1193
+ }
1194
+ })) {
1195
+ this.model = model;
1196
+ this._visibleRange = _visibleRange;
1197
+ }
1198
+ getCells(range, plane) {
1199
+ switch (plane) {
1200
+ case "grid":
1201
+ this._visibleRange.value = range;
1202
+ return this.getMainGridCells(range);
1203
+ case "frozenRowsStart":
1204
+ return this.getHeaderCells(range);
1205
+ case "frozenColsStart":
1206
+ return this.getSelectionColumnCells(range);
1207
+ case "frozenColsEnd":
1208
+ return this.getActionColumnCells(range);
1209
+ case "fixedStartStart":
1210
+ return this.getSelectAllCell();
1211
+ case "fixedStartEnd":
1212
+ return this.getNewColumnCell();
1213
+ default:
1214
+ return {};
1215
+ }
1216
+ }
1217
+ getMainGridCells(range) {
1218
+ const cells = {};
1219
+ const fields = this.model.table.view?.target?.fields ?? [];
1220
+ const addCell = (obj, field, colIndex, displayIndex) => {
1221
+ const { props } = this.model.projection.getFieldProjection(field.id);
1222
+ const cell = {
1223
+ get value() {
1224
+ const value = (0, import_echo_schema6.getValue)(obj, field.path);
1225
+ if (value == null) {
1226
+ return "";
1227
+ }
1228
+ switch (props.format) {
1229
+ case import_echo_schema6.FormatEnum.Boolean:
1230
+ case import_echo_schema6.FormatEnum.SingleSelect: {
1231
+ return "";
1232
+ }
1233
+ case import_echo_schema6.FormatEnum.Ref: {
1234
+ if (!field.referencePath) {
1235
+ return "";
1236
+ }
1237
+ return (0, import_echo_schema6.getValue)(value.target, field.referencePath);
1238
+ }
1239
+ default: {
1240
+ return (0, import_react_ui_form6.formatForDisplay)({
1241
+ type: props.type,
1242
+ format: props.format,
1243
+ value
1244
+ });
1245
+ }
1246
+ }
1247
+ }
1248
+ };
1249
+ const classes = [];
1250
+ const formatClasses = (0, import_react_ui_form6.cellClassesForFieldType)({
1251
+ type: props.type,
1252
+ format: props.format
1253
+ });
1254
+ if (formatClasses) {
1255
+ classes.push(formatClasses);
1256
+ }
1257
+ const rowSelectionClasses = (0, import_react_ui_form6.cellClassesForRowSelection)(this.model.selection.isObjectSelected(obj));
1258
+ if (rowSelectionClasses) {
1259
+ classes.push(rowSelectionClasses);
1260
+ }
1261
+ if (classes.length > 0) {
1262
+ cell.className = (0, import_react_ui_theme2.mx)(classes.flat());
1263
+ }
1264
+ if (props.format === import_echo_schema6.FormatEnum.Ref && props.referenceSchema) {
1265
+ const targetObj = (0, import_echo_schema6.getValue)(obj, field.path)?.target;
1266
+ if (targetObj) {
1267
+ cell.accessoryHtml = tableButtons.referencedCell.render({
1268
+ targetId: targetObj.id,
1269
+ schemaId: props.referenceSchema
1270
+ });
1271
+ }
1272
+ }
1273
+ if (props.format === import_echo_schema6.FormatEnum.Boolean) {
1274
+ const value = (0, import_echo_schema6.getValue)(obj, field.path);
1275
+ cell.accessoryHtml = tableControls.switch.render({
1276
+ colIndex,
1277
+ rowIndex: displayIndex,
1278
+ checked: value ?? false
1279
+ });
1280
+ cell.readonly = true;
1281
+ }
1282
+ if (props.format === import_echo_schema6.FormatEnum.SingleSelect) {
1283
+ const value = (0, import_echo_schema6.getValue)(obj, field.path);
1284
+ const options = this.model.projection.getFieldProjection(field.id).props.options;
1285
+ if (options) {
1286
+ const option = options.find((o) => o.id === value);
1287
+ if (option) {
1288
+ cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
1289
+ }
1290
+ }
1291
+ }
1292
+ const idx = (0, import_react_ui_grid3.toPlaneCellIndex)({
1293
+ col: colIndex,
1294
+ row: displayIndex
1295
+ });
1296
+ cells[idx] = cell;
1297
+ };
1298
+ for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
1299
+ for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1300
+ const field = fields[col];
1301
+ if (!field) {
1302
+ continue;
1303
+ }
1304
+ addCell(this.model.rows.value[row], field, col, row);
1305
+ }
1306
+ }
1307
+ return cells;
1308
+ }
1309
+ getHeaderCells(range) {
1310
+ const cells = {};
1311
+ const fields = this.model.table.view?.target?.fields ?? [];
1312
+ for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1313
+ const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
1314
+ const sorting = this.model.sorting?.sorting;
1315
+ const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
1316
+ cells[(0, import_react_ui_grid3.toPlaneCellIndex)({
1317
+ col,
1318
+ row: 0
1319
+ })] = {
1320
+ // TODO(burdon): Use same logic as form for fallback title.
1321
+ value: props.title ?? field.path,
1322
+ readonly: true,
1323
+ resizeHandle: "col",
1324
+ accessoryHtml: `
1325
+ ${direction !== void 0 ? tableButtons.sort.render({
1326
+ fieldId: field.id,
1327
+ direction
1328
+ }) : ""}
1329
+ ${tableButtons.columnSettings.render({
1330
+ fieldId: field.id
1331
+ })}
1332
+ `,
1333
+ className: "!bg-gridHeader"
1334
+ };
1335
+ }
1336
+ return cells;
1337
+ }
1338
+ getSelectionColumnCells(range) {
1339
+ const cells = {};
1340
+ for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
1341
+ const isSelected = this.model.selection.isRowIndexSelected(row);
1342
+ const classes = (0, import_react_ui_form6.cellClassesForRowSelection)(isSelected);
1343
+ cells[(0, import_react_ui_grid3.toPlaneCellIndex)({
1344
+ col: 0,
1345
+ row
1346
+ })] = {
1347
+ value: "",
1348
+ readonly: true,
1349
+ className: classes ? (0, import_react_ui_theme2.mx)(classes) : void 0,
1350
+ accessoryHtml: tableControls.checkbox.render({
1351
+ rowIndex: row,
1352
+ checked: isSelected
1353
+ })
1354
+ };
1355
+ }
1356
+ return cells;
1357
+ }
1358
+ getActionColumnCells(range) {
1359
+ const cells = {};
1360
+ for (let row = range.start.row; row <= range.end.row && row < this.model.getRowCount(); row++) {
1361
+ const isSelected = this.model.selection.isRowIndexSelected(row);
1362
+ const classes = (0, import_react_ui_form6.cellClassesForRowSelection)(isSelected);
1363
+ cells[(0, import_react_ui_grid3.toPlaneCellIndex)({
1364
+ col: 0,
1365
+ row
1366
+ })] = {
1367
+ value: "",
1368
+ readonly: true,
1369
+ className: classes ? (0, import_react_ui_theme2.mx)(classes) : void 0,
1370
+ accessoryHtml: tableButtons.rowMenu.render({
1371
+ rowIndex: row
1372
+ })
1373
+ };
1374
+ }
1375
+ return cells;
1376
+ }
1377
+ getSelectAllCell() {
1378
+ return {
1379
+ [(0, import_react_ui_grid3.toPlaneCellIndex)({
1380
+ col: 0,
1381
+ row: 0
1382
+ })]: {
1383
+ accessoryHtml: tableControls.checkbox.render({
1384
+ rowIndex: 0,
1385
+ header: true,
1386
+ checked: this.model.selection.allRowsSeleted.value
1387
+ }),
1388
+ className: "!bg-gridHeader",
1389
+ readonly: true,
1390
+ value: ""
1391
+ }
1392
+ };
1393
+ }
1394
+ getNewColumnCell() {
1395
+ return {
1396
+ [(0, import_react_ui_grid3.toPlaneCellIndex)({
1397
+ col: 0,
1398
+ row: 0
1399
+ })]: {
1400
+ accessoryHtml: tableButtons.addColumn.render({
1401
+ disabled: (this.model.table.view?.target?.fields?.length ?? 0) >= import_schema2.VIEW_FIELD_LIMIT
1402
+ }),
1403
+ className: "!bg-gridHeader",
1404
+ readonly: true,
1405
+ value: ""
1406
+ }
1407
+ };
1408
+ }
1409
+ };
1410
+ var completion = ({ debug, onQuery, onMatch }) => {
1411
+ return [
1412
+ import_view.EditorView.theme({
1413
+ ".cm-completionDialog": {
1414
+ width: "var(--dx-gridCellWidth)"
1415
+ }
1416
+ }),
1417
+ (0, import_autocomplete.autocompletion)({
1418
+ icons: false,
1419
+ activateOnTyping: true,
1420
+ closeOnBlur: !debug,
1421
+ override: [
1422
+ async (context) => {
1423
+ const text = context.state.doc.toString();
1424
+ return {
1425
+ from: 0,
1426
+ options: await onQuery(text)
1427
+ };
1428
+ }
1429
+ ],
1430
+ // TODO(burdon): Consts from grid (to line-up with grid).
1431
+ tooltipClass: () => "cm-completionDialog !mt-[8px] !-ml-[4px] [&>ul]:!max-h-[264px]",
1432
+ optionClass: () => "flex h-[33px] items-center"
1433
+ }),
1434
+ // Check for accepted completion metadata in the transaction.
1435
+ import_view.EditorView.updateListener.of((update) => {
1436
+ update.transactions.forEach((tr) => {
1437
+ const completion2 = tr.annotation(import_autocomplete.pickedCompletion);
1438
+ if (completion2) {
1439
+ onMatch(completion2.data);
1440
+ }
1441
+ });
1442
+ })
1443
+ ];
1444
+ };
1445
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
1446
+ var newValue = Symbol.for("newValue");
1447
+ var createOption = (text) => ({
1448
+ [newValue]: true,
1449
+ text
1450
+ });
1451
+ var isCreateOption = (data) => typeof data === "object" && data[newValue];
1452
+ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }) => {
1453
+ const { id: gridId, editing, setEditing } = (0, import_react_ui_grid4.useGridContext)("TableCellEditor", __gridScope);
1454
+ const suppressNextBlur = (0, import_react7.useRef)(false);
1455
+ const fieldProjection = (0, import_react7.useMemo)(() => {
1456
+ if (!model || !editing) {
1457
+ return;
1458
+ }
1459
+ const { col } = (0, import_react_ui_grid4.parseCellIndex)(editing.index);
1460
+ const field = model.projection.view.fields[col];
1461
+ const fieldProjection2 = model.projection.getFieldProjection(field.id);
1462
+ (0, import_invariant3.invariant)(fieldProjection2, void 0, {
1463
+ F: __dxlog_file3,
1464
+ L: 67,
1465
+ S: void 0,
1466
+ A: [
1467
+ "fieldProjection",
1468
+ ""
1469
+ ]
1470
+ });
1471
+ return fieldProjection2;
1472
+ }, [
1473
+ model,
1474
+ editing
1475
+ ]);
1476
+ const handleEnter = (0, import_react7.useCallback)((value) => {
1477
+ if (!model || !editing) {
1478
+ return;
1479
+ }
1480
+ const cell = (0, import_react_ui_grid4.parseCellIndex)(editing.index);
1481
+ model.setCellData(cell, value);
1482
+ onEnter?.(cell);
1483
+ onFocus?.();
1484
+ setEditing(null);
1485
+ }, [
1486
+ model,
1487
+ editing
1488
+ ]);
1489
+ const handleBlur = (0, import_react7.useCallback)((value) => {
1490
+ if (!model || !editing) {
1491
+ return;
1492
+ }
1493
+ if (suppressNextBlur.current) {
1494
+ suppressNextBlur.current = false;
1495
+ return;
1496
+ }
1497
+ const cell = (0, import_react_ui_grid4.parseCellIndex)(editing.index);
1498
+ if (value !== void 0) {
1499
+ model.setCellData(cell, value);
1500
+ }
1501
+ }, [
1502
+ model,
1503
+ editing
1504
+ ]);
1505
+ const handleClose = (0, import_react7.useCallback)((value, event) => {
1506
+ if (!model || !editing || !fieldProjection) {
1507
+ return;
1508
+ }
1509
+ const cell = (0, import_react_ui_grid4.parseCellIndex)(editing.index);
1510
+ onEnter?.(cell);
1511
+ if (event && onFocus) {
1512
+ onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
1513
+ }
1514
+ }, [
1515
+ model,
1516
+ editing,
1517
+ onFocus,
1518
+ onEnter,
1519
+ fieldProjection,
1520
+ determineNavigationAxis,
1521
+ determineNavigationDelta
1522
+ ]);
1523
+ const extension = (0, import_react7.useMemo)(() => {
1524
+ if (!fieldProjection) {
1525
+ return [];
1526
+ }
1527
+ const extension2 = [
1528
+ (0, import_react_ui_grid4.editorKeys)({
1529
+ onClose: handleClose,
1530
+ ...editing?.initialContent && {
1531
+ onNav: handleClose
1532
+ }
1533
+ })
1534
+ ];
1535
+ if (onQuery) {
1536
+ switch (fieldProjection.props.format) {
1537
+ case import_echo_schema7.FormatEnum.Ref: {
1538
+ extension2.push([
1539
+ completion({
1540
+ onQuery: (text) => onQuery(fieldProjection, text),
1541
+ onMatch: (data) => {
1542
+ if (model && editing && modals) {
1543
+ if (isCreateOption(data)) {
1544
+ const { field, props } = fieldProjection;
1545
+ if (props.referenceSchema) {
1546
+ suppressNextBlur.current = true;
1547
+ modals.openCreateRef(props.referenceSchema, document.querySelector((0, import_react_ui_grid4.cellQuery)(editing.index, gridId)), {
1548
+ [field.referencePath]: data.text
1549
+ }, (data2) => {
1550
+ handleEnter(data2);
1551
+ });
1552
+ }
1553
+ } else {
1554
+ handleEnter(data);
1555
+ }
1556
+ }
1557
+ }
1558
+ })
1559
+ ]);
1560
+ break;
1561
+ }
1562
+ }
1563
+ }
1564
+ return extension2;
1565
+ }, [
1566
+ model,
1567
+ modals,
1568
+ editing,
1569
+ fieldProjection,
1570
+ handleClose
1571
+ ]);
1572
+ const getCellContent = (0, import_react7.useCallback)(() => {
1573
+ if (model && editing) {
1574
+ const value = model.getCellData((0, import_react_ui_grid4.parseCellIndex)(editing.index));
1575
+ return value !== void 0 ? String(value) : "";
1576
+ }
1577
+ }, [
1578
+ model,
1579
+ editing
1580
+ ]);
1581
+ return /* @__PURE__ */ import_react7.default.createElement(import_react_ui_grid4.GridCellEditor, {
1582
+ extension,
1583
+ getCellContent,
1584
+ onBlur: handleBlur
1585
+ });
1586
+ };
1587
+ var determineNavigationAxis = ({ key }) => {
1588
+ switch (key) {
1589
+ case "ArrowUp":
1590
+ case "ArrowDown":
1591
+ case "Enter": {
1592
+ return "row";
1593
+ }
1594
+ case "ArrowLeft":
1595
+ case "ArrowRight":
1596
+ case "Tab":
1597
+ return "col";
1598
+ }
1599
+ };
1600
+ var determineNavigationDelta = ({ key, shift }) => {
1601
+ switch (key) {
1602
+ case "ArrowUp":
1603
+ case "ArrowLeft":
1604
+ return -1;
1605
+ case "ArrowDown":
1606
+ case "ArrowRight":
1607
+ return 1;
1608
+ }
1609
+ return shift ? -1 : 1;
1610
+ };
1611
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
1612
+ var inlineEndLine = "[&>.dx-grid]:relative [&>.dx-grid]:after:absolute [&>.dx-grid]:after:inset-block-0 [&>.dx-grid]:after:-inline-end-px [&>.dx-grid]:after:is-px [&>.dx-grid]:after:bg-separator";
1613
+ var blockEndLine = "[&>.dx-grid]:before:absolute [&>.dx-grid]:before:inset-inline-0 [&>.dx-grid]:before:-block-end-px [&>.dx-grid]:before:bs-px [&>.dx-grid]:before:bg-separator";
1614
+ var frozen = {
1615
+ frozenRowsStart: 1,
1616
+ frozenColsStart: 1,
1617
+ frozenColsEnd: 1
1618
+ };
1619
+ var TableRoot = ({ children, role }) => {
1620
+ return /* @__PURE__ */ import_react.default.createElement("div", {
1621
+ role: "none",
1622
+ className: (0, import_react_ui_theme.mx)("relative border-bs !border-separator", role === "section" ? "attention-surface overflow-hidden [&_.dx-grid]:max-is-[--dx-grid-content-inline-size]" : "flex flex-col [&_.dx-grid]:grow [&_.dx-grid]:max-is-[--dx-grid-content-inline-size] [&_.dx-grid]:bs-0 [&_.dx-grid]:max-bs-[--dx-grid-content-block-size]")
1623
+ }, children);
1624
+ };
1625
+ var TableMain = /* @__PURE__ */ (0, import_react.forwardRef)(({ model, presentation, ignoreAttention }, forwardedRef) => {
1626
+ const [dxGrid, setDxGrid] = (0, import_react.useState)(null);
1627
+ const { hasAttention } = (0, import_react_ui_attention.useAttention)(model?.table ? (0, import_echo.fullyQualifiedId)(model.table) : "table");
1628
+ const { t } = (0, import_react_ui.useTranslation)(translationKey);
1629
+ const modals = (0, import_react.useMemo)(() => new ModalController(), []);
1630
+ (0, import_react.useImperativeHandle)(forwardedRef, () => {
1631
+ if (!presentation || !dxGrid) {
1632
+ return {};
1633
+ }
1634
+ dxGrid.getCells = (range, plane) => presentation.getCells(range, plane);
1635
+ return {
1636
+ update: (cell) => {
1637
+ if (cell) {
1638
+ dxGrid.updateIfWithinBounds(cell);
1639
+ } else {
1640
+ dxGrid.updateCells(true);
1641
+ dxGrid.requestUpdate();
1642
+ }
1643
+ }
1644
+ };
1645
+ }, [
1646
+ presentation,
1647
+ dxGrid
1648
+ ]);
1649
+ const handleGridClick = (0, import_react.useCallback)((event) => {
1650
+ if (!modals) {
1651
+ return;
1652
+ }
1653
+ const buttonSelector = Object.values(tableButtons).map((button) => `button[${button.attr}]`).join(",");
1654
+ const tableButtonElement = event.target.closest(buttonSelector);
1655
+ if (tableButtonElement) {
1656
+ const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
1657
+ if (!button) {
1658
+ return;
1659
+ }
1660
+ modals.setTrigger(tableButtonElement);
1661
+ const data = button.getData(tableButtonElement);
1662
+ switch (data.type) {
1663
+ case "rowMenu": {
1664
+ modals.showRowMenu(data.rowIndex);
1665
+ break;
1666
+ }
1667
+ case "columnSettings": {
1668
+ modals.showColumnMenu(data.fieldId);
1669
+ break;
1670
+ }
1671
+ case "newColumn": {
1672
+ modals.showColumnCreator();
1673
+ break;
1674
+ }
1675
+ case "referencedCell": {
1676
+ modals.showReferencePanel(data.targetId, data.schemaId);
1677
+ break;
1678
+ }
1679
+ case "sort": {
1680
+ model?.sorting?.toggleSort(data.fieldId);
1681
+ break;
1682
+ }
1456
1683
  }
1457
- const field = this._table.view?.fields.find((field2) => field2.id === sort.fieldId);
1458
- if (!field) {
1459
- return this._rows.value;
1684
+ return;
1685
+ }
1686
+ const controlSelector = Object.values(tableControls).map((control) => `input[${control.attr}]`).join(",");
1687
+ const controlElement = event.target.closest(controlSelector);
1688
+ if (controlElement) {
1689
+ const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
1690
+ if (!control) {
1691
+ return;
1460
1692
  }
1461
- const dataWithIndices = this._rows.value.map((item, index) => {
1462
- const value = (0, import_echo_schema4.getValue)(item, field.path);
1463
- return {
1464
- item,
1465
- index,
1466
- value,
1467
- isEmpty: value == null || value === ""
1468
- };
1469
- });
1470
- const sorted = (0, import_lodash.default)(dataWithIndices, [
1471
- "isEmpty",
1472
- "value"
1473
- ], [
1474
- "asc",
1475
- sort.direction
1476
- ]);
1477
- for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
1478
- const { index: dataIndex } = sorted[displayIndex];
1479
- if (displayIndex !== dataIndex) {
1480
- this._displayToDataIndex.set(displayIndex, dataIndex);
1693
+ const data = control.getData(controlElement);
1694
+ switch (data.type) {
1695
+ case "checkbox": {
1696
+ if (data.header) {
1697
+ model?.selection.setSelection(model.selection.allRowsSeleted.value ? "none" : "all");
1698
+ } else {
1699
+ model?.selection.toggleSelectionForRowIndex(data.rowIndex);
1700
+ }
1701
+ break;
1702
+ }
1703
+ case "switch": {
1704
+ if (model) {
1705
+ model.updateCellData({
1706
+ row: data.rowIndex,
1707
+ col: data.colIndex
1708
+ }, (value) => !value);
1709
+ }
1710
+ break;
1481
1711
  }
1482
1712
  }
1483
- return sorted.map(({ item }) => item);
1484
- });
1485
- }
1486
- initializeEffects() {
1487
- const rowOrderWatcher = (0, import_signals_core.effect)(() => {
1488
- touch(this._sortedRows.value);
1489
- this._onRowOrderChanged?.();
1490
- });
1491
- this._ctx.onDispose(rowOrderWatcher);
1492
- const rowEffectManager = (0, import_signals_core.effect)(() => {
1493
- const { start, end } = touch(this._visibleRange.value);
1494
- const rowEffects = [];
1495
- for (let row = start.row; row <= end.row; row++) {
1496
- rowEffects.push((0, import_signals_core.effect)(() => {
1497
- const obj = this._sortedRows.value[row];
1498
- this?._table?.view?.fields.forEach((field) => touch((0, import_echo_schema4.getValue)(obj, field.path)));
1499
- this._onCellUpdate?.({
1500
- row,
1501
- col: start.col,
1502
- plane: "grid"
1503
- });
1504
- }));
1713
+ }
1714
+ }, [
1715
+ model,
1716
+ modals
1717
+ ]);
1718
+ const handleFocus = (0, import_react.useCallback)((increment, delta) => {
1719
+ if (dxGrid) {
1720
+ dxGrid.refocus(increment, delta);
1721
+ }
1722
+ }, [
1723
+ dxGrid
1724
+ ]);
1725
+ const handleEnter = (0, import_react.useCallback)((cell) => {
1726
+ if (model && cell.row === model.getRowCount() - 1) {
1727
+ model.insertRow(cell.row);
1728
+ if (dxGrid) {
1729
+ requestAnimationFrame(() => {
1730
+ dxGrid?.scrollToRow(cell.row + 1);
1731
+ dxGrid?.refocus("row", 1);
1732
+ });
1505
1733
  }
1506
- return () => rowEffects.forEach((cleanup) => cleanup());
1507
- });
1508
- this._ctx.onDispose(rowEffectManager);
1509
- }
1510
- //
1511
- // Column Operations
1512
- //
1513
- deleteColumn(fieldId) {
1514
- if (!this.table.view) {
1734
+ }
1735
+ }, [
1736
+ model,
1737
+ dxGrid
1738
+ ]);
1739
+ const handleKeyDown = (0, import_react.useCallback)((event) => {
1740
+ const cell = (0, import_react_ui_grid.closestCell)(event.target);
1741
+ if (!model || !cell) {
1515
1742
  return;
1516
1743
  }
1517
- const field = this.table.view.fields.find((field2) => field2.id === fieldId);
1518
- if (field && this._onDeleteColumn) {
1519
- if (this._sorting.value?.fieldId === fieldId) {
1520
- this.clearSort();
1744
+ switch (event.key) {
1745
+ case "Backspace":
1746
+ case "Delete": {
1747
+ model.setCellData(cell, void 0);
1748
+ break;
1521
1749
  }
1522
- this._onDeleteColumn(field.id);
1523
1750
  }
1524
- }
1525
- //
1526
- // Resize
1527
- //
1528
- setColumnWidth(columnIndex, width) {
1529
- const fields = this.table.view?.fields ?? [];
1530
- if (columnIndex < fields.length) {
1531
- const newWidth = Math.max(0, width);
1532
- const field = fields[columnIndex];
1533
- if (field) {
1534
- field.size = newWidth;
1751
+ }, [
1752
+ model
1753
+ ]);
1754
+ const handleAxisResize = (0, import_react.useCallback)((event) => {
1755
+ if (event.axis === "col") {
1756
+ const columnIndex = parseInt(event.index, 10);
1757
+ model?.setColumnWidth(columnIndex, event.size);
1758
+ }
1759
+ }, [
1760
+ model
1761
+ ]);
1762
+ const handleWheel = (0, import_react.useCallback)((event) => {
1763
+ if (!ignoreAttention && !hasAttention) {
1764
+ event.stopPropagation();
1765
+ }
1766
+ }, [
1767
+ hasAttention,
1768
+ ignoreAttention
1769
+ ]);
1770
+ const handleNewColumn = (0, import_react.useCallback)(() => {
1771
+ const columns = model?.getColumnCount();
1772
+ if (dxGrid && columns) {
1773
+ dxGrid.scrollToColumn(columns - 1);
1774
+ }
1775
+ }, [
1776
+ model,
1777
+ dxGrid
1778
+ ]);
1779
+ const handleQuery = (0, import_react.useCallback)(async ({ field, props }, text) => {
1780
+ if (model && props.referenceSchema && field.referencePath) {
1781
+ const space = (0, import_echo.getSpace)(model.table);
1782
+ (0, import_invariant.invariant)(space, void 0, {
1783
+ F: __dxlog_file4,
1784
+ L: 264,
1785
+ S: void 0,
1786
+ A: [
1787
+ "space",
1788
+ ""
1789
+ ]
1790
+ });
1791
+ const schema = space.db.schemaRegistry.getSchema(props.referenceSchema);
1792
+ if (schema) {
1793
+ const { objects } = await space.db.query(import_echo.Filter.schema(schema)).run();
1794
+ const options = objects.map((obj) => {
1795
+ const value = (0, import_echo_schema.getValue)(obj, field.referencePath);
1796
+ if (!value || typeof value !== "string") {
1797
+ return void 0;
1798
+ }
1799
+ return {
1800
+ label: value,
1801
+ data: obj
1802
+ };
1803
+ }).filter(import_util.isNotFalsy);
1804
+ return [
1805
+ ...options,
1806
+ {
1807
+ label: t("create new object label", {
1808
+ text
1809
+ }),
1810
+ data: createOption(text)
1811
+ }
1812
+ ];
1535
1813
  }
1536
1814
  }
1815
+ return [];
1816
+ }, [
1817
+ model
1818
+ ]);
1819
+ if (!model || !modals) {
1820
+ return /* @__PURE__ */ import_react.default.createElement("span", {
1821
+ role: "none",
1822
+ className: "attention-surface"
1823
+ });
1537
1824
  }
1538
- //
1539
- // Sorting
1540
- //
1541
- setSort(fieldId, direction) {
1542
- this._sorting.value = {
1543
- fieldId,
1544
- direction
1545
- };
1546
- }
1547
- clearSort() {
1548
- this._sorting.value = void 0;
1549
- }
1550
- //
1551
- // Pinning
1552
- //
1553
- // TODO(burdon): Change to setPinned(on/off).
1554
- pinRow(rowIndex, side) {
1555
- this._pinnedRows[side].push(rowIndex);
1556
- }
1557
- unpinRow(rowIndex) {
1558
- this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
1559
- this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
1560
- }
1825
+ return /* @__PURE__ */ import_react.default.createElement(import_react_ui_grid.Grid.Root, {
1826
+ id: model.table.id ?? "table-grid"
1827
+ }, /* @__PURE__ */ import_react.default.createElement(TableCellEditor, {
1828
+ model,
1829
+ modals,
1830
+ onEnter: handleEnter,
1831
+ onFocus: handleFocus,
1832
+ onQuery: handleQuery
1833
+ }), /* @__PURE__ */ import_react.default.createElement(import_react_ui_grid.Grid.Content, {
1834
+ onWheelCapture: handleWheel,
1835
+ className: (0, import_react_ui_theme.mx)("[--dx-grid-base:var(--surface-bg)]", inlineEndLine, blockEndLine),
1836
+ frozen,
1837
+ columns: model.columnMeta.value,
1838
+ limitRows: model.getRowCount() ?? 0,
1839
+ limitColumns: model.table.view?.target?.fields?.length ?? 0,
1840
+ onAxisResize: handleAxisResize,
1841
+ onClick: handleGridClick,
1842
+ onKeyDown: handleKeyDown,
1843
+ overscroll: "trap",
1844
+ ref: setDxGrid
1845
+ }), /* @__PURE__ */ import_react.default.createElement(RowActionsMenu, {
1846
+ model,
1847
+ modals
1848
+ }), /* @__PURE__ */ import_react.default.createElement(ColumnActionsMenu, {
1849
+ model,
1850
+ modals
1851
+ }), /* @__PURE__ */ import_react.default.createElement(ColumnSettings, {
1852
+ model,
1853
+ modals,
1854
+ onNewColumn: handleNewColumn
1855
+ }), /* @__PURE__ */ import_react.default.createElement(RefPanel, {
1856
+ model,
1857
+ modals
1858
+ }), /* @__PURE__ */ import_react.default.createElement(CreateRefPanel, {
1859
+ model,
1860
+ modals
1861
+ }));
1862
+ });
1863
+ var Table = {
1864
+ Root: TableRoot,
1865
+ Main: TableMain
1866
+ };
1867
+ var createTableToolbarActions = (state) => {
1868
+ const add = (0, import_react_ui_menu.createMenuAction)("add-row", {
1869
+ type: "add-row",
1870
+ icon: "ph--plus--regular",
1871
+ label: [
1872
+ "add row",
1873
+ {
1874
+ ns: translationKey
1875
+ }
1876
+ ],
1877
+ testId: "table.toolbar.add-row"
1878
+ });
1879
+ const save = (0, import_react_ui_menu.createMenuAction)("save-view", {
1880
+ type: "save-view",
1881
+ icon: "ph--floppy-disk--regular",
1882
+ label: [
1883
+ "save view label",
1884
+ {
1885
+ ns: translationKey
1886
+ }
1887
+ ],
1888
+ testId: "table.toolbar.save-view",
1889
+ iconOnly: false,
1890
+ hidden: !state.viewDirty
1891
+ });
1892
+ const gap = (0, import_react_ui_menu.createGapSeparator)();
1893
+ const comment = (0, import_react_ui_menu.createMenuAction)("comment", {
1894
+ type: "comment",
1895
+ icon: "ph--chat-text--regular",
1896
+ label: [
1897
+ "create comment",
1898
+ {
1899
+ ns: translationKey
1900
+ }
1901
+ ],
1902
+ testId: "table.toolbar.comment"
1903
+ });
1904
+ const nodes = [
1905
+ add,
1906
+ save,
1907
+ ...gap.nodes,
1908
+ comment
1909
+ ];
1910
+ return {
1911
+ nodes,
1912
+ edges: nodes.map(({ id: target }) => ({
1913
+ source: "root",
1914
+ target
1915
+ }))
1916
+ };
1917
+ };
1918
+ var TableToolbar = ({ classNames, viewDirty, attendableId, onAction }) => {
1919
+ const state = (0, import_react8.useMemo)(() => (0, import_live_object2.create)({
1920
+ viewDirty
1921
+ }), []);
1922
+ const actionsCreator = (0, import_react8.useCallback)(() => createTableToolbarActions(state), [
1923
+ state
1924
+ ]);
1925
+ const menu = (0, import_react_ui_menu.useMenuActions)(actionsCreator);
1926
+ return /* @__PURE__ */ import_react8.default.createElement(import_react_ui_menu.MenuProvider, {
1927
+ ...menu,
1928
+ attendableId,
1929
+ onAction
1930
+ }, /* @__PURE__ */ import_react8.default.createElement(import_react_ui_menu.ToolbarMenu, {
1931
+ classNames
1932
+ }));
1561
1933
  };
1562
1934
  var useTableModel = ({ objects, table, projection, ...props }) => {
1563
- const [model, setModel] = (0, import_react10.useState)();
1564
- (0, import_react10.useEffect)(() => {
1935
+ const [model, setModel] = (0, import_react9.useState)();
1936
+ (0, import_react9.useEffect)(() => {
1565
1937
  if (!table || !projection) {
1566
1938
  return;
1567
1939
  }
@@ -1583,7 +1955,7 @@ var useTableModel = ({ objects, table, projection, ...props }) => {
1583
1955
  table,
1584
1956
  projection
1585
1957
  ]);
1586
- (0, import_react10.useEffect)(() => {
1958
+ (0, import_react9.useEffect)(() => {
1587
1959
  if (objects) {
1588
1960
  model?.setRows(objects);
1589
1961
  }
@@ -1591,19 +1963,43 @@ var useTableModel = ({ objects, table, projection, ...props }) => {
1591
1963
  model,
1592
1964
  objects
1593
1965
  ]);
1966
+ const { select, clear } = (0, import_react_ui_attention2.useSelectionActions)(table?.id);
1967
+ (0, import_react9.useEffect)(() => {
1968
+ if (!model) {
1969
+ return;
1970
+ }
1971
+ const unsubscribe = (0, import_signals_core6.effect)(() => {
1972
+ select([
1973
+ ...model.selection.selection.value
1974
+ ]);
1975
+ });
1976
+ return () => {
1977
+ clear();
1978
+ unsubscribe();
1979
+ };
1980
+ }, [
1981
+ model
1982
+ ]);
1594
1983
  return model;
1595
1984
  };
1596
1985
  // Annotate the CommonJS export names for ESM import in node:
1597
1986
  0 && (module.exports = {
1987
+ BUTTON_IDENTIFIERS,
1988
+ CONTROL_IDENTIFIERS,
1989
+ ModalController,
1598
1990
  Table,
1991
+ TableModel,
1992
+ TablePresentation,
1599
1993
  TableSchema,
1994
+ TableToolbar,
1600
1995
  TableType,
1601
- Toolbar,
1602
1996
  initializeTable,
1997
+ renderCheckbox,
1998
+ renderSwitch,
1603
1999
  tableButtons,
2000
+ tableControls,
1604
2001
  touch,
1605
2002
  translations,
1606
- useTableModel,
1607
- useToolbarContext
2003
+ useTableModel
1608
2004
  });
1609
2005
  //# sourceMappingURL=index.cjs.map