@dxos/react-ui-table 0.10.0 → 0.11.0

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 (110) hide show
  1. package/dist/lib/chunk-types.mjs +65 -0
  2. package/dist/lib/chunk-types.mjs.map +1 -0
  3. package/dist/lib/chunk-useTableModel.mjs +1595 -0
  4. package/dist/lib/chunk-useTableModel.mjs.map +1 -0
  5. package/dist/lib/index.mjs +1133 -0
  6. package/dist/lib/index.mjs.map +1 -0
  7. package/dist/lib/testing.mjs +88 -0
  8. package/dist/lib/testing.mjs.map +1 -0
  9. package/dist/lib/translations.mjs +30 -0
  10. package/dist/lib/translations.mjs.map +1 -0
  11. package/dist/lib/types.mjs +2 -0
  12. package/dist/types/src/components/Table/DynamicTable.d.ts +4 -1
  13. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  14. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -1
  15. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/Table/Relations.stories.d.ts +1 -0
  17. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  18. package/dist/types/src/components/Table/Table.d.ts +3 -9
  19. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  20. package/dist/types/src/components/Table/Table.stories.d.ts +2 -0
  21. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/Table/TableContent.d.ts +1 -1
  23. package/dist/types/src/components/Table/TableContent.d.ts.map +1 -1
  24. package/dist/types/src/components/Table/TableContext.d.ts +10 -0
  25. package/dist/types/src/components/Table/TableContext.d.ts.map +1 -0
  26. package/dist/types/src/components/Table/TableToolbar.d.ts +1 -1
  27. package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/index.d.ts +1 -0
  29. package/dist/types/src/components/Table/index.d.ts.map +1 -1
  30. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  31. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/testing.d.ts +1 -1
  33. package/dist/types/src/components/testing.d.ts.map +1 -1
  34. package/dist/types/src/hooks/useProjectionModel.d.ts +1 -1
  35. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
  36. package/dist/types/src/hooks/useTableModel.d.ts +8 -1
  37. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  38. package/dist/types/src/model/modal-controller.d.ts +1 -1
  39. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  40. package/dist/types/src/model/selection-model.d.ts +1 -1
  41. package/dist/types/src/model/selection-model.d.ts.map +1 -1
  42. package/dist/types/src/model/table-model.d.ts +42 -7
  43. package/dist/types/src/model/table-model.d.ts.map +1 -1
  44. package/dist/types/src/model/table-presentation.d.ts +20 -5
  45. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  46. package/dist/types/src/model/table-view-state.d.ts +5 -1
  47. package/dist/types/src/model/table-view-state.d.ts.map +1 -1
  48. package/dist/types/src/types/index.d.ts.map +1 -1
  49. package/dist/types/src/util/dynamic-table.d.ts +1 -1
  50. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  51. package/dist/types/src/util/table-controls.d.ts +5 -3
  52. package/dist/types/src/util/table-controls.d.ts.map +1 -1
  53. package/dist/types/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +48 -50
  55. package/src/components/Table/DynamicTable.stories.tsx +59 -1
  56. package/src/components/Table/DynamicTable.tsx +16 -8
  57. package/src/components/Table/Relations.stories.tsx +2 -2
  58. package/src/components/Table/Table.stories.tsx +76 -2
  59. package/src/components/Table/Table.tsx +3 -15
  60. package/src/components/Table/TableContent.tsx +6 -5
  61. package/src/components/Table/TableContext.ts +21 -0
  62. package/src/components/Table/index.ts +2 -0
  63. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +2 -3
  64. package/src/components/TableCellEditor/TableCellEditor.tsx +27 -22
  65. package/src/components/testing.ts +1 -1
  66. package/src/hooks/useProjectionModel.ts +1 -1
  67. package/src/hooks/useTableModel.ts +18 -6
  68. package/src/model/modal-controller.ts +1 -1
  69. package/src/model/selection-model.ts +1 -1
  70. package/src/model/table-model.test.ts +3 -3
  71. package/src/model/table-model.ts +148 -31
  72. package/src/model/table-presentation.test.ts +59 -1
  73. package/src/model/table-presentation.ts +171 -81
  74. package/src/model/table-view-state.ts +2 -2
  75. package/src/testing/useTestTableModel.ts +1 -1
  76. package/src/types/index.ts +4 -0
  77. package/src/util/dynamic-table.test.ts +1 -1
  78. package/src/util/dynamic-table.ts +1 -1
  79. package/src/util/table-buttons.ts +1 -1
  80. package/src/util/table-controls.ts +18 -10
  81. package/dist/lib/browser/chunk-J5LGTIGS.mjs +0 -10
  82. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +0 -7
  83. package/dist/lib/browser/chunk-K4J2CKTZ.mjs +0 -1751
  84. package/dist/lib/browser/chunk-K4J2CKTZ.mjs.map +0 -7
  85. package/dist/lib/browser/chunk-VV3P3ADH.mjs +0 -67
  86. package/dist/lib/browser/chunk-VV3P3ADH.mjs.map +0 -7
  87. package/dist/lib/browser/index.mjs +0 -1416
  88. package/dist/lib/browser/index.mjs.map +0 -7
  89. package/dist/lib/browser/meta.json +0 -1
  90. package/dist/lib/browser/testing/index.mjs +0 -115
  91. package/dist/lib/browser/testing/index.mjs.map +0 -7
  92. package/dist/lib/browser/translations.mjs +0 -39
  93. package/dist/lib/browser/translations.mjs.map +0 -7
  94. package/dist/lib/browser/types/index.mjs +0 -8
  95. package/dist/lib/browser/types/index.mjs.map +0 -7
  96. package/dist/lib/node-esm/chunk-E6V274ZW.mjs +0 -1753
  97. package/dist/lib/node-esm/chunk-E6V274ZW.mjs.map +0 -7
  98. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  99. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  100. package/dist/lib/node-esm/chunk-RPEWNOZZ.mjs +0 -68
  101. package/dist/lib/node-esm/chunk-RPEWNOZZ.mjs.map +0 -7
  102. package/dist/lib/node-esm/index.mjs +0 -1417
  103. package/dist/lib/node-esm/index.mjs.map +0 -7
  104. package/dist/lib/node-esm/meta.json +0 -1
  105. package/dist/lib/node-esm/testing/index.mjs +0 -116
  106. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  107. package/dist/lib/node-esm/translations.mjs +0 -40
  108. package/dist/lib/node-esm/translations.mjs.map +0 -7
  109. package/dist/lib/node-esm/types/index.mjs +0 -9
  110. package/dist/lib/node-esm/types/index.mjs.map +0 -7
@@ -0,0 +1,1133 @@
1
+ import { t as Table$1 } from "./chunk-types.mjs";
2
+ import { _ as compareValues, a as cellClassesForRowSelection, c as createEchoChangeCallback, d as CONTROL_IDENTIFIERS, f as CheckboxComponent, g as tableButtons, h as BUTTON_IDENTIFIERS, i as TablePresentation, l as FieldSortSchema, m as tableControls, n as useProjectionModel, o as TableModel, p as SwitchComponent, r as useAddRow, s as createDirectChangeCallback, t as useTableModel, u as tableSortAspect, v as getSortValue, y as extractOrder } from "./chunk-useTableModel.mjs";
3
+ import { createContext } from "@radix-ui/react-context";
4
+ import { Atom } from "@effect-atom/atom";
5
+ import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
6
+ import { Atom as Atom$1, RegistryContext, useAtomValue } from "@effect-atom/atom-react";
7
+ import * as String$1 from "effect/String";
8
+ import { log } from "@dxos/log";
9
+ import { DropdownMenu, Popover, composable, composableProps, toLocalizedString, useDefaultValue, useTranslation } from "@dxos/react-ui";
10
+ import { useAttention } from "@dxos/react-ui-attention";
11
+ import { DxEditRequest, Grid, GridCellEditor, closestCell, editorKeys, gridSeparatorBlockEnd, gridSeparatorInlineEnd, parseCellIndex, useGridContext } from "@dxos/react-ui-grid";
12
+ import { mx } from "@dxos/ui-theme";
13
+ import { Entity, Filter, Obj, Order, Query, Ref, Type } from "@dxos/echo";
14
+ import { SchemaEx } from "@dxos/effect";
15
+ import { invariant } from "@dxos/invariant";
16
+ import { FieldEditor, Form } from "@dxos/react-ui-form";
17
+ import { ProjectionModel, ViewModel, createEchoChangeCallback as createEchoChangeCallback$1, getSchemaFromJsonSchema, getSchemaFromPropertyDefinitions } from "@dxos/schema";
18
+ import * as Schema from "effect/Schema";
19
+ import * as SchemaAST from "effect/SchemaAST";
20
+ import { Format as Format$1, TypeEnum } from "@dxos/echo/Format";
21
+ import { EditorView } from "@codemirror/view";
22
+ import { debounce } from "@dxos/async";
23
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
24
+ import { getDeep, isTruthy, setDeep } from "@dxos/util";
25
+ import { translationKey } from "#translations";
26
+ import { Menu, createGapSeparator, createMenuAction, createMenuItemGroup, useMenuActions } from "@dxos/react-ui-menu";
27
+ //#region src/components/Table/TableContext.ts
28
+ var [TableContextProvider, useTableContext] = createContext("Table");
29
+ //#endregion
30
+ //#region src/util/dynamic-table.ts
31
+ /**
32
+ * @deprecated
33
+ */
34
+ var getBaseSchema = ({ type, typename, properties, jsonSchema }) => {
35
+ const resolved = typename && properties ? getSchemaFromPropertyDefinitions(typename, properties) : type ?? (jsonSchema ? getSchemaFromJsonSchema(jsonSchema, typename) : void 0);
36
+ if (!resolved) throw new Error("invalid properties");
37
+ return {
38
+ typename: Type.getTypename(resolved),
39
+ type: resolved
40
+ };
41
+ };
42
+ var makeDynamicTable = ({ registry, type, properties }) => {
43
+ const jsonSchema = type.jsonSchema;
44
+ const view = ViewModel.make({
45
+ query: Query.select(Filter.everything()),
46
+ jsonSchema,
47
+ type,
48
+ ...properties && { fields: properties.map((property) => property.name) }
49
+ });
50
+ const object = Obj.make(Table$1, {
51
+ view: Ref.make(view),
52
+ sizes: {}
53
+ });
54
+ const projection = new ProjectionModel({
55
+ registry,
56
+ view,
57
+ baseSchema: jsonSchema,
58
+ change: createEchoChangeCallback$1(view, type)
59
+ });
60
+ projection.normalizeView();
61
+ if (properties && projection.getFields()) setProperties(view, projection, object, properties);
62
+ return {
63
+ projection,
64
+ object
65
+ };
66
+ };
67
+ var setProperties = (view, projection, table, properties) => {
68
+ for (const property of properties) {
69
+ const field = projection.getFields().find((field) => field.path === property.name);
70
+ if (field) {
71
+ if (property.size !== void 0) Obj.update(table, (table) => {
72
+ table.sizes[field.path] = property.size;
73
+ });
74
+ if (property.title !== void 0) {
75
+ const fieldProjection = projection.getFieldProjection(field.id);
76
+ projection.setFieldProjection({
77
+ ...fieldProjection,
78
+ props: {
79
+ ...fieldProjection.props,
80
+ title: property.title
81
+ }
82
+ });
83
+ }
84
+ if (property.sort) {
85
+ const newQuery = Query.fromAst(Obj.getSnapshot(view).query.ast).orderBy(Order.property(field.path, property.sort));
86
+ Obj.update(view, (view) => {
87
+ view.query.ast = newQuery.ast;
88
+ });
89
+ }
90
+ }
91
+ }
92
+ };
93
+ //#endregion
94
+ //#region src/util/signals.ts
95
+ /**
96
+ * Explicitly marks signal dependencies for tracking within effects.
97
+ * Returns the passed value to enable inline usage.
98
+ */
99
+ var touch = (value) => value;
100
+ //#endregion
101
+ //#region src/util/schema.ts
102
+ /**
103
+ * Creates a narrowed schema from an original schema that only includes
104
+ * the specified paths. This allows form components to only display
105
+ * and edit/validate specific fields rather than the entire object.
106
+ *
107
+ * @param schema The original schema to narrow
108
+ * @param paths Array of field paths to extract
109
+ * @returns A narrowed Schema instance containing only the properties at the specified paths
110
+ */
111
+ var narrowSchema = (schema, paths) => {
112
+ const ast = schema?.ast;
113
+ if (SchemaAST.isTypeLiteral(ast)) {
114
+ const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
115
+ if (propertySignatures.length > 0) {
116
+ const narrowType = new SchemaAST.TypeLiteral(propertySignatures, []);
117
+ return Schema.make(narrowType);
118
+ }
119
+ }
120
+ };
121
+ //#endregion
122
+ //#region src/model/modal-controller.ts
123
+ var ModalController = class {
124
+ _registry;
125
+ _state;
126
+ _triggerRef = { current: null };
127
+ constructor(registry) {
128
+ this._registry = registry;
129
+ this._state = Atom.make({ type: "closed" });
130
+ }
131
+ get state() {
132
+ return this._state;
133
+ }
134
+ get stateValue() {
135
+ return this._registry.get(this._state);
136
+ }
137
+ get trigger() {
138
+ return this._triggerRef;
139
+ }
140
+ setTrigger = (trigger) => {
141
+ this._triggerRef.current = trigger;
142
+ };
143
+ showRowMenu = (rowIndex) => {
144
+ this._registry.set(this._state, {
145
+ type: "row",
146
+ rowIndex
147
+ });
148
+ };
149
+ showColumnMenu = (fieldId) => {
150
+ this._registry.set(this._state, {
151
+ type: "column",
152
+ fieldId
153
+ });
154
+ };
155
+ showColumnCreator = () => {
156
+ this._registry.set(this._state, {
157
+ type: "columnSettings",
158
+ mode: { type: "create" }
159
+ });
160
+ };
161
+ showReferencePanel = (targetId, schemaId) => {
162
+ this._registry.set(this._state, {
163
+ type: "refPanel",
164
+ targetId,
165
+ typename: schemaId
166
+ });
167
+ };
168
+ openColumnSettings = () => {
169
+ const currentState = this._registry.get(this._state);
170
+ if (currentState.type === "column") {
171
+ const fieldId = currentState.fieldId;
172
+ requestAnimationFrame(() => {
173
+ this._registry.set(this._state, {
174
+ type: "columnSettings",
175
+ mode: {
176
+ type: "edit",
177
+ fieldId
178
+ }
179
+ });
180
+ });
181
+ }
182
+ };
183
+ close = () => {
184
+ this._registry.set(this._state, { type: "closed" });
185
+ };
186
+ };
187
+ //#endregion
188
+ //#region src/components/TableCellEditor/CellValidationMessage.tsx
189
+ var CellValidationMessage = ({ __gridScope, validationError, variant = "error" }) => {
190
+ const { editing, editBox: box } = useGridContext("GridSheetCellEditor", __gridScope);
191
+ if (!editing || !validationError) return null;
192
+ return /* @__PURE__ */ jsx("div", {
193
+ className: `absolute ${variant === "error" ? "bg-error-surface" : "bg-warning-surface"} ${variant === "error" ? "text-error-fg" : "text-warning-fg"} rounded-h-sm text-xs p-1`,
194
+ style: {
195
+ "--dx-grid-cell-width": `${box?.inlineSize ?? 200}px`,
196
+ zIndex: 10,
197
+ insetBlockEnd: `calc(100% - ${box.insetBlockStart}px)`,
198
+ insetInlineStart: box.insetInlineStart,
199
+ inlineSize: box.inlineSize
200
+ },
201
+ children: validationError
202
+ });
203
+ };
204
+ //#endregion
205
+ //#region src/components/TableCellEditor/FormCellEditor.tsx
206
+ var __dxlog_file$2 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/FormCellEditor.tsx";
207
+ var createOptionLabel = ["create-new-object.label", { ns: translationKey }];
208
+ var FormCellEditor = ({ __gridScope, schema, model, fieldProjection, onSave, onCreate, ...formProps }) => {
209
+ const anchorRef = useRef(null);
210
+ const { id: _gridId, editing: contextEditing, setEditing } = useGridContext("ArrayEditor", __gridScope);
211
+ const [editing, setLocalEditing] = useState(false);
212
+ useEffect(() => {
213
+ if (contextEditing && contextEditing.cellElement) {
214
+ anchorRef.current = contextEditing.cellElement.querySelector(".dx-grid__cell__content");
215
+ setLocalEditing(true);
216
+ } else {
217
+ anchorRef.current = null;
218
+ setLocalEditing(false);
219
+ }
220
+ }, [contextEditing]);
221
+ const narrowedSchema = useMemo(() => {
222
+ if (!schema) return;
223
+ return narrowSchema(Type.getSchema(schema), [fieldProjection.field.path]);
224
+ }, [JSON.stringify(schema), fieldProjection.field.path]);
225
+ const originalRow = useMemo(() => {
226
+ if (model && contextEditing) {
227
+ const cell = parseCellIndex(contextEditing.index);
228
+ if (model.isDraftCell(cell)) {
229
+ const draftRow = model.getDraftRows()[cell.row];
230
+ invariant(draftRow, void 0, {
231
+ "~LogMeta": "~LogMeta",
232
+ F: __dxlog_file$2,
233
+ L: 75,
234
+ S: void 0,
235
+ A: ["draftRow", ""]
236
+ });
237
+ return draftRow.data;
238
+ } else {
239
+ const row = model.getRowAt(cell.row);
240
+ invariant(row, void 0, {
241
+ "~LogMeta": "~LogMeta",
242
+ F: __dxlog_file$2,
243
+ L: 79,
244
+ S: void 0,
245
+ A: ["row", ""]
246
+ });
247
+ return row;
248
+ }
249
+ }
250
+ }, [model, contextEditing]);
251
+ const [formValues, setFormValues] = useState(useMemo(() => originalRow ? JSON.parse(JSON.stringify(originalRow)) : {}, [originalRow]));
252
+ const handleValuesChanged = useCallback((values) => {
253
+ setFormValues(values);
254
+ }, []);
255
+ const handleOpenChange = useCallback((nextOpen) => {
256
+ if (nextOpen === false) {
257
+ setEditing(null);
258
+ onSave?.();
259
+ }
260
+ setLocalEditing(nextOpen);
261
+ }, []);
262
+ const handleSave = useCallback((values) => {
263
+ if (!originalRow) return;
264
+ const path = fieldProjection.field.path;
265
+ const value = getDeep(values, [path]);
266
+ if (model) model.changeRow(originalRow, (mutableRow) => {
267
+ setDeep(mutableRow, [path], value);
268
+ });
269
+ else setDeep(originalRow, [path], value);
270
+ contextEditing?.cellElement?.focus();
271
+ setEditing(null);
272
+ setLocalEditing(false);
273
+ onSave?.();
274
+ }, [
275
+ fieldProjection.field.path,
276
+ onSave,
277
+ contextEditing,
278
+ originalRow,
279
+ model
280
+ ]);
281
+ const handleCreate = useCallback((schema, values) => {
282
+ const objectWithId = onCreate?.(schema, values);
283
+ if (objectWithId && originalRow) {
284
+ const ref = Ref.make(objectWithId);
285
+ const path = fieldProjection.field.path;
286
+ if (model) model.changeRow(originalRow, (mutableRow) => {
287
+ setDeep(mutableRow, [path], ref);
288
+ });
289
+ else setDeep(originalRow, [path], ref);
290
+ }
291
+ contextEditing?.cellElement?.focus();
292
+ setEditing(null);
293
+ setLocalEditing(false);
294
+ onSave?.();
295
+ }, [
296
+ fieldProjection.field.path,
297
+ onSave,
298
+ contextEditing,
299
+ originalRow,
300
+ onCreate,
301
+ model
302
+ ]);
303
+ const getOptions = useCallback((results) => results.map((obj) => {
304
+ return {
305
+ id: Entity.getURI(obj),
306
+ label: SchemaEx.getValue(obj, fieldProjection.field.referencePath) || obj.id.toString()
307
+ };
308
+ }).filter(isTruthy), [fieldProjection]);
309
+ if (!editing) return null;
310
+ return /* @__PURE__ */ jsxs(Popover.Root, {
311
+ open: editing,
312
+ onOpenChange: handleOpenChange,
313
+ children: [/* @__PURE__ */ jsx(Popover.VirtualTrigger, { virtualRef: anchorRef }), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
314
+ tabIndex: -1,
315
+ classNames: "dx-card-popover-width dx-density-md",
316
+ children: [/* @__PURE__ */ jsx(Popover.Arrow, {}), /* @__PURE__ */ jsx(Popover.Viewport, { children: /* @__PURE__ */ jsx(Form.Root, {
317
+ ...formProps,
318
+ autoFocus: true,
319
+ schema: narrowedSchema,
320
+ values: formValues,
321
+ onValuesChanged: handleValuesChanged,
322
+ projection: model?.projection,
323
+ createInitialValuePath: fieldProjection.field.referencePath,
324
+ createOptionIcon: "ph--plus--regular",
325
+ createOptionLabel,
326
+ db: model?.db,
327
+ getOptions,
328
+ onCreate: handleCreate,
329
+ onSave: handleSave,
330
+ children: /* @__PURE__ */ jsx(Form.Viewport, { children: /* @__PURE__ */ jsxs(Form.Content, { children: [/* @__PURE__ */ jsx(Form.FieldSet, {}), /* @__PURE__ */ jsx(Form.Actions, {})] }) })
331
+ }) })]
332
+ }) })]
333
+ });
334
+ };
335
+ //#endregion
336
+ //#region src/components/TableCellEditor/TableCellEditor.tsx
337
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
338
+ /**
339
+ * Resolves the field projection for the cell being edited, guarding against an out-of-range field
340
+ * index (e.g. a stale cell, or a pinned plane whose `getFieldIndex` maps beyond the projection).
341
+ */
342
+ var resolveFieldProjection = (model, editing) => {
343
+ if (!model || !editing) return;
344
+ const { col, plane } = parseCellIndex(editing.index);
345
+ const fieldIndex = model.getFieldIndex(plane, col);
346
+ const fields = model.projection.getFields();
347
+ if (fieldIndex === void 0 || fieldIndex < 0 || fieldIndex >= fields.length) return;
348
+ const fieldProjection = model.projection.getFieldProjection(fields[fieldIndex].id);
349
+ invariant(fieldProjection, void 0, {
350
+ "~LogMeta": "~LogMeta",
351
+ F: __dxlog_file$1,
352
+ L: 68,
353
+ S: void 0,
354
+ A: ["fieldProjection", ""]
355
+ });
356
+ return fieldProjection;
357
+ };
358
+ var TableValueEditor = ({ __gridScope, schema, model, modals, onFocus, onSave, onCreate }) => {
359
+ const { editing } = useGridContext("TableValueEditor", __gridScope);
360
+ const fieldProjection = useMemo(() => resolveFieldProjection(model, editing), [model, editing]);
361
+ if (fieldProjection?.props.type === TypeEnum.Array || fieldProjection?.props.format === Format$1.TypeFormat.SingleSelect || fieldProjection?.props.format === Format$1.TypeFormat.Ref) return /* @__PURE__ */ jsx(FormCellEditor, {
362
+ __gridScope,
363
+ schema,
364
+ model,
365
+ fieldProjection,
366
+ modals,
367
+ onSave,
368
+ onCreate
369
+ });
370
+ return /* @__PURE__ */ jsx(TableCellEditor, {
371
+ model,
372
+ modals,
373
+ onFocus,
374
+ onSave,
375
+ __gridScope
376
+ });
377
+ };
378
+ var cellEditorSlots = { content: { className: "!py-(--dx-grid-cell-editor-padding-block)" } };
379
+ var TableCellEditor = ({ __gridScope, model, modals, onFocus, onSave }) => {
380
+ const { editing, setEditing } = useGridContext("TableCellEditor", __gridScope);
381
+ const suppressNextBlur = useRef(false);
382
+ const [validationError, setValidationError] = useState(null);
383
+ const [validationVariant, setValidationVariant] = useState("error");
384
+ const fieldProjection = useMemo(() => resolveFieldProjection(model, editing), [model, editing]);
385
+ useCallback(async (value) => {
386
+ if (!model || !editing) return;
387
+ const cell = parseCellIndex(editing.index);
388
+ const validationResult = await model.validateCellData(cell, value);
389
+ if (validationResult.valid) {
390
+ setValidationError(null);
391
+ model.setCellData(cell, value);
392
+ onFocus?.();
393
+ setEditing(null);
394
+ onSave?.();
395
+ } else {
396
+ setValidationError(validationResult.error);
397
+ setValidationVariant("error");
398
+ }
399
+ }, [
400
+ model,
401
+ editing,
402
+ onFocus,
403
+ setEditing
404
+ ]);
405
+ const handleBlur = useCallback(async (value) => {
406
+ if (suppressNextBlur.current) suppressNextBlur.current = false;
407
+ else if (model && editing) {
408
+ const cell = parseCellIndex(editing.index);
409
+ if (value !== void 0) {
410
+ if ((await model.validateCellData(cell, value)).valid) {
411
+ setValidationError(null);
412
+ model.setCellData(cell, value);
413
+ setEditing(null);
414
+ onSave?.();
415
+ }
416
+ }
417
+ }
418
+ }, [model, editing]);
419
+ const handleClose = useCallback(async (value, event) => {
420
+ if (!model || !editing || !fieldProjection) return;
421
+ const cell = parseCellIndex(editing.index);
422
+ if (value !== void 0) {
423
+ const result = await model.validateCellData(cell, value);
424
+ if (result.valid) {
425
+ suppressNextBlur.current = true;
426
+ setValidationError(null);
427
+ model.setCellData(cell, value);
428
+ setEditing(null);
429
+ onSave?.();
430
+ if (event && onFocus) onFocus(determineNavigationAxis(event), determineNavigationDelta(event), cell);
431
+ } else {
432
+ setValidationError(result.error);
433
+ setValidationVariant("error");
434
+ }
435
+ } else {
436
+ suppressNextBlur.current = true;
437
+ setValidationError(null);
438
+ setEditing(null);
439
+ onSave?.();
440
+ if (event && onFocus) onFocus(determineNavigationAxis(event), determineNavigationDelta(event));
441
+ }
442
+ }, [
443
+ model,
444
+ editing,
445
+ onFocus,
446
+ fieldProjection,
447
+ setEditing,
448
+ onSave
449
+ ]);
450
+ const extensions = useMemo(() => {
451
+ if (!fieldProjection) return [];
452
+ const extensions = [editorKeys({
453
+ onClose: handleClose,
454
+ ...editing?.initialContent && { onNav: handleClose }
455
+ })];
456
+ if (model && editing) extensions.push(EditorView.updateListener.of(debounce((update) => {
457
+ const content = update.state.doc.toString();
458
+ const cell = parseCellIndex(editing.index);
459
+ model.validateCellData(cell, content).then((result) => {
460
+ if (result.valid) setValidationError(null);
461
+ else {
462
+ setValidationError(result.error);
463
+ setValidationVariant("error");
464
+ }
465
+ });
466
+ }, 10)));
467
+ return extensions;
468
+ }, [
469
+ model,
470
+ modals,
471
+ editing,
472
+ fieldProjection,
473
+ handleClose
474
+ ]);
475
+ const getCellContent = useCallback(() => {
476
+ if (model && editing) {
477
+ const cell = parseCellIndex(editing.index);
478
+ const value = model.getCellData(cell);
479
+ return value !== void 0 ? String(value) : "";
480
+ }
481
+ }, [model, editing]);
482
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CellValidationMessage, {
483
+ validationError,
484
+ variant: validationVariant,
485
+ __gridScope
486
+ }), /* @__PURE__ */ jsx(GridCellEditor, {
487
+ extensions,
488
+ slots: cellEditorSlots,
489
+ getCellContent,
490
+ onBlur: handleBlur
491
+ })] });
492
+ };
493
+ var determineNavigationAxis = ({ key }) => {
494
+ switch (key) {
495
+ case "ArrowUp":
496
+ case "ArrowDown":
497
+ case "Enter": return "row";
498
+ case "ArrowLeft":
499
+ case "ArrowRight":
500
+ case "Tab": return "col";
501
+ }
502
+ };
503
+ var determineNavigationDelta = ({ key, shift }) => {
504
+ switch (key) {
505
+ case "ArrowUp":
506
+ case "ArrowLeft": return -1;
507
+ case "ArrowDown":
508
+ case "ArrowRight": return 1;
509
+ }
510
+ return shift ? -1 : 1;
511
+ };
512
+ //#endregion
513
+ //#region src/components/Table/ColumnActionsMenu.tsx
514
+ var ColumnActionsMenu = ({ model, modals }) => {
515
+ const { t } = useTranslation(translationKey);
516
+ const state = useAtomValue(modals.state);
517
+ if (state?.type !== "column") return null;
518
+ const currentSort = model.getSorting();
519
+ const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
520
+ return /* @__PURE__ */ jsxs(DropdownMenu.Root, {
521
+ modal: false,
522
+ open: true,
523
+ onOpenChange: modals.close,
524
+ children: [/* @__PURE__ */ jsx(DropdownMenu.VirtualTrigger, { virtualRef: modals.trigger }), /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [/* @__PURE__ */ jsxs(DropdownMenu.Viewport, { children: [
525
+ (!isCurrentColumnSorted || currentSort?.direction === "asc") && /* @__PURE__ */ jsx(DropdownMenu.Item, {
526
+ "data-testid": "column-sort-descending",
527
+ onClick: () => model.setSort(state.fieldId, "desc"),
528
+ children: t("column-action-sort-descending.menu")
529
+ }),
530
+ (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ jsx(DropdownMenu.Item, {
531
+ "data-testid": "column-sort-ascending",
532
+ onClick: () => model.setSort(state.fieldId, "asc"),
533
+ children: t("column-action-sort-ascending.menu")
534
+ }),
535
+ isCurrentColumnSorted && /* @__PURE__ */ jsx(DropdownMenu.Item, {
536
+ "data-testid": "column-clear-sort",
537
+ onClick: () => model.clearSort(),
538
+ children: t("column-action-clear-sorting.menu")
539
+ }),
540
+ model.getColumnCount() > 1 && model.features.schemaEditable && /* @__PURE__ */ jsx(DropdownMenu.Item, {
541
+ "data-testid": "column-delete",
542
+ onClick: () => model.deleteColumn(state.fieldId),
543
+ children: t("column-action-delete.menu")
544
+ }),
545
+ model.features.schemaEditable && /* @__PURE__ */ jsx(DropdownMenu.Item, {
546
+ "data-testid": "column-settings",
547
+ onClick: () => modals.openColumnSettings(),
548
+ children: t("column-action-settings.menu")
549
+ })
550
+ ] }), /* @__PURE__ */ jsx(DropdownMenu.Arrow, {})] }) })]
551
+ });
552
+ };
553
+ //#endregion
554
+ //#region src/components/Table/ColumnSettings.tsx
555
+ var ColumnSettings = ({ registry, model, modals, onNewColumn }) => {
556
+ const [newField, setNewField] = useState();
557
+ const state = useAtomValue(modals.state);
558
+ useEffect(() => {
559
+ if (state?.type === "columnSettings" && state.mode.type === "create" && model?.projection) {
560
+ setNewField(model.projection.createFieldProjection());
561
+ requestAnimationFrame(() => {
562
+ onNewColumn();
563
+ });
564
+ } else setNewField(void 0);
565
+ }, [model?.projection, state]);
566
+ const field = useMemo(() => {
567
+ if (state?.type === "columnSettings") {
568
+ const { mode } = state;
569
+ if (mode.type === "edit") return model?.projection?.getFields().find((f) => f.id === mode.fieldId);
570
+ }
571
+ }, [model?.projection, state]) ?? newField;
572
+ const handleSave = useCallback(() => {
573
+ modals.close();
574
+ }, [modals]);
575
+ const handleCancel = useCallback(() => {
576
+ if (state?.type === "columnSettings" && state.mode.type === "create" && newField) model?.projection.deleteFieldProjection(newField.id);
577
+ }, [
578
+ model?.projection,
579
+ state,
580
+ newField
581
+ ]);
582
+ if (!model?.projection || !field) return null;
583
+ return /* @__PURE__ */ jsxs(Popover.Root, {
584
+ modal: false,
585
+ open: state?.type === "columnSettings",
586
+ children: [/* @__PURE__ */ jsx(Popover.VirtualTrigger, { virtualRef: modals.trigger }), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
587
+ classNames: "md:w-64",
588
+ children: [/* @__PURE__ */ jsx(Popover.Viewport, { children: /* @__PURE__ */ jsx(FieldEditor, {
589
+ projection: model.projection,
590
+ field,
591
+ registry,
592
+ onSave: handleSave,
593
+ onCancel: handleCancel
594
+ }) }), /* @__PURE__ */ jsx(Popover.Arrow, {})]
595
+ }) })]
596
+ });
597
+ };
598
+ //#endregion
599
+ //#region src/components/Table/RowActionsMenu.tsx
600
+ var RowActionsMenu = ({ model, modals }) => {
601
+ const { t } = useTranslation(translationKey);
602
+ const hasSelection = model.selection.hasSelection;
603
+ const state = useAtomValue(modals.state);
604
+ if (state?.type !== "row") return null;
605
+ return /* @__PURE__ */ jsxs(DropdownMenu.Root, {
606
+ modal: false,
607
+ open: true,
608
+ onOpenChange: modals.close,
609
+ children: [/* @__PURE__ */ jsx(DropdownMenu.VirtualTrigger, { virtualRef: modals.trigger }), /* @__PURE__ */ jsxs(DropdownMenu.Content, { children: [/* @__PURE__ */ jsxs(DropdownMenu.Viewport, { children: [model.rowActions?.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenu.Group, { children: model.rowActions?.map((action) => /* @__PURE__ */ jsx(DropdownMenu.Item, {
610
+ "data-testid": `row-action-${action.id}`,
611
+ onClick: () => {
612
+ modals.close();
613
+ model.handleRowAction(action.id, state.rowIndex);
614
+ },
615
+ children: toLocalizedString(action.label, t)
616
+ }, action.id)) }), /* @__PURE__ */ jsx(DropdownMenu.Separator, {})] }), model.features.dataEditable !== false && /* @__PURE__ */ jsx(DropdownMenu.Item, {
617
+ "data-testid": "row-menu-delete",
618
+ onClick: () => model.deleteRow(state.rowIndex),
619
+ children: t(hasSelection ? "bulk-delete-row.label" : "delete-row.label")
620
+ })] }), /* @__PURE__ */ jsx(DropdownMenu.Arrow, {})] })]
621
+ });
622
+ };
623
+ //#endregion
624
+ //#region src/components/Table/TableContent.tsx
625
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-table/src/components/Table/TableContent.tsx";
626
+ var columnDefault = { grid: {
627
+ minSize: 80,
628
+ maxSize: 640
629
+ } };
630
+ var rowDefault = { frozenRowsStart: {
631
+ readonly: true,
632
+ focusUnfurl: false
633
+ } };
634
+ var emptyColumnMeta = Atom$1.make({ grid: {} });
635
+ var emptyRowsAtom = Atom$1.make([]);
636
+ var emptyCellUpdateAtom = Atom$1.make(0);
637
+ var TABLE_CONTENT_NAME = "Table.Content";
638
+ var TableContent = composable(({ schema, model, presentation, ignoreAttention, attendableId, onCreate, onRowClick, testId, ...props }, forwardedRef) => {
639
+ const registry = useContext(RegistryContext);
640
+ const [dxGrid, setDxGrid] = useState(null);
641
+ const { hasAttention } = useAttention(attendableId ?? model?.id ?? "table");
642
+ const modals = useMemo(() => new ModalController(registry), [registry]);
643
+ const columnMeta = useAtomValue(model?.columnMeta ?? emptyColumnMeta);
644
+ const rows = useAtomValue(model?.rows ?? emptyRowsAtom);
645
+ const cellUpdateCounter = useAtomValue(model?.cellUpdate ?? emptyCellUpdateAtom);
646
+ const columnCount = Object.keys(columnMeta.grid).length || model?.projection.getFields().length || 0;
647
+ const draftRowCount = model?.getDraftRowCount() ?? 0;
648
+ const frozen = useMemo(() => {
649
+ const noActionColumn = model?.features.dataEditable === false && model?.features.schemaEditable === false && model.rowActions.length === 0;
650
+ return {
651
+ frozenRowsStart: 1,
652
+ frozenRowsEnd: Math.max(1, draftRowCount),
653
+ frozenColsStart: model?.frozenColsStart ?? 0,
654
+ frozenColsEnd: noActionColumn ? 0 : 1
655
+ };
656
+ }, [
657
+ model,
658
+ draftRowCount,
659
+ columnCount
660
+ ]);
661
+ const getCells = useCallback((range, plane) => presentation?.getCells(range, plane) ?? {}, [presentation]);
662
+ useEffect(() => {
663
+ if (!dxGrid || !presentation) return;
664
+ dxGrid.getCells = getCells;
665
+ }, [
666
+ dxGrid,
667
+ presentation,
668
+ getCells
669
+ ]);
670
+ useEffect(() => {
671
+ if (!dxGrid) return;
672
+ dxGrid.updateCells(true);
673
+ dxGrid.requestUpdate();
674
+ }, [
675
+ dxGrid,
676
+ rows,
677
+ cellUpdateCounter
678
+ ]);
679
+ const handleInsertRowResult = useCallback((insertResult) => {
680
+ if (insertResult === "draft") requestAnimationFrame(() => {
681
+ dxGrid?.setFocus({
682
+ plane: "frozenRowsEnd",
683
+ col: 0,
684
+ row: 0
685
+ });
686
+ dxGrid?.refocus();
687
+ });
688
+ else requestAnimationFrame(() => {
689
+ dxGrid?.setFocus({
690
+ plane: "grid",
691
+ col: 0,
692
+ row: model ? model.getRowCount() - 1 : 0
693
+ });
694
+ dxGrid?.refocus();
695
+ });
696
+ }, [model, dxGrid]);
697
+ const handleSave = useCallback(() => {
698
+ dxGrid?.updateCells(true);
699
+ dxGrid?.requestUpdate();
700
+ }, [dxGrid]);
701
+ const handleSaveDraftRow = useCallback((rowIndex = 0) => {
702
+ if (model && dxGrid) {
703
+ if (model.commitDraftRow(rowIndex)) requestAnimationFrame(() => {
704
+ dxGrid.scrollToEndRow();
705
+ });
706
+ }
707
+ }, [model, dxGrid]);
708
+ const handleGridClick = useCallback((event) => {
709
+ const cell = closestCell(event.target);
710
+ if (cell) {
711
+ const { row: rowIndex, plane } = cell;
712
+ if (onRowClick) if (plane === "grid") {
713
+ const row = model?.getRowAt(rowIndex);
714
+ row && onRowClick(row);
715
+ } else onRowClick(cell);
716
+ if (model?.features.selection.enabled && model?.selection.selectionMode === "single") model.selection.toggleSelectionForRowIndex(rowIndex);
717
+ }
718
+ if (!modals) return;
719
+ const buttonSelector = Object.values(tableButtons).map((button) => `button[${button.attr}]`).join(",");
720
+ const tableButtonElement = event.target.closest(buttonSelector);
721
+ if (tableButtonElement) {
722
+ const button = Object.values(tableButtons).find((btn) => tableButtonElement.hasAttribute(btn.attr));
723
+ if (!button) return;
724
+ modals.setTrigger(tableButtonElement);
725
+ const data = button.getData(tableButtonElement);
726
+ switch (data.type) {
727
+ case "rowMenu":
728
+ modals.showRowMenu(data.rowIndex);
729
+ break;
730
+ case "columnSettings":
731
+ modals.showColumnMenu(data.fieldId);
732
+ break;
733
+ case "newColumn":
734
+ modals.showColumnCreator();
735
+ break;
736
+ case "sort":
737
+ model?.toggleSort(data.fieldId);
738
+ break;
739
+ case "saveDraftRow":
740
+ handleSaveDraftRow();
741
+ break;
742
+ }
743
+ return;
744
+ }
745
+ const controlSelector = Object.values(tableControls).map((control) => `input[${control.attr}]`).join(",");
746
+ const controlElement = event.target.closest(controlSelector);
747
+ if (controlElement) {
748
+ const control = Object.values(tableControls).find((ctrl) => controlElement.hasAttribute(ctrl.attr));
749
+ if (!control) return;
750
+ const data = control.getData(controlElement);
751
+ switch (data.type) {
752
+ case "checkbox":
753
+ if (data.header) model?.selection.setSelection(model.selection.allRowsSelected ? "none" : "all");
754
+ else model?.selection.toggleSelectionForRowIndex(data.rowIndex);
755
+ break;
756
+ case "switch":
757
+ if (model) model.updateCellData({
758
+ row: data.rowIndex,
759
+ col: data.colIndex,
760
+ plane: data.plane
761
+ }, (value) => !value);
762
+ break;
763
+ }
764
+ }
765
+ }, [
766
+ model,
767
+ modals,
768
+ dxGrid
769
+ ]);
770
+ const handleFocus = useCallback((increment, delta, cell) => {
771
+ if (dxGrid && model) if (cell?.plane === "grid" && cell?.row >= model.getRowCount() - 1 && increment !== "col") handleInsertRowResult(draftRowCount < 1 ? model.insertRow() : "final");
772
+ else if (cell?.plane === "frozenRowsEnd" && increment === "row") {
773
+ handleSaveDraftRow(cell.row);
774
+ handleInsertRowResult(model.insertRow());
775
+ } else dxGrid.refocus(increment, delta);
776
+ }, [dxGrid, model]);
777
+ const handleKeyDown = useCallback((event) => {
778
+ if (!model?.features.dataEditable) return;
779
+ const cell = closestCell(event.target);
780
+ if (!model || !cell) return;
781
+ if (event.metaKey || event.ctrlKey) switch (event.key) {
782
+ case "c":
783
+ try {
784
+ const textContent = model.getCellData(cell)?.toString() ?? "";
785
+ navigator.clipboard.writeText(textContent);
786
+ event.preventDefault();
787
+ } catch (error) {
788
+ log.catch(error, void 0, {
789
+ "~LogMeta": "~LogMeta",
790
+ F: __dxlog_file,
791
+ L: 284,
792
+ S: void 0
793
+ });
794
+ }
795
+ break;
796
+ case "v":
797
+ event.preventDefault();
798
+ navigator.clipboard.readText().then((clipboardText) => {
799
+ try {
800
+ model.setCellData(cell, String$1.trim(clipboardText).replace(/[\n\r]+/, " "));
801
+ handleSave();
802
+ } catch {
803
+ const cellElement = event.target.closest("[data-dx-grid-action=\"cell\"]");
804
+ if (cellElement) {
805
+ const rect = cellElement.getBoundingClientRect();
806
+ const editRequest = new DxEditRequest({
807
+ cellIndex: `${cell.plane},${cell.col},${cell.row}`,
808
+ cellBox: {
809
+ insetInlineStart: rect.left,
810
+ insetBlockStart: rect.top,
811
+ inlineSize: rect.width,
812
+ blockSize: rect.height
813
+ },
814
+ cellElement,
815
+ initialContent: clipboardText
816
+ });
817
+ cellElement.dispatchEvent(editRequest);
818
+ }
819
+ }
820
+ });
821
+ break;
822
+ }
823
+ switch (event.key) {
824
+ case "Backspace":
825
+ case "Delete":
826
+ try {
827
+ model.setCellData(cell, void 0);
828
+ event.preventDefault();
829
+ handleSave();
830
+ } catch {}
831
+ break;
832
+ }
833
+ }, [
834
+ model,
835
+ dxGrid,
836
+ handleSave
837
+ ]);
838
+ const handleAxisResize = useCallback((event) => {
839
+ if (event.axis === "col") {
840
+ const columnIndex = parseInt(event.index, 10);
841
+ model?.setColumnWidth(columnIndex, event.size, event.plane);
842
+ }
843
+ }, [model]);
844
+ const handleWheel = useCallback((event) => {
845
+ if (!ignoreAttention && !hasAttention) event.stopPropagation();
846
+ }, [hasAttention, ignoreAttention]);
847
+ const handleNewColumn = useCallback(() => {
848
+ const columns = model?.getColumnCount();
849
+ if (dxGrid && columns) dxGrid.scrollToColumn(columns - 1);
850
+ }, [model, dxGrid]);
851
+ const { controllerRef } = useTableContext(TABLE_CONTENT_NAME);
852
+ controllerRef.current = useMemo(() => {
853
+ if (!dxGrid || !presentation) return {};
854
+ return {
855
+ update: (cell) => {
856
+ if (cell) dxGrid.updateIfWithinBounds(cell, true);
857
+ else {
858
+ dxGrid.updateCells(true);
859
+ dxGrid.requestUpdate();
860
+ }
861
+ },
862
+ handleInsertRowResult
863
+ };
864
+ }, [
865
+ dxGrid,
866
+ model,
867
+ presentation
868
+ ]);
869
+ if (!model || !modals) return /* @__PURE__ */ jsx("span", { className: "dx-attention-surface" });
870
+ return /* @__PURE__ */ jsx("div", {
871
+ ...composableProps(props, { classNames: "dx-container relative" }),
872
+ ref: forwardedRef,
873
+ children: /* @__PURE__ */ jsxs(Grid.Root, {
874
+ id: model.id ?? "table-grid",
875
+ children: [
876
+ /* @__PURE__ */ jsx(TableValueEditor, {
877
+ model,
878
+ modals,
879
+ schema,
880
+ onFocus: handleFocus,
881
+ onSave: handleSave,
882
+ onCreate
883
+ }),
884
+ /* @__PURE__ */ jsx(Grid.Content, {
885
+ className: mx("[--dx-grid-base:var(--base-surface)]", gridSeparatorInlineEnd, gridSeparatorBlockEnd),
886
+ frozen,
887
+ columns: columnMeta,
888
+ columnDefault,
889
+ rowDefault,
890
+ limitRows: rows.length,
891
+ limitColumns: columnCount,
892
+ overscroll: "trap",
893
+ onAxisResize: handleAxisResize,
894
+ onClick: handleGridClick,
895
+ onKeyDownCapture: handleKeyDown,
896
+ onWheelCapture: handleWheel,
897
+ ...testId && { "data-testid": testId },
898
+ ref: setDxGrid
899
+ }),
900
+ /* @__PURE__ */ jsx(RowActionsMenu, {
901
+ model,
902
+ modals
903
+ }),
904
+ /* @__PURE__ */ jsx(ColumnActionsMenu, {
905
+ model,
906
+ modals
907
+ }),
908
+ /* @__PURE__ */ jsx(ColumnSettings, {
909
+ model,
910
+ modals,
911
+ onNewColumn: handleNewColumn
912
+ })
913
+ ]
914
+ })
915
+ });
916
+ });
917
+ TableContent.displayName = TABLE_CONTENT_NAME;
918
+ //#endregion
919
+ //#region src/components/Table/TableToolbar.tsx
920
+ var createTableToolbarActions = ({ stateAtom, onAdd, onSave, onExport, customActions }) => Atom$1.make((get) => {
921
+ const nodes = [];
922
+ const edges = [];
923
+ if (onAdd) {
924
+ const add = createMenuAction("add-row", onAdd, {
925
+ type: "add-row",
926
+ icon: "ph--plus--regular",
927
+ label: ["add-row.label", { ns: translationKey }],
928
+ testId: "table.toolbar.add-row"
929
+ });
930
+ nodes.push(add);
931
+ edges.push({
932
+ source: "root",
933
+ target: add.id,
934
+ relation: "child"
935
+ });
936
+ }
937
+ if (onSave) {
938
+ const state = get(stateAtom);
939
+ const save = createMenuAction("save-view", onSave, {
940
+ type: "save-view",
941
+ icon: "ph--floppy-disk--regular",
942
+ label: ["save-view.label", { ns: translationKey }],
943
+ testId: "table.toolbar.save-view",
944
+ iconOnly: false,
945
+ hidden: !state.viewDirty
946
+ });
947
+ nodes.push(save);
948
+ edges.push({
949
+ source: "root",
950
+ target: save.id,
951
+ relation: "child"
952
+ });
953
+ }
954
+ if (onExport) {
955
+ const exportGroup = createMenuItemGroup("export-rows", {
956
+ label: ["export-rows.menu", { ns: translationKey }],
957
+ icon: "ph--export--regular",
958
+ iconOnly: true,
959
+ variant: "dropdownMenu"
960
+ });
961
+ nodes.push(exportGroup);
962
+ edges.push({
963
+ source: "root",
964
+ target: exportGroup.id,
965
+ relation: "child"
966
+ });
967
+ for (const { format, type, label } of [
968
+ {
969
+ format: "csv",
970
+ type: "export-csv",
971
+ label: "export-rows-csv.menu"
972
+ },
973
+ {
974
+ format: "json",
975
+ type: "export-json",
976
+ label: "export-rows-json.menu"
977
+ },
978
+ {
979
+ format: "xml",
980
+ type: "export-xml",
981
+ label: "export-rows-xml.menu"
982
+ }
983
+ ]) {
984
+ const action = createMenuAction(type, () => onExport(format), {
985
+ type,
986
+ label: [label, { ns: translationKey }]
987
+ });
988
+ nodes.push(action);
989
+ edges.push({
990
+ source: exportGroup.id,
991
+ target: action.id,
992
+ relation: "child"
993
+ });
994
+ }
995
+ }
996
+ const gap = createGapSeparator();
997
+ nodes.push(...gap.nodes);
998
+ edges.push(...gap.edges);
999
+ if (customActions) {
1000
+ const custom = get(customActions);
1001
+ nodes.push(...custom.nodes);
1002
+ edges.push(...custom.edges);
1003
+ }
1004
+ return {
1005
+ nodes,
1006
+ edges
1007
+ };
1008
+ });
1009
+ var TableToolbar = composable(({ attendableId, viewDirty, onAdd, onSave, onExport, customActions, ...props }, forwardedRef) => {
1010
+ const registry = useContext(RegistryContext);
1011
+ const stateAtom = useMemo(() => Atom$1.make({ viewDirty }), []);
1012
+ useEffect(() => {
1013
+ registry.set(stateAtom, { viewDirty });
1014
+ }, [
1015
+ registry,
1016
+ stateAtom,
1017
+ viewDirty
1018
+ ]);
1019
+ const menuActions = useMenuActions(useMemo(() => createTableToolbarActions({
1020
+ stateAtom,
1021
+ onAdd,
1022
+ onSave,
1023
+ onExport,
1024
+ customActions
1025
+ }), [
1026
+ stateAtom,
1027
+ onAdd,
1028
+ onSave,
1029
+ onExport,
1030
+ customActions
1031
+ ]));
1032
+ return /* @__PURE__ */ jsx(Menu.Root, {
1033
+ ...menuActions,
1034
+ attendableId,
1035
+ children: /* @__PURE__ */ jsx(Menu.Toolbar, {
1036
+ ...composableProps(props),
1037
+ ref: forwardedRef
1038
+ })
1039
+ });
1040
+ });
1041
+ TableToolbar.displayName = "TableToolbar";
1042
+ //#endregion
1043
+ //#region src/components/Table/Table.tsx
1044
+ Atom.make({ grid: {} });
1045
+ var TABLE_ROOT_NAME = "Table.Root";
1046
+ var TableRoot = forwardRef(({ children }, forwardedRef) => {
1047
+ const controllerRef = useRef({});
1048
+ useImperativeHandle(forwardedRef, () => ({
1049
+ update: (cell) => controllerRef.current?.update?.(cell),
1050
+ handleInsertRowResult: (result) => controllerRef.current?.handleInsertRowResult?.(result)
1051
+ }), []);
1052
+ return /* @__PURE__ */ jsx(TableContextProvider, {
1053
+ controllerRef,
1054
+ children
1055
+ });
1056
+ });
1057
+ TableRoot.displayName = TABLE_ROOT_NAME;
1058
+ var Table = {
1059
+ Root: TableRoot,
1060
+ Toolbar: TableToolbar,
1061
+ Content: TableContent
1062
+ };
1063
+ //#endregion
1064
+ //#region src/components/Table/DynamicTable.tsx
1065
+ /**
1066
+ * Properties define both the schema and display characteristics of the table columns.
1067
+ */
1068
+ var DynamicTable = ({ classNames, type: typeProp, name = "com.example.dynamicTable", rows, properties, jsonSchema: jsonSchemaProp, rowActions, selection, onRowClick, onRowAction, onSelectionChanged, ...props }) => {
1069
+ const registry = useContext(RegistryContext);
1070
+ const [dynamicTable, setDynamicTable] = useState();
1071
+ const { type } = useMemo(() => getBaseSchema({
1072
+ typename: name,
1073
+ properties,
1074
+ jsonSchema: jsonSchemaProp,
1075
+ type: typeProp
1076
+ }), [
1077
+ name,
1078
+ properties,
1079
+ jsonSchemaProp,
1080
+ typeProp
1081
+ ]);
1082
+ useEffect(() => {
1083
+ setDynamicTable(makeDynamicTable({
1084
+ registry,
1085
+ type,
1086
+ properties
1087
+ }));
1088
+ }, [
1089
+ registry,
1090
+ type,
1091
+ properties
1092
+ ]);
1093
+ const tableRef = useRef(null);
1094
+ const handleCellUpdate = useCallback((cell) => {
1095
+ tableRef.current?.update?.(cell);
1096
+ }, []);
1097
+ const handleRowOrderChange = useCallback(() => {
1098
+ tableRef.current?.update?.();
1099
+ }, []);
1100
+ const features = useDefaultValue(props.features, () => ({
1101
+ selection: { enabled: false },
1102
+ dataEditable: false
1103
+ }));
1104
+ const model = useTableModel({
1105
+ rows,
1106
+ object: dynamicTable?.object,
1107
+ projection: dynamicTable?.projection,
1108
+ features,
1109
+ rowActions,
1110
+ selection,
1111
+ onCellUpdate: handleCellUpdate,
1112
+ onRowOrderChange: handleRowOrderChange,
1113
+ onRowAction,
1114
+ onSelectionChanged
1115
+ });
1116
+ const presentation = useMemo(() => {
1117
+ if (model) return new TablePresentation(registry, model);
1118
+ }, [registry, model]);
1119
+ return /* @__PURE__ */ jsx(Table.Root, {
1120
+ ref: tableRef,
1121
+ children: /* @__PURE__ */ jsx(Table.Content, {
1122
+ classNames,
1123
+ model,
1124
+ presentation,
1125
+ ignoreAttention: true,
1126
+ onRowClick
1127
+ })
1128
+ });
1129
+ };
1130
+ //#endregion
1131
+ export { BUTTON_IDENTIFIERS, CONTROL_IDENTIFIERS, CheckboxComponent, DynamicTable, FieldSortSchema, ModalController, SwitchComponent, Table, TableModel, TablePresentation, cellClassesForRowSelection, compareValues, createDirectChangeCallback, createEchoChangeCallback, extractOrder, getBaseSchema, getSortValue, makeDynamicTable, narrowSchema, tableButtons, tableControls, tableSortAspect, touch, useAddRow, useProjectionModel, useTableContext, useTableModel };
1132
+
1133
+ //# sourceMappingURL=index.mjs.map