@dxos/react-ui-table 0.7.5-main.499c70c → 0.7.5-main.5ae2ba8

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 (117) hide show
  1. package/dist/lib/browser/{chunk-AT7O4RAD.mjs → chunk-3FR6NHPD.mjs} +6 -4
  2. package/dist/lib/browser/chunk-3FR6NHPD.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +482 -284
  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-YSBXNBDJ.cjs → chunk-NB3SFHGA.cjs} +8 -6
  8. package/dist/lib/node/chunk-NB3SFHGA.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +474 -270
  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-6OH2NHUX.mjs → chunk-RLVVOZW5.mjs} +6 -4
  15. package/dist/lib/node-esm/chunk-RLVVOZW5.mjs.map +7 -0
  16. package/dist/lib/node-esm/index.mjs +482 -284
  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/components/Cell/Cell.d.ts +2 -2
  21. package/dist/types/src/_deprecated/components/Cell/Cell.d.ts.map +1 -1
  22. package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts +1 -2
  23. package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts.map +1 -1
  24. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts +2 -3
  25. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +1 -1
  26. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts +6 -7
  27. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -1
  28. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts +1 -2
  29. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +1 -1
  30. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts +6 -7
  31. package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +1 -1
  32. package/dist/types/src/_deprecated/components/Table/Table.d.ts +5 -5
  33. package/dist/types/src/_deprecated/components/Table/Table.d.ts.map +1 -1
  34. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts +8 -9
  35. package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts.map +1 -1
  36. package/dist/types/src/_deprecated/components/Table/TableBody.d.ts +1 -2
  37. package/dist/types/src/_deprecated/components/Table/TableBody.d.ts.map +1 -1
  38. package/dist/types/src/_deprecated/components/Table/TableContext.d.ts +1 -1
  39. package/dist/types/src/_deprecated/components/Table/TableContext.d.ts.map +1 -1
  40. package/dist/types/src/_deprecated/components/Table/TableHead.d.ts +1 -2
  41. package/dist/types/src/_deprecated/components/Table/TableHead.d.ts.map +1 -1
  42. package/dist/types/src/_deprecated/schema/Schema.stories.d.ts +2 -3
  43. package/dist/types/src/_deprecated/schema/Schema.stories.d.ts.map +1 -1
  44. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
  45. package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
  46. package/dist/types/src/components/Table/ColumnSettings.d.ts +1 -2
  47. package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
  48. package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -2
  49. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
  50. package/dist/types/src/components/Table/RefPanel.d.ts +1 -2
  51. package/dist/types/src/components/Table/RefPanel.d.ts.map +1 -1
  52. package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -2
  53. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  54. package/dist/types/src/components/Table/Table.d.ts +1 -1
  55. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  56. package/dist/types/src/components/Table/Table.stories.d.ts +1 -4
  57. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  58. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +1 -2
  59. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  60. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +17 -0
  61. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -0
  62. package/dist/types/src/components/TableToolbar/index.d.ts +2 -0
  63. package/dist/types/src/components/TableToolbar/index.d.ts.map +1 -0
  64. package/dist/types/src/components/index.d.ts +1 -1
  65. package/dist/types/src/components/index.d.ts.map +1 -1
  66. package/dist/types/src/components/testing.d.ts +12 -35
  67. package/dist/types/src/components/testing.d.ts.map +1 -1
  68. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  69. package/dist/types/src/model/table-model.d.ts +11 -19
  70. package/dist/types/src/model/table-model.d.ts.map +1 -1
  71. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  72. package/dist/types/src/model/table-sorting.d.ts +53 -0
  73. package/dist/types/src/model/table-sorting.d.ts.map +1 -0
  74. package/dist/types/src/translations.d.ts +2 -1
  75. package/dist/types/src/translations.d.ts.map +1 -1
  76. package/dist/types/src/types/table.d.ts +21 -90
  77. package/dist/types/src/types/table.d.ts.map +1 -1
  78. package/dist/types/src/util/table-buttons.d.ts +15 -1
  79. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  80. package/dist/types/src/util/table.d.ts +2 -1
  81. package/dist/types/src/util/table.d.ts.map +1 -1
  82. package/dist/types/tsconfig.tsbuildinfo +1 -1
  83. package/package.json +36 -34
  84. package/src/_deprecated/schema/Schema.stories.tsx +1 -1
  85. package/src/components/Table/ColumnActionsMenu.tsx +4 -4
  86. package/src/components/Table/CreateRefPanel.tsx +3 -4
  87. package/src/components/Table/Table.stories.tsx +55 -44
  88. package/src/components/Table/Table.tsx +4 -0
  89. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +1 -1
  90. package/src/components/TableToolbar/TableToolbar.tsx +72 -0
  91. package/src/components/{Toolbar → TableToolbar}/index.ts +1 -1
  92. package/src/components/index.ts +1 -1
  93. package/src/hooks/useTableModel.ts +21 -0
  94. package/src/model/table-model.test.ts +0 -5
  95. package/src/model/table-model.ts +41 -99
  96. package/src/model/table-presentation.ts +25 -4
  97. package/src/model/table-sorting.ts +215 -0
  98. package/src/playwright/smoke.spec.ts +35 -13
  99. package/src/translations.ts +2 -1
  100. package/src/types/table.ts +3 -3
  101. package/src/util/table-buttons.ts +33 -3
  102. package/src/util/table-controls.ts +2 -2
  103. package/src/util/table.ts +85 -52
  104. package/dist/lib/browser/chunk-AT7O4RAD.mjs.map +0 -7
  105. package/dist/lib/node/chunk-YSBXNBDJ.cjs.map +0 -7
  106. package/dist/lib/node-esm/chunk-6OH2NHUX.mjs.map +0 -7
  107. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -21
  108. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  109. package/dist/types/src/components/Toolbar/common.d.ts +0 -14
  110. package/dist/types/src/components/Toolbar/common.d.ts.map +0 -1
  111. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  112. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  113. package/dist/types/src/playwright/playwright.config.d.ts +0 -3
  114. package/dist/types/src/playwright/playwright.config.d.ts.map +0 -1
  115. package/src/components/Toolbar/Toolbar.tsx +0 -87
  116. package/src/components/Toolbar/common.tsx +0 -71
  117. /package/src/playwright/{playwright.config.ts → playwright.config.cts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TableSchema,
3
3
  TableType
4
- } from "./chunk-AT7O4RAD.mjs";
4
+ } from "./chunk-3FR6NHPD.mjs";
5
5
 
6
6
  // packages/ui/react-ui-table/src/translations.ts
7
7
  var translationKey = "table";
@@ -17,7 +17,7 @@ var translations_default = [
17
17
  "new schema": "New schema",
18
18
  "continue label": "Continue",
19
19
  "create stack section label": "Create table",
20
- "add row": "Add row",
20
+ "add row label": "Add row",
21
21
  "create comment": "Create thread",
22
22
  "column action sort descending": "Sort Descending",
23
23
  "column action sort ascending": "Sort Ascending",
@@ -28,7 +28,8 @@ var translations_default = [
28
28
  "bulk delete row label": "Delete selected rows",
29
29
  "column deleted label": "Column deleted",
30
30
  "new column button label": "Create column",
31
- "create new object label": "Create new object \u201C{{text}}\u201D"
31
+ "create new object label": "Create new object \u201C{{text}}\u201D",
32
+ "save view label": "Save changes to view"
32
33
  }
33
34
  }
34
35
  }
@@ -36,7 +37,7 @@ var translations_default = [
36
37
 
37
38
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
38
39
  import React7, { forwardRef, useCallback as useCallback4, useImperativeHandle, useState as useState2, useMemo as useMemo4 } from "react";
39
- import { getValue as getValue3 } from "@dxos/echo-schema";
40
+ import { getValue as getValue4 } from "@dxos/echo-schema";
40
41
  import { invariant as invariant3 } from "@dxos/invariant";
41
42
  import { Filter, getSpace as getSpace4, fullyQualifiedId } from "@dxos/react-client/echo";
42
43
  import { useTranslation as useTranslation3 } from "@dxos/react-ui";
@@ -54,7 +55,7 @@ var ColumnActionsMenu = ({ model, modals }) => {
54
55
  if (state?.type !== "column") {
55
56
  return null;
56
57
  }
57
- const currentSort = model.sorting;
58
+ const currentSort = model.sorting?.sorting;
58
59
  const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
59
60
  return /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
60
61
  modal: false,
@@ -64,13 +65,13 @@ var ColumnActionsMenu = ({ model, modals }) => {
64
65
  virtualRef: modals.trigger
65
66
  }), /* @__PURE__ */ React.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React.createElement(DropdownMenu.Content, null, /* @__PURE__ */ React.createElement(DropdownMenu.Viewport, null, (!isCurrentColumnSorted || currentSort?.direction === "asc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
66
67
  "data-testid": "column-sort-descending",
67
- onClick: () => model.setSort(state.fieldId, "desc")
68
+ onClick: () => model.sorting?.setSort(state.fieldId, "desc")
68
69
  }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
69
70
  "data-testid": "column-sort-ascending",
70
- onClick: () => model.setSort(state.fieldId, "asc")
71
+ onClick: () => model.sorting?.setSort(state.fieldId, "asc")
71
72
  }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
72
73
  "data-testid": "column-clear-sort",
73
- onClick: () => model.clearSort()
74
+ onClick: () => model.sorting?.clearSort()
74
75
  }, t("column action clear sorting")), model.getColumnCount() > 1 && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
75
76
  "data-testid": "column-delete",
76
77
  onClick: () => model.deleteColumn(state.fieldId)
@@ -164,7 +165,9 @@ var CreateRefPanel = ({ model, modals, __gridScope }) => {
164
165
  if (!space || state?.type !== "createRefPanel") {
165
166
  return;
166
167
  }
167
- const schema2 = space.db.schemaRegistry.getSchema(state.typename)?.schema;
168
+ const [schema2] = space.db.schemaRegistry.query({
169
+ typename: state.typename
170
+ }).runSync();
168
171
  if (schema2) {
169
172
  const omit = S.omit("id");
170
173
  return omit(schema2);
@@ -177,7 +180,9 @@ var CreateRefPanel = ({ model, modals, __gridScope }) => {
177
180
  if (!model || !space || state?.type !== "createRefPanel") {
178
181
  return;
179
182
  }
180
- const schema2 = space.db.schemaRegistry.getSchema(state.typename)?.schema;
183
+ const [schema2] = space.db.schemaRegistry.query({
184
+ typename: state.typename
185
+ }).runSync();
181
186
  if (schema2) {
182
187
  const obj = space.db.add(create(schema2, values));
183
188
  state.onCreate?.(obj);
@@ -281,14 +286,13 @@ var RowActionsMenu = ({ model, modals }) => {
281
286
  };
282
287
 
283
288
  // packages/ui/react-ui-table/src/model/table-model.ts
284
- import { computed as computed2, effect as effect2, signal as signal2 } from "@preact/signals-core";
285
- import orderBy from "lodash.orderby";
289
+ import { computed as computed3, effect as effect2, signal as signal3 } from "@preact/signals-core";
286
290
  import { Resource as Resource2 } from "@dxos/context";
287
- import { getValue, setValue, FormatEnum as FormatEnum2 } from "@dxos/echo-schema";
291
+ import { FormatEnum as FormatEnum3, getValue as getValue2, setValue } from "@dxos/echo-schema";
288
292
  import { invariant } from "@dxos/invariant";
289
293
  import { isReactiveObject, makeRef as makeRef2 } from "@dxos/live-object";
290
294
  import { PublicKey as PublicKey2 } from "@dxos/react-client";
291
- import { formatForDisplay, formatForEditing, parseValue } from "@dxos/react-ui-form";
295
+ import { formatForEditing, parseValue } from "@dxos/react-ui-form";
292
296
 
293
297
  // packages/ui/react-ui-table/src/model/selection-model.ts
294
298
  import { computed, effect, signal } from "@preact/signals-core";
@@ -299,73 +303,88 @@ var touch = (value) => value;
299
303
 
300
304
  // packages/ui/react-ui-table/src/util/table.ts
301
305
  import { AST, Format, FormatEnum, S as S2, TypedObject, TypeEnum } from "@dxos/echo-schema";
302
- import { log } from "@dxos/log";
303
306
  import { PublicKey } from "@dxos/react-client";
304
307
  import { create as create2, makeRef } from "@dxos/react-client/echo";
305
- import { createFieldId, createView, ViewProjection } from "@dxos/schema";
306
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/util/table.ts";
307
- var initializeTable = async ({ space, table, initialRow = true }) => {
308
- log("initializeTable", {
309
- table
310
- }, {
311
- F: __dxlog_file,
312
- L: 36,
313
- S: void 0,
314
- C: (f, a) => f(...a)
315
- });
316
- const ContactSchema = TypedObject({
317
- typename: `example.com/type/${PublicKey.random().truncate()}`,
318
- version: "0.1.0"
319
- })({
320
- name: S2.optional(S2.String).annotations({
321
- [AST.TitleAnnotationId]: "Name"
322
- }),
323
- active: S2.optional(S2.Boolean),
324
- email: S2.optional(Format.Email),
325
- salary: S2.optional(Format.Currency()).annotations({
326
- [AST.TitleAnnotationId]: "Salary"
327
- })
328
- });
329
- const [contactSchema] = await space.db.schemaRegistry.register([
330
- ContactSchema
331
- ]);
332
- table.view = makeRef(createView({
333
- name: "Test",
334
- typename: contactSchema.typename,
335
- jsonSchema: contactSchema.jsonSchema,
336
- fields: [
337
- "name",
338
- "active",
339
- "email",
340
- "salary"
341
- ]
342
- }));
343
- const projection = new ViewProjection(contactSchema, table.view.target);
344
- projection.setFieldProjection({
345
- field: {
346
- id: table.view.target.fields.find((f) => f.path === "salary").id,
347
- path: "salary",
348
- size: 150
308
+ import { createFieldId, createView, getSchemaProperties, ViewProjection } from "@dxos/schema";
309
+ var initializeTable = async ({ space, table, initialRow = true, initialSchema }) => {
310
+ if (initialSchema) {
311
+ const schema = await space.db.schemaRegistry.query({
312
+ typename: initialSchema
313
+ }).firstOrUndefined();
314
+ if (!schema) {
315
+ throw new Error(`Schema not found: ${initialSchema}`);
349
316
  }
350
- });
351
- projection.setFieldProjection({
352
- field: {
353
- id: createFieldId(),
354
- path: "manager",
355
- referencePath: "name"
356
- },
357
- props: {
358
- property: "manager",
359
- type: TypeEnum.Ref,
360
- format: FormatEnum.Ref,
361
- referenceSchema: contactSchema.typename,
362
- title: "Manager"
317
+ const fields = getSchemaProperties(schema.ast).filter((prop) => prop.format !== FormatEnum.Ref).map((prop) => prop.name);
318
+ table.view = makeRef(createView({
319
+ // TODO(ZaymonFC): Don't hardcode name?
320
+ name: "View",
321
+ typename: schema.typename,
322
+ jsonSchema: schema.jsonSchema,
323
+ fields
324
+ }));
325
+ return schema;
326
+ } else {
327
+ const ContactSchema = TypedObject({
328
+ typename: `example.com/type/${PublicKey.random().truncate()}`,
329
+ version: "0.1.0"
330
+ })({
331
+ name: S2.optional(S2.String).annotations({
332
+ [AST.TitleAnnotationId]: "Name"
333
+ }),
334
+ active: S2.optional(S2.Boolean),
335
+ email: S2.optional(Format.Email),
336
+ salary: S2.optional(Format.Currency()).annotations({
337
+ [AST.TitleAnnotationId]: "Salary"
338
+ })
339
+ });
340
+ const [contactSchema] = await space.db.schemaRegistry.register([
341
+ ContactSchema
342
+ ]);
343
+ table.view = makeRef(createView({
344
+ name: "View",
345
+ typename: contactSchema.typename,
346
+ jsonSchema: contactSchema.jsonSchema,
347
+ fields: [
348
+ "name",
349
+ "email",
350
+ "salary",
351
+ "active"
352
+ ]
353
+ }));
354
+ const projection = new ViewProjection(contactSchema, table.view.target);
355
+ projection.setFieldProjection({
356
+ field: {
357
+ id: table.view.target.fields.find((f) => f.path === "salary").id,
358
+ path: "salary",
359
+ size: 150
360
+ }
361
+ });
362
+ projection.setFieldProjection({
363
+ field: {
364
+ id: table.view.target.fields.find((f) => f.path === "active").id,
365
+ path: "active",
366
+ size: 100
367
+ }
368
+ });
369
+ projection.setFieldProjection({
370
+ field: {
371
+ id: createFieldId(),
372
+ path: "manager",
373
+ referencePath: "name"
374
+ },
375
+ props: {
376
+ property: "manager",
377
+ type: TypeEnum.Ref,
378
+ format: FormatEnum.Ref,
379
+ referenceSchema: contactSchema.typename,
380
+ title: "Manager"
381
+ }
382
+ });
383
+ if (initialRow) {
384
+ space.db.add(create2(contactSchema, {}));
363
385
  }
364
- });
365
- if (initialRow) {
366
- space.db.add(create2(contactSchema, {}));
386
+ return contactSchema;
367
387
  }
368
- return contactSchema;
369
388
  };
370
389
 
371
390
  // packages/ui/react-ui-table/src/util/table-buttons.ts
@@ -373,11 +392,13 @@ var BUTTON_IDENTIFIERS = {
373
392
  columnSettings: "data-table-column-settings-button",
374
393
  newColumn: "data-table-new-column-button",
375
394
  referencedCell: "data-table-ref-cell-button",
376
- rowMenu: "data-table-row-menu-button"
395
+ rowMenu: "data-table-row-menu-button",
396
+ sort: "data-table-sort-button"
377
397
  };
378
- var createButton = ({ attr, data, disabled = false, icon, testId }) => {
398
+ var createButton = ({ attr, data, disabled = false, icon, testId, type = "primary" }) => {
379
399
  const dataAttrs = Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
380
- return `<button ${attr} data-testid="${testId}" class="ch-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 inline-end-2" ${dataAttrs} ${disabled ? "disabled" : ""}><svg><use href="/icons.svg#${icon}"/></svg></button>`;
400
+ const positionClass = type === "primary" ? "inline-end-2" : "inline-end-9";
401
+ 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>`;
381
402
  };
382
403
  var addColumnButton = {
383
404
  attr: BUTTON_IDENTIFIERS.newColumn,
@@ -451,11 +472,35 @@ var rowMenuButton = {
451
472
  rowIndex: Number(el.getAttribute("data-row-index"))
452
473
  })
453
474
  };
475
+ var sortButton = {
476
+ attr: BUTTON_IDENTIFIERS.sort,
477
+ icon: "ph--sort-ascending--regular",
478
+ render: ({ fieldId, direction }) => {
479
+ return createButton({
480
+ attr: BUTTON_IDENTIFIERS.sort,
481
+ icon: direction === "desc" ? "ph--sort-descending--regular" : "ph--sort-ascending--regular",
482
+ data: {
483
+ "data-field-id": fieldId,
484
+ "data-direction": direction ?? ""
485
+ },
486
+ testId: "table-sort-button",
487
+ // TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
488
+ // This is a bit hacky.
489
+ type: "secondary"
490
+ });
491
+ },
492
+ getData: (el) => ({
493
+ type: "sort",
494
+ fieldId: el.getAttribute("data-field-id"),
495
+ direction: el.getAttribute("data-direction")
496
+ })
497
+ };
454
498
  var tableButtons = {
455
499
  addColumn: addColumnButton,
456
500
  columnSettings: columnSettingsButton,
457
501
  referencedCell: referencedCellButton,
458
- rowMenu: rowMenuButton
502
+ rowMenu: rowMenuButton,
503
+ sort: sortButton
459
504
  };
460
505
 
461
506
  // packages/ui/react-ui-table/src/util/table-controls.ts
@@ -464,8 +509,8 @@ var CONTROL_IDENTIFIERS = {
464
509
  switch: "data-table-switch"
465
510
  };
466
511
  var BASE_CLASSES = {
467
- checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] ch-checkbox",
468
- switch: "absolute inset-block-[.25rem] inline-end-[.25rem] ch-checkbox--switch"
512
+ checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] dx-checkbox",
513
+ switch: "absolute inset-block-[.25rem] inline-end-[.25rem] dx-checkbox--switch"
469
514
  };
470
515
  var renderAttributes = (data) => {
471
516
  return Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
@@ -592,8 +637,173 @@ var SelectionModel = class extends Resource {
592
637
  }
593
638
  };
594
639
 
640
+ // packages/ui/react-ui-table/src/model/table-sorting.ts
641
+ import { computed as computed2, signal as signal2 } from "@preact/signals-core";
642
+ import orderBy from "lodash.orderby";
643
+ import { getValue, FormatEnum as FormatEnum2, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
644
+ import { formatForDisplay } from "@dxos/react-ui-form";
645
+ var TableSorting = class {
646
+ constructor(rows, _view, _projection) {
647
+ this._view = _view;
648
+ this._projection = _projection;
649
+ this._displayToDataIndex = /* @__PURE__ */ new Map();
650
+ this._localSort = signal2(void 0);
651
+ this._rows = rows;
652
+ this._isDirty = computed2(() => {
653
+ const local = this._localSort.value;
654
+ const viewSort = this._view?.query.sort?.[0];
655
+ if (local?.type === "cleared") {
656
+ return viewSort !== void 0;
657
+ }
658
+ if (local?.type === "active") {
659
+ if (!viewSort) {
660
+ return true;
661
+ }
662
+ return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
663
+ }
664
+ return false;
665
+ });
666
+ this.sortedRows = this.initialiseSortedRows();
667
+ }
668
+ /**
669
+ * @reactive
670
+ * @returns Local sort if present, falls back to view sort.
671
+ */
672
+ get sorting() {
673
+ const local = this._localSort.value;
674
+ if (local?.type === "cleared") {
675
+ return void 0;
676
+ }
677
+ if (local?.type === "active") {
678
+ return local.sort;
679
+ }
680
+ return this._view?.query.sort?.[0];
681
+ }
682
+ /**
683
+ * @reactive
684
+ * @returns Whether local sort differs from view sort.
685
+ */
686
+ get isDirty() {
687
+ return this._isDirty.value;
688
+ }
689
+ getDataIndex(displayIndex) {
690
+ return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
691
+ }
692
+ setSort(fieldId, direction) {
693
+ this._localSort.value = {
694
+ type: "active",
695
+ sort: {
696
+ fieldId,
697
+ direction
698
+ }
699
+ };
700
+ }
701
+ toggleSort(fieldId) {
702
+ if (!this._view || !this.sorting || this.sorting.fieldId !== fieldId) {
703
+ return;
704
+ }
705
+ switch (this.sorting.direction) {
706
+ case "asc": {
707
+ this.setSort(fieldId, "desc");
708
+ break;
709
+ }
710
+ case "desc": {
711
+ this.setSort(fieldId, "asc");
712
+ break;
713
+ }
714
+ }
715
+ }
716
+ clearSort() {
717
+ this._localSort.value = {
718
+ type: "cleared"
719
+ };
720
+ }
721
+ save() {
722
+ if (this._view && this._localSort.value !== void 0) {
723
+ if (this._localSort.value.type === "active") {
724
+ this._view.query.sort = [
725
+ this._localSort.value.sort
726
+ ];
727
+ } else {
728
+ this._view.query.sort = [];
729
+ }
730
+ this._localSort.value = void 0;
731
+ }
732
+ }
733
+ //
734
+ // Initialisation.
735
+ //
736
+ initialiseSortedRows() {
737
+ return computed2(() => {
738
+ this._displayToDataIndex.clear();
739
+ const sort = this.sorting;
740
+ if (!sort || !this._view) {
741
+ return this._rows.value;
742
+ }
743
+ const field = this._view.fields.find((f) => f.id === sort.fieldId);
744
+ if (!field) {
745
+ return this._rows.value;
746
+ }
747
+ const { props } = this._projection.getFieldProjection(field.id);
748
+ const dataWithIndices = this._rows.value.map((item, index) => {
749
+ const sortValue = this.getSortValue(props, field, item);
750
+ const isEmpty = !(props.type === TypeEnum2.Boolean) && (sortValue == null || sortValue === "");
751
+ return {
752
+ item,
753
+ index,
754
+ value: sortValue,
755
+ isEmpty
756
+ };
757
+ });
758
+ const ordered = orderBy(dataWithIndices, [
759
+ "isEmpty",
760
+ "value"
761
+ ], [
762
+ "asc",
763
+ sort.direction
764
+ ]);
765
+ ordered.forEach(({ index: dataIndex }, displayIndex) => {
766
+ if (displayIndex !== dataIndex) {
767
+ this._displayToDataIndex.set(displayIndex, dataIndex);
768
+ }
769
+ });
770
+ return ordered.map(({ item }) => item);
771
+ });
772
+ }
773
+ getSortValue(props, field, item) {
774
+ const { type, format } = props;
775
+ const value = getValue(item, field.path);
776
+ if (type === TypeEnum2.Boolean) {
777
+ return !!value;
778
+ }
779
+ if (type === TypeEnum2.Number) {
780
+ return value;
781
+ }
782
+ if (value == null) {
783
+ return value;
784
+ }
785
+ if (format === FormatEnum2.SingleSelect) {
786
+ const options = props.options;
787
+ if (!options) {
788
+ return void 0;
789
+ }
790
+ const option = options.find((option2) => option2.id === value);
791
+ return option ? options.indexOf(option) : void 0;
792
+ }
793
+ if (format === FormatEnum2.Ref && field.referencePath) {
794
+ const refValue = getValue(value.target, field.referencePath);
795
+ return refValue;
796
+ }
797
+ return formatForDisplay({
798
+ type,
799
+ format,
800
+ value
801
+ });
802
+ }
803
+ };
804
+
595
805
  // packages/ui/react-ui-table/src/model/table-model.ts
596
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
806
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
597
807
  var TableModel = class extends Resource2 {
598
808
  constructor({ table, projection, sorting = [], pinnedRows = {
599
809
  top: [],
@@ -601,7 +811,7 @@ var TableModel = class extends Resource2 {
601
811
  }, onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChanged }) {
602
812
  super();
603
813
  this.id = `table-model-${PublicKey2.random().truncate()}`;
604
- this._visibleRange = signal2({
814
+ this._visibleRange = signal3({
605
815
  start: {
606
816
  row: 0,
607
817
  col: 0
@@ -611,14 +821,7 @@ var TableModel = class extends Resource2 {
611
821
  col: 0
612
822
  }
613
823
  });
614
- /**
615
- * Maps display indices to data indices.
616
- * Used for translating between sorted/displayed order and original data order.
617
- * Keys are display indices, values are corresponding data indices.
618
- */
619
- this._displayToDataIndex = /* @__PURE__ */ new Map();
620
- this._sorting = signal2(void 0);
621
- this._rows = signal2([]);
824
+ this._rows = signal3([]);
622
825
  //
623
826
  // Data
624
827
  //
@@ -628,11 +831,11 @@ var TableModel = class extends Resource2 {
628
831
  this.getRowCount = () => this._rows.value.length;
629
832
  this.getColumnCount = () => this.table.view?.target?.fields.length ?? 0;
630
833
  this.insertRow = (rowIndex) => {
631
- const row = rowIndex !== void 0 ? this._displayToDataIndex.get(rowIndex) ?? rowIndex : this._rows.value.length;
834
+ const row = rowIndex !== void 0 ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
632
835
  this._onInsertRow?.(row);
633
836
  };
634
837
  this.deleteRow = (rowIndex) => {
635
- const row = this._displayToDataIndex.get(rowIndex) ?? rowIndex;
838
+ const row = this._sorting.getDataIndex(rowIndex);
636
839
  const obj = this._rows.value[row];
637
840
  const objectsToDelete = [];
638
841
  if (this._selection.hasSelection.value) {
@@ -649,18 +852,18 @@ var TableModel = class extends Resource2 {
649
852
  return void 0;
650
853
  }
651
854
  const field = fields[col];
652
- const dataIndex = this._displayToDataIndex.get(row) ?? row;
653
- const value = getValue(this._rows.value[dataIndex], field.path);
855
+ const dataIndex = this._sorting.getDataIndex(row);
856
+ const value = getValue2(this._rows.value[dataIndex], field.path);
654
857
  if (value == null) {
655
858
  return "";
656
859
  }
657
860
  const { props } = this._projection.getFieldProjection(field.id);
658
861
  switch (props.format) {
659
- case FormatEnum2.Ref: {
862
+ case FormatEnum3.Ref: {
660
863
  if (!field.referencePath) {
661
864
  return "";
662
865
  }
663
- return getValue(value.target, field.referencePath);
866
+ return getValue2(value.target, field.referencePath);
664
867
  }
665
868
  default: {
666
869
  return formatForEditing({
@@ -672,7 +875,7 @@ var TableModel = class extends Resource2 {
672
875
  }
673
876
  };
674
877
  this.setCellData = ({ col, row }, value) => {
675
- const rowIdx = this._displayToDataIndex.get(row) ?? row;
878
+ const rowIdx = this._sorting.getDataIndex(row);
676
879
  const fields = this.table.view?.target?.fields ?? [];
677
880
  if (col < 0 || col >= fields.length) {
678
881
  return;
@@ -680,7 +883,7 @@ var TableModel = class extends Resource2 {
680
883
  const field = fields[col];
681
884
  const { props } = this._projection.getFieldProjection(field.id);
682
885
  switch (props.format) {
683
- case FormatEnum2.Ref: {
886
+ case FormatEnum3.Ref: {
684
887
  if (isReactiveObject(value)) {
685
888
  setValue(this._rows.value[rowIdx], field.path, makeRef2(value));
686
889
  }
@@ -698,7 +901,11 @@ var TableModel = class extends Resource2 {
698
901
  };
699
902
  this._table = table;
700
903
  this._projection = projection;
701
- this._sorting.value = sorting.at(0);
904
+ this._sorting = new TableSorting(this._rows, table.view?.target, projection);
905
+ if (sorting.length > 0) {
906
+ const [sort] = sorting;
907
+ this._sorting.setSort(sort.fieldId, sort.direction);
908
+ }
702
909
  this._pinnedRows = pinnedRows;
703
910
  this._onInsertRow = onInsertRow;
704
911
  this._onDeleteRows = onDeleteRows;
@@ -713,14 +920,20 @@ var TableModel = class extends Resource2 {
713
920
  return this._projection;
714
921
  }
715
922
  get rows() {
716
- return this._sortedRows;
923
+ return this._sorting.sortedRows;
924
+ }
925
+ /**
926
+ * @reactive
927
+ */
928
+ get sorting() {
929
+ return this._sorting;
717
930
  }
718
931
  get pinnedRows() {
719
932
  return this._pinnedRows;
720
933
  }
721
934
  get columnMeta() {
722
935
  invariant(this._columnMeta, void 0, {
723
- F: __dxlog_file2,
936
+ F: __dxlog_file,
724
937
  L: 117,
725
938
  S: this,
726
939
  A: [
@@ -730,24 +943,24 @@ var TableModel = class extends Resource2 {
730
943
  });
731
944
  return this._columnMeta;
732
945
  }
733
- get sorting() {
734
- return this._sorting.value;
735
- }
736
946
  get selection() {
737
947
  return this._selection;
738
948
  }
949
+ /** @reactive */
950
+ get isViewDirty() {
951
+ return this._sorting.isDirty;
952
+ }
739
953
  //
740
954
  // Initialisation
741
955
  //
742
956
  async _open() {
743
957
  this.initializeColumnMeta();
744
- this.initializeSorting();
745
958
  this.initializeEffects();
746
- this._selection = new SelectionModel(this._sortedRows, () => this._onRowOrderChanged?.());
959
+ this._selection = new SelectionModel(this._sorting.sortedRows, () => this._onRowOrderChanged?.());
747
960
  await this._selection.open(this._ctx);
748
961
  }
749
962
  initializeColumnMeta() {
750
- this._columnMeta = computed2(() => {
963
+ this._columnMeta = computed3(() => {
751
964
  const fields = this._table.view?.target?.fields ?? [];
752
965
  const meta = Object.fromEntries(fields.map((field, index) => [
753
966
  index,
@@ -773,60 +986,9 @@ var TableModel = class extends Resource2 {
773
986
  };
774
987
  });
775
988
  }
776
- initializeSorting() {
777
- this._sortedRows = computed2(() => {
778
- this._displayToDataIndex.clear();
779
- const sort = this._sorting.value;
780
- if (!sort) {
781
- return this._rows.value;
782
- }
783
- const field = this._table.view?.target?.fields.find((field2) => field2.id === sort.fieldId);
784
- if (!field) {
785
- return this._rows.value;
786
- }
787
- const { props } = this._projection.getFieldProjection(field.id);
788
- const dataWithIndices = this._rows.value.map((item, index) => {
789
- const rawValue = getValue(item, field.path);
790
- let sortValue = rawValue;
791
- if (props.type.startsWith("boolean")) {
792
- sortValue = !!rawValue;
793
- } else if (rawValue != null) {
794
- if (props.format === FormatEnum2.Ref && field.referencePath) {
795
- sortValue = getValue(rawValue, field.referencePath);
796
- } else {
797
- sortValue = formatForDisplay({
798
- type: props.type,
799
- format: props.format,
800
- value: rawValue
801
- });
802
- }
803
- }
804
- return {
805
- item,
806
- index,
807
- value: sortValue,
808
- isEmpty: !props.type.startsWith("boolean") && (sortValue == null || sortValue === "")
809
- };
810
- });
811
- const sorted = orderBy(dataWithIndices, [
812
- "isEmpty",
813
- "value"
814
- ], [
815
- "asc",
816
- sort.direction
817
- ]);
818
- for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
819
- const { index: dataIndex } = sorted[displayIndex];
820
- if (displayIndex !== dataIndex) {
821
- this._displayToDataIndex.set(displayIndex, dataIndex);
822
- }
823
- }
824
- return sorted.map(({ item }) => item);
825
- });
826
- }
827
989
  initializeEffects() {
828
990
  const rowOrderWatcher = effect2(() => {
829
- touch(this._sortedRows.value);
991
+ touch(this._sorting.sortedRows.value);
830
992
  this._onRowOrderChanged?.();
831
993
  });
832
994
  this._ctx.onDispose(rowOrderWatcher);
@@ -835,8 +997,8 @@ var TableModel = class extends Resource2 {
835
997
  const rowEffects = [];
836
998
  for (let row = start.row; row <= end.row; row++) {
837
999
  rowEffects.push(effect2(() => {
838
- const obj = this._sortedRows.value[row];
839
- this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
1000
+ const obj = this._sorting.sortedRows.value[row];
1001
+ this?._table?.view?.target?.fields.forEach((field) => touch(getValue2(obj, field.path)));
840
1002
  this._onCellUpdate?.({
841
1003
  row,
842
1004
  col: start.col,
@@ -854,10 +1016,10 @@ var TableModel = class extends Resource2 {
854
1016
  * @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
855
1017
  */
856
1018
  updateCellData({ col, row }, update) {
857
- const dataRow = this._displayToDataIndex.get(row) ?? row;
1019
+ const dataRow = this._sorting.getDataIndex(row);
858
1020
  const fields = this.table.view?.target?.fields ?? [];
859
1021
  const field = fields[col];
860
- const value = getValue(this._rows.value[dataRow], field.path);
1022
+ const value = getValue2(this._rows.value[dataRow], field.path);
861
1023
  const updatedValue = update(value);
862
1024
  setValue(this._rows.value[dataRow], field.path, updatedValue);
863
1025
  }
@@ -870,9 +1032,6 @@ var TableModel = class extends Resource2 {
870
1032
  }
871
1033
  const field = this.table.view.target?.fields.find((field2) => field2.id === fieldId);
872
1034
  if (field && this._onDeleteColumn) {
873
- if (this._sorting.value?.fieldId === fieldId) {
874
- this.clearSort();
875
- }
876
1035
  this._onDeleteColumn(field.id);
877
1036
  }
878
1037
  }
@@ -890,18 +1049,6 @@ var TableModel = class extends Resource2 {
890
1049
  }
891
1050
  }
892
1051
  //
893
- // Sorting
894
- //
895
- setSort(fieldId, direction) {
896
- this._sorting.value = {
897
- fieldId,
898
- direction
899
- };
900
- }
901
- clearSort() {
902
- this._sorting.value = void 0;
903
- }
904
- //
905
1052
  // Pinning
906
1053
  //
907
1054
  // TODO(burdon): Change to setPinned(on/off).
@@ -912,15 +1059,21 @@ var TableModel = class extends Resource2 {
912
1059
  this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
913
1060
  this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
914
1061
  }
1062
+ //
1063
+ // View operations
1064
+ //
1065
+ saveView() {
1066
+ this._sorting.save();
1067
+ }
915
1068
  };
916
1069
 
917
1070
  // packages/ui/react-ui-table/src/model/modal-controller.ts
918
- import { signal as signal3 } from "@preact/signals-core";
919
- import { log as log2 } from "@dxos/log";
920
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
1071
+ import { signal as signal4 } from "@preact/signals-core";
1072
+ import { log } from "@dxos/log";
1073
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
921
1074
  var ModalController = class {
922
1075
  constructor() {
923
- this._state = signal3({
1076
+ this._state = signal4({
924
1077
  type: "closed"
925
1078
  });
926
1079
  this._triggerRef = {
@@ -980,8 +1133,8 @@ var ModalController = class {
980
1133
  onCreate
981
1134
  };
982
1135
  } else {
983
- log2.warn("anchor cell not found while creating new ref", void 0, {
984
- F: __dxlog_file3,
1136
+ log.warn("anchor cell not found while creating new ref", void 0, {
1137
+ F: __dxlog_file2,
985
1138
  L: 86,
986
1139
  S: this,
987
1140
  C: (f, a) => f(...a)
@@ -1003,14 +1156,14 @@ var ModalController = class {
1003
1156
  };
1004
1157
 
1005
1158
  // packages/ui/react-ui-table/src/model/table-presentation.ts
1006
- import { signal as signal4 } from "@preact/signals-core";
1007
- import { FormatEnum as FormatEnum3, getValue as getValue2 } from "@dxos/echo-schema";
1159
+ import { signal as signal5 } from "@preact/signals-core";
1160
+ import { FormatEnum as FormatEnum4, getValue as getValue3 } from "@dxos/echo-schema";
1008
1161
  import { cellClassesForFieldType, cellClassesForRowSelection, formatForDisplay as formatForDisplay2 } from "@dxos/react-ui-form";
1009
1162
  import { toPlaneCellIndex } from "@dxos/react-ui-grid";
1010
1163
  import { mx } from "@dxos/react-ui-theme";
1011
1164
  import { VIEW_FIELD_LIMIT } from "@dxos/schema";
1012
1165
  var TablePresentation = class {
1013
- constructor(model, _visibleRange = signal4({
1166
+ constructor(model, _visibleRange = signal5({
1014
1167
  start: {
1015
1168
  row: 0,
1016
1169
  col: 0
@@ -1049,19 +1202,20 @@ var TablePresentation = class {
1049
1202
  const { props } = this.model.projection.getFieldProjection(field.id);
1050
1203
  const cell = {
1051
1204
  get value() {
1052
- const value = getValue2(obj, field.path);
1205
+ const value = getValue3(obj, field.path);
1053
1206
  if (value == null) {
1054
1207
  return "";
1055
1208
  }
1056
1209
  switch (props.format) {
1057
- case FormatEnum3.Boolean: {
1210
+ case FormatEnum4.Boolean:
1211
+ case FormatEnum4.SingleSelect: {
1058
1212
  return "";
1059
1213
  }
1060
- case FormatEnum3.Ref: {
1214
+ case FormatEnum4.Ref: {
1061
1215
  if (!field.referencePath) {
1062
1216
  return "";
1063
1217
  }
1064
- return getValue2(value.target, field.referencePath);
1218
+ return getValue3(value.target, field.referencePath);
1065
1219
  }
1066
1220
  default: {
1067
1221
  return formatForDisplay2({
@@ -1088,8 +1242,8 @@ var TablePresentation = class {
1088
1242
  if (classes.length > 0) {
1089
1243
  cell.className = mx(classes.flat());
1090
1244
  }
1091
- if (props.format === FormatEnum3.Ref && props.referenceSchema) {
1092
- const targetObj = getValue2(obj, field.path)?.target;
1245
+ if (props.format === FormatEnum4.Ref && props.referenceSchema) {
1246
+ const targetObj = getValue3(obj, field.path)?.target;
1093
1247
  if (targetObj) {
1094
1248
  cell.accessoryHtml = tableButtons.referencedCell.render({
1095
1249
  targetId: targetObj.id,
@@ -1097,8 +1251,8 @@ var TablePresentation = class {
1097
1251
  });
1098
1252
  }
1099
1253
  }
1100
- if (props.format === FormatEnum3.Boolean) {
1101
- const value = getValue2(obj, field.path);
1254
+ if (props.format === FormatEnum4.Boolean) {
1255
+ const value = getValue3(obj, field.path);
1102
1256
  cell.accessoryHtml = tableControls.switch.render({
1103
1257
  colIndex,
1104
1258
  rowIndex: displayIndex,
@@ -1106,6 +1260,16 @@ var TablePresentation = class {
1106
1260
  });
1107
1261
  cell.readonly = true;
1108
1262
  }
1263
+ if (props.format === FormatEnum4.SingleSelect) {
1264
+ const value = getValue3(obj, field.path);
1265
+ const options = this.model.projection.getFieldProjection(field.id).props.options;
1266
+ if (options) {
1267
+ const option = options.find((o) => o.id === value);
1268
+ if (option) {
1269
+ cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
1270
+ }
1271
+ }
1272
+ }
1109
1273
  const idx = toPlaneCellIndex({
1110
1274
  col: colIndex,
1111
1275
  row: displayIndex
@@ -1128,6 +1292,8 @@ var TablePresentation = class {
1128
1292
  const fields = this.model.table.view?.target?.fields ?? [];
1129
1293
  for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1130
1294
  const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
1295
+ const sorting = this.model.sorting?.sorting;
1296
+ const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
1131
1297
  cells[toPlaneCellIndex({
1132
1298
  col,
1133
1299
  row: 0
@@ -1136,9 +1302,16 @@ var TablePresentation = class {
1136
1302
  value: props.title ?? field.path,
1137
1303
  readonly: true,
1138
1304
  resizeHandle: "col",
1139
- accessoryHtml: tableButtons.columnSettings.render({
1305
+ accessoryHtml: `
1306
+ ${direction !== void 0 ? tableButtons.sort.render({
1307
+ fieldId: field.id,
1308
+ direction
1309
+ }) : ""}
1310
+ ${tableButtons.columnSettings.render({
1140
1311
  fieldId: field.id
1141
- })
1312
+ })}
1313
+ `,
1314
+ className: "!bg-gridHeader"
1142
1315
  };
1143
1316
  }
1144
1317
  return cells;
@@ -1188,13 +1361,14 @@ var TablePresentation = class {
1188
1361
  col: 0,
1189
1362
  row: 0
1190
1363
  })]: {
1191
- value: "",
1192
1364
  accessoryHtml: tableControls.checkbox.render({
1193
1365
  rowIndex: 0,
1194
1366
  header: true,
1195
1367
  checked: this.model.selection.allRowsSeleted.value
1196
1368
  }),
1197
- readonly: true
1369
+ className: "!bg-gridHeader",
1370
+ readonly: true,
1371
+ value: ""
1198
1372
  }
1199
1373
  };
1200
1374
  }
@@ -1204,11 +1378,12 @@ var TablePresentation = class {
1204
1378
  col: 0,
1205
1379
  row: 0
1206
1380
  })]: {
1207
- value: "",
1208
1381
  accessoryHtml: tableButtons.addColumn.render({
1209
1382
  disabled: (this.model.table.view?.target?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT
1210
1383
  }),
1211
- readonly: true
1384
+ className: "!bg-gridHeader",
1385
+ readonly: true,
1386
+ value: ""
1212
1387
  }
1213
1388
  };
1214
1389
  }
@@ -1216,7 +1391,7 @@ var TablePresentation = class {
1216
1391
 
1217
1392
  // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
1218
1393
  import React6, { useCallback as useCallback3, useMemo as useMemo3, useRef } from "react";
1219
- import { FormatEnum as FormatEnum4 } from "@dxos/echo-schema";
1394
+ import { FormatEnum as FormatEnum5 } from "@dxos/echo-schema";
1220
1395
  import { invariant as invariant2 } from "@dxos/invariant";
1221
1396
  import { cellQuery, editorKeys, parseCellIndex, useGridContext as useGridContext2, GridCellEditor } from "@dxos/react-ui-grid";
1222
1397
 
@@ -1260,7 +1435,7 @@ var completion = ({ debug, onQuery, onMatch }) => {
1260
1435
  };
1261
1436
 
1262
1437
  // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
1263
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
1438
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
1264
1439
  var newValue = Symbol.for("newValue");
1265
1440
  var createOption = (text) => ({
1266
1441
  [newValue]: true,
@@ -1278,7 +1453,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
1278
1453
  const field = model.projection.view.fields[col];
1279
1454
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
1280
1455
  invariant2(fieldProjection2, void 0, {
1281
- F: __dxlog_file4,
1456
+ F: __dxlog_file3,
1282
1457
  L: 67,
1283
1458
  S: void 0,
1284
1459
  A: [
@@ -1352,7 +1527,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
1352
1527
  ];
1353
1528
  if (onQuery) {
1354
1529
  switch (fieldProjection.props.format) {
1355
- case FormatEnum4.Ref: {
1530
+ case FormatEnum5.Ref: {
1356
1531
  extension2.push([
1357
1532
  completion({
1358
1533
  onQuery: (text) => onQuery(fieldProjection, text),
@@ -1428,7 +1603,7 @@ var determineNavigationDelta = ({ key, shift }) => {
1428
1603
  };
1429
1604
 
1430
1605
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
1431
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
1606
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
1432
1607
  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";
1433
1608
  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";
1434
1609
  var frozen = {
@@ -1496,6 +1671,10 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1496
1671
  modals.showReferencePanel(data.targetId, data.schemaId);
1497
1672
  break;
1498
1673
  }
1674
+ case "sort": {
1675
+ model?.sorting?.toggleSort(data.fieldId);
1676
+ break;
1677
+ }
1499
1678
  }
1500
1679
  return;
1501
1680
  }
@@ -1596,8 +1775,8 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1596
1775
  if (model && props.referenceSchema && field.referencePath) {
1597
1776
  const space = getSpace4(model.table);
1598
1777
  invariant3(space, void 0, {
1599
- F: __dxlog_file5,
1600
- L: 260,
1778
+ F: __dxlog_file4,
1779
+ L: 264,
1601
1780
  S: void 0,
1602
1781
  A: [
1603
1782
  "space",
@@ -1608,7 +1787,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1608
1787
  if (schema) {
1609
1788
  const { objects } = await space.db.query(Filter.schema(schema)).run();
1610
1789
  const options = objects.map((obj) => {
1611
- const value = getValue3(obj, field.referencePath);
1790
+ const value = getValue4(obj, field.referencePath);
1612
1791
  if (!value || typeof value !== "string") {
1613
1792
  return void 0;
1614
1793
  }
@@ -1681,83 +1860,83 @@ var Table = {
1681
1860
  Main: TableMain
1682
1861
  };
1683
1862
 
1684
- // packages/ui/react-ui-table/src/components/Toolbar/Toolbar.tsx
1685
- import { createContext } from "@radix-ui/react-context";
1686
- import React9 from "react";
1687
- import { Toolbar as NaturalToolbar2, useTranslation as useTranslation4 } from "@dxos/react-ui";
1688
-
1689
- // packages/ui/react-ui-table/src/components/Toolbar/common.tsx
1690
- import React8 from "react";
1691
- import { Icon, Toolbar as NaturalToolbar, Tooltip } from "@dxos/react-ui";
1692
- var buttonStyles = "min-bs-0 p-2";
1693
- var tooltipProps = {
1694
- side: "top",
1695
- classNames: "z-10"
1696
- };
1697
- var ToolbarSeparator = () => /* @__PURE__ */ React8.createElement("div", {
1698
- role: "separator",
1699
- className: "grow"
1700
- });
1701
- var ToolbarButton = ({ icon, children, ...props }) => {
1702
- return /* @__PURE__ */ React8.createElement(Tooltip.Root, null, /* @__PURE__ */ React8.createElement(Tooltip.Trigger, {
1703
- asChild: true
1704
- }, /* @__PURE__ */ React8.createElement(NaturalToolbar.Button, {
1705
- variant: "ghost",
1706
- ...props,
1707
- classNames: buttonStyles
1708
- }, /* @__PURE__ */ React8.createElement(Icon, {
1709
- icon,
1710
- size: 5
1711
- }), /* @__PURE__ */ React8.createElement("span", {
1712
- className: "sr-only"
1713
- }, children))), /* @__PURE__ */ React8.createElement(Tooltip.Portal, null, /* @__PURE__ */ React8.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React8.createElement(Tooltip.Arrow, null))));
1714
- };
1715
-
1716
- // packages/ui/react-ui-table/src/components/Toolbar/Toolbar.tsx
1717
- var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
1718
- var ToolbarRoot = ({ classNames, children, onAction }) => {
1719
- return /* @__PURE__ */ React9.createElement(ToolbarContextProvider, {
1720
- onAction
1721
- }, /* @__PURE__ */ React9.createElement(NaturalToolbar2.Root, {
1722
- classNames: [
1723
- "is-full shrink-0 overflow-x-auto overflow-y-hidden p-1 attention-surface",
1724
- classNames
1725
- ]
1726
- }, children));
1727
- };
1728
- var Editing = () => {
1729
- const { onAction } = useToolbarContext("Editing");
1730
- const { t } = useTranslation4(translationKey);
1731
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(ToolbarButton, {
1732
- value: "add-row",
1863
+ // packages/ui/react-ui-table/src/components/TableToolbar/TableToolbar.tsx
1864
+ import React8, { useCallback as useCallback5, useMemo as useMemo5 } from "react";
1865
+ import { create as create3 } from "@dxos/live-object";
1866
+ import { createGapSeparator, createMenuAction, MenuProvider, ToolbarMenu, useMenuActions } from "@dxos/react-ui-menu";
1867
+ var createTableToolbarActions = (state) => {
1868
+ const add = createMenuAction("add-row", {
1869
+ type: "add-row",
1733
1870
  icon: "ph--plus--regular",
1734
- "data-testid": "table.toolbar.add-row",
1735
- onClick: () => onAction?.({
1736
- type: "add-row"
1737
- })
1738
- }, t("add row")));
1739
- };
1740
- var Actions = () => {
1741
- const { onAction } = useToolbarContext("Actions");
1742
- const { t } = useTranslation4(translationKey);
1743
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(ToolbarButton, {
1744
- value: "comment",
1871
+ label: [
1872
+ "add row",
1873
+ {
1874
+ ns: translationKey
1875
+ }
1876
+ ],
1877
+ testId: "table.toolbar.add-row"
1878
+ });
1879
+ const save = 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 = createGapSeparator();
1893
+ const comment = createMenuAction("comment", {
1894
+ type: "comment",
1745
1895
  icon: "ph--chat-text--regular",
1746
- "data-testid": "table.toolbar.comment",
1747
- onClick: () => onAction?.({
1748
- type: "comment"
1749
- })
1750
- }, t("create comment")));
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
+ };
1751
1917
  };
1752
- var Toolbar = {
1753
- Root: ToolbarRoot,
1754
- Editing,
1755
- Actions,
1756
- Separator: ToolbarSeparator
1918
+ var TableToolbar = ({ classNames, viewDirty, attendableId, onAction }) => {
1919
+ const state = useMemo5(() => create3({
1920
+ viewDirty
1921
+ }), []);
1922
+ const actionsCreator = useCallback5(() => createTableToolbarActions(state), [
1923
+ state
1924
+ ]);
1925
+ const menu = useMenuActions(actionsCreator);
1926
+ return /* @__PURE__ */ React8.createElement(MenuProvider, {
1927
+ ...menu,
1928
+ attendableId,
1929
+ onAction
1930
+ }, /* @__PURE__ */ React8.createElement(ToolbarMenu, {
1931
+ classNames
1932
+ }));
1757
1933
  };
1758
1934
 
1759
1935
  // packages/ui/react-ui-table/src/hooks/useTableModel.ts
1936
+ import { effect as effect3 } from "@preact/signals-core";
1760
1937
  import { useEffect as useEffect2, useState as useState3 } from "react";
1938
+ import { useSelectionActions } from "@dxos/react-ui-attention";
1939
+ import { isNonNullable } from "@dxos/util";
1761
1940
  var useTableModel = ({ objects, table, projection, ...props }) => {
1762
1941
  const [model, setModel] = useState3();
1763
1942
  useEffect2(() => {
@@ -1790,6 +1969,26 @@ var useTableModel = ({ objects, table, projection, ...props }) => {
1790
1969
  model,
1791
1970
  objects
1792
1971
  ]);
1972
+ const { select, clear } = useSelectionActions([
1973
+ table?.id,
1974
+ table?.view?.target?.query.type
1975
+ ].filter(isNonNullable));
1976
+ useEffect2(() => {
1977
+ if (!model) {
1978
+ return;
1979
+ }
1980
+ const unsubscribe = effect3(() => {
1981
+ select([
1982
+ ...model.selection.selection.value
1983
+ ]);
1984
+ });
1985
+ return () => {
1986
+ clear();
1987
+ unsubscribe();
1988
+ };
1989
+ }, [
1990
+ model
1991
+ ]);
1793
1992
  return model;
1794
1993
  };
1795
1994
  export {
@@ -1800,8 +1999,8 @@ export {
1800
1999
  TableModel,
1801
2000
  TablePresentation,
1802
2001
  TableSchema,
2002
+ TableToolbar,
1803
2003
  TableType,
1804
- Toolbar,
1805
2004
  initializeTable,
1806
2005
  renderCheckbox,
1807
2006
  renderSwitch,
@@ -1809,7 +2008,6 @@ export {
1809
2008
  tableControls,
1810
2009
  touch,
1811
2010
  translations_default as translations,
1812
- useTableModel,
1813
- useToolbarContext
2011
+ useTableModel
1814
2012
  };
1815
2013
  //# sourceMappingURL=index.mjs.map