@dxos/react-ui-table 0.7.5-main.9cb18ac → 0.7.5-main.9d2a38b

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 (67) hide show
  1. package/dist/lib/browser/{chunk-AT7O4RAD.mjs → chunk-T7CTRLHA.mjs} +2 -2
  2. package/dist/lib/browser/{chunk-AT7O4RAD.mjs.map → chunk-T7CTRLHA.mjs.map} +1 -1
  3. package/dist/lib/browser/index.mjs +418 -276
  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-XQRKYO7Q.cjs} +6 -6
  8. package/dist/lib/node/chunk-XQRKYO7Q.cjs.map +7 -0
  9. package/dist/lib/node/index.cjs +410 -262
  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-6HE6MLBK.mjs} +2 -2
  15. package/dist/lib/node-esm/{chunk-6OH2NHUX.mjs.map → chunk-6HE6MLBK.mjs.map} +1 -1
  16. package/dist/lib/node-esm/index.mjs +418 -276
  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/components/Table/Table.d.ts.map +1 -1
  21. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +18 -0
  23. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -0
  24. package/dist/types/src/components/TableToolbar/index.d.ts +2 -0
  25. package/dist/types/src/components/TableToolbar/index.d.ts.map +1 -0
  26. package/dist/types/src/components/index.d.ts +1 -1
  27. package/dist/types/src/components/index.d.ts.map +1 -1
  28. package/dist/types/src/components/testing.d.ts +1 -1
  29. package/dist/types/src/model/table-model.d.ts +11 -19
  30. package/dist/types/src/model/table-model.d.ts.map +1 -1
  31. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  32. package/dist/types/src/model/table-sorting.d.ts +53 -0
  33. package/dist/types/src/model/table-sorting.d.ts.map +1 -0
  34. package/dist/types/src/translations.d.ts +2 -1
  35. package/dist/types/src/translations.d.ts.map +1 -1
  36. package/dist/types/src/types/table.d.ts +3 -3
  37. package/dist/types/src/types/table.d.ts.map +1 -1
  38. package/dist/types/src/util/table-buttons.d.ts +15 -1
  39. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  40. package/dist/types/src/util/table.d.ts +2 -1
  41. package/dist/types/src/util/table.d.ts.map +1 -1
  42. package/dist/types/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +30 -29
  44. package/src/components/Table/ColumnActionsMenu.tsx +4 -4
  45. package/src/components/Table/Table.stories.tsx +33 -21
  46. package/src/components/Table/Table.tsx +4 -0
  47. package/src/components/TableToolbar/TableToolbar.tsx +72 -0
  48. package/src/components/{Toolbar → TableToolbar}/index.ts +1 -1
  49. package/src/components/index.ts +1 -1
  50. package/src/model/table-model.test.ts +0 -5
  51. package/src/model/table-model.ts +40 -98
  52. package/src/model/table-presentation.ts +7 -1
  53. package/src/model/table-sorting.ts +201 -0
  54. package/src/playwright/smoke.spec.ts +18 -11
  55. package/src/translations.ts +2 -1
  56. package/src/types/table.ts +1 -1
  57. package/src/util/table-buttons.ts +33 -3
  58. package/src/util/table.ts +78 -52
  59. package/dist/lib/node/chunk-YSBXNBDJ.cjs.map +0 -7
  60. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -21
  61. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  62. package/dist/types/src/components/Toolbar/common.d.ts +0 -14
  63. package/dist/types/src/components/Toolbar/common.d.ts.map +0 -1
  64. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  65. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  66. package/src/components/Toolbar/Toolbar.tsx +0 -87
  67. package/src/components/Toolbar/common.tsx +0 -71
@@ -2,7 +2,7 @@ import { createRequire } from 'node:module';const require = createRequire(import
2
2
  import {
3
3
  TableSchema,
4
4
  TableType
5
- } from "./chunk-6OH2NHUX.mjs";
5
+ } from "./chunk-6HE6MLBK.mjs";
6
6
 
7
7
  // packages/ui/react-ui-table/src/translations.ts
8
8
  var translationKey = "table";
@@ -18,7 +18,7 @@ var translations_default = [
18
18
  "new schema": "New schema",
19
19
  "continue label": "Continue",
20
20
  "create stack section label": "Create table",
21
- "add row": "Add row",
21
+ "add row label": "Add row",
22
22
  "create comment": "Create thread",
23
23
  "column action sort descending": "Sort Descending",
24
24
  "column action sort ascending": "Sort Ascending",
@@ -29,7 +29,8 @@ var translations_default = [
29
29
  "bulk delete row label": "Delete selected rows",
30
30
  "column deleted label": "Column deleted",
31
31
  "new column button label": "Create column",
32
- "create new object label": "Create new object \u201C{{text}}\u201D"
32
+ "create new object label": "Create new object \u201C{{text}}\u201D",
33
+ "save view label": "Save changes to view"
33
34
  }
34
35
  }
35
36
  }
@@ -37,7 +38,7 @@ var translations_default = [
37
38
 
38
39
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
39
40
  import React7, { forwardRef, useCallback as useCallback4, useImperativeHandle, useState as useState2, useMemo as useMemo4 } from "react";
40
- import { getValue as getValue3 } from "@dxos/echo-schema";
41
+ import { getValue as getValue4 } from "@dxos/echo-schema";
41
42
  import { invariant as invariant3 } from "@dxos/invariant";
42
43
  import { Filter, getSpace as getSpace4, fullyQualifiedId } from "@dxos/react-client/echo";
43
44
  import { useTranslation as useTranslation3 } from "@dxos/react-ui";
@@ -55,7 +56,7 @@ var ColumnActionsMenu = ({ model, modals }) => {
55
56
  if (state?.type !== "column") {
56
57
  return null;
57
58
  }
58
- const currentSort = model.sorting;
59
+ const currentSort = model.sorting?.sorting;
59
60
  const isCurrentColumnSorted = currentSort?.fieldId === state.fieldId;
60
61
  return /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
61
62
  modal: false,
@@ -65,13 +66,13 @@ var ColumnActionsMenu = ({ model, modals }) => {
65
66
  virtualRef: modals.trigger
66
67
  }), /* @__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, {
67
68
  "data-testid": "column-sort-descending",
68
- onClick: () => model.setSort(state.fieldId, "desc")
69
+ onClick: () => model.sorting?.setSort(state.fieldId, "desc")
69
70
  }, t("column action sort descending")), (!isCurrentColumnSorted || currentSort?.direction === "desc") && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
70
71
  "data-testid": "column-sort-ascending",
71
- onClick: () => model.setSort(state.fieldId, "asc")
72
+ onClick: () => model.sorting?.setSort(state.fieldId, "asc")
72
73
  }, t("column action sort ascending")), isCurrentColumnSorted && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
73
74
  "data-testid": "column-clear-sort",
74
- onClick: () => model.clearSort()
75
+ onClick: () => model.sorting?.clearSort()
75
76
  }, t("column action clear sorting")), model.getColumnCount() > 1 && /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
76
77
  "data-testid": "column-delete",
77
78
  onClick: () => model.deleteColumn(state.fieldId)
@@ -286,14 +287,13 @@ var RowActionsMenu = ({ model, modals }) => {
286
287
  };
287
288
 
288
289
  // packages/ui/react-ui-table/src/model/table-model.ts
289
- import { computed as computed2, effect as effect2, signal as signal2 } from "@preact/signals-core";
290
- import orderBy from "lodash.orderby";
290
+ import { computed as computed3, effect as effect2, signal as signal3 } from "@preact/signals-core";
291
291
  import { Resource as Resource2 } from "@dxos/context";
292
- import { getValue, setValue, FormatEnum as FormatEnum2 } from "@dxos/echo-schema";
292
+ import { FormatEnum as FormatEnum3, getValue as getValue2, setValue } from "@dxos/echo-schema";
293
293
  import { invariant } from "@dxos/invariant";
294
294
  import { isReactiveObject, makeRef as makeRef2 } from "@dxos/live-object";
295
295
  import { PublicKey as PublicKey2 } from "@dxos/react-client";
296
- import { formatForDisplay, formatForEditing, parseValue } from "@dxos/react-ui-form";
296
+ import { formatForEditing, parseValue } from "@dxos/react-ui-form";
297
297
 
298
298
  // packages/ui/react-ui-table/src/model/selection-model.ts
299
299
  import { computed, effect, signal } from "@preact/signals-core";
@@ -304,73 +304,81 @@ var touch = (value) => value;
304
304
 
305
305
  // packages/ui/react-ui-table/src/util/table.ts
306
306
  import { AST, Format, FormatEnum, S as S2, TypedObject, TypeEnum } from "@dxos/echo-schema";
307
- import { log } from "@dxos/log";
308
307
  import { PublicKey } from "@dxos/react-client";
309
308
  import { create as create2, makeRef } from "@dxos/react-client/echo";
310
- import { createFieldId, createView, ViewProjection } from "@dxos/schema";
311
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/util/table.ts";
312
- var initializeTable = async ({ space, table, initialRow = true }) => {
313
- log("initializeTable", {
314
- table
315
- }, {
316
- F: __dxlog_file,
317
- L: 36,
318
- S: void 0,
319
- C: (f, a) => f(...a)
320
- });
321
- const ContactSchema = TypedObject({
322
- typename: `example.com/type/${PublicKey.random().truncate()}`,
323
- version: "0.1.0"
324
- })({
325
- name: S2.optional(S2.String).annotations({
326
- [AST.TitleAnnotationId]: "Name"
327
- }),
328
- active: S2.optional(S2.Boolean),
329
- email: S2.optional(Format.Email),
330
- salary: S2.optional(Format.Currency()).annotations({
331
- [AST.TitleAnnotationId]: "Salary"
332
- })
333
- });
334
- const [contactSchema] = await space.db.schemaRegistry.register([
335
- ContactSchema
336
- ]);
337
- table.view = makeRef(createView({
338
- name: "Test",
339
- typename: contactSchema.typename,
340
- jsonSchema: contactSchema.jsonSchema,
341
- fields: [
342
- "name",
343
- "active",
344
- "email",
345
- "salary"
346
- ]
347
- }));
348
- const projection = new ViewProjection(contactSchema, table.view.target);
349
- projection.setFieldProjection({
350
- field: {
351
- id: table.view.target.fields.find((f) => f.path === "salary").id,
352
- path: "salary",
353
- size: 150
309
+ import { createFieldId, createView, getSchemaProperties, ViewProjection } from "@dxos/schema";
310
+ var initializeTable = async ({ space, table, initialRow = true, initialSchema }) => {
311
+ if (initialSchema) {
312
+ const schema = await space.db.schemaRegistry.query({
313
+ typename: initialSchema
314
+ }).firstOrUndefined();
315
+ if (!schema) {
316
+ throw new Error(`Schema not found: ${initialSchema}`);
354
317
  }
355
- });
356
- projection.setFieldProjection({
357
- field: {
358
- id: createFieldId(),
359
- path: "manager",
360
- referencePath: "name"
361
- },
362
- props: {
363
- property: "manager",
364
- type: TypeEnum.Ref,
365
- format: FormatEnum.Ref,
366
- referenceSchema: contactSchema.typename,
367
- title: "Manager"
318
+ const fields = getSchemaProperties(schema.ast).filter((prop) => prop.type !== "object" || prop.format === FormatEnum.Ref).map((prop) => prop.name);
319
+ table.view = makeRef(createView({
320
+ // TODO(ZaymonFC): Don't hardcode name?
321
+ name: "View",
322
+ typename: schema.typename,
323
+ jsonSchema: schema.jsonSchema,
324
+ fields
325
+ }));
326
+ return schema;
327
+ } else {
328
+ const ContactSchema = TypedObject({
329
+ typename: `example.com/type/${PublicKey.random().truncate()}`,
330
+ version: "0.1.0"
331
+ })({
332
+ name: S2.optional(S2.String).annotations({
333
+ [AST.TitleAnnotationId]: "Name"
334
+ }),
335
+ active: S2.optional(S2.Boolean),
336
+ email: S2.optional(Format.Email),
337
+ salary: S2.optional(Format.Currency()).annotations({
338
+ [AST.TitleAnnotationId]: "Salary"
339
+ })
340
+ });
341
+ const [contactSchema] = await space.db.schemaRegistry.register([
342
+ ContactSchema
343
+ ]);
344
+ table.view = makeRef(createView({
345
+ name: "View",
346
+ typename: contactSchema.typename,
347
+ jsonSchema: contactSchema.jsonSchema,
348
+ fields: [
349
+ "name",
350
+ "active",
351
+ "email",
352
+ "salary"
353
+ ]
354
+ }));
355
+ const projection = new ViewProjection(contactSchema, table.view.target);
356
+ projection.setFieldProjection({
357
+ field: {
358
+ id: table.view.target.fields.find((f) => f.path === "salary").id,
359
+ path: "salary",
360
+ size: 150
361
+ }
362
+ });
363
+ projection.setFieldProjection({
364
+ field: {
365
+ id: createFieldId(),
366
+ path: "manager",
367
+ referencePath: "name"
368
+ },
369
+ props: {
370
+ property: "manager",
371
+ type: TypeEnum.Ref,
372
+ format: FormatEnum.Ref,
373
+ referenceSchema: contactSchema.typename,
374
+ title: "Manager"
375
+ }
376
+ });
377
+ if (initialRow) {
378
+ space.db.add(create2(contactSchema, {}));
368
379
  }
369
- });
370
- if (initialRow) {
371
- space.db.add(create2(contactSchema, {}));
380
+ return contactSchema;
372
381
  }
373
- return contactSchema;
374
382
  };
375
383
 
376
384
  // packages/ui/react-ui-table/src/util/table-buttons.ts
@@ -378,11 +386,13 @@ var BUTTON_IDENTIFIERS = {
378
386
  columnSettings: "data-table-column-settings-button",
379
387
  newColumn: "data-table-new-column-button",
380
388
  referencedCell: "data-table-ref-cell-button",
381
- rowMenu: "data-table-row-menu-button"
389
+ rowMenu: "data-table-row-menu-button",
390
+ sort: "data-table-sort-button"
382
391
  };
383
- var createButton = ({ attr, data, disabled = false, icon, testId }) => {
392
+ var createButton = ({ attr, data, disabled = false, icon, testId, type = "primary" }) => {
384
393
  const dataAttrs = Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
385
- 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>`;
394
+ const positionClass = type === "primary" ? "inline-end-2" : "inline-end-9";
395
+ return `<button ${attr} data-testid="${testId}" class="ch-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>`;
386
396
  };
387
397
  var addColumnButton = {
388
398
  attr: BUTTON_IDENTIFIERS.newColumn,
@@ -456,11 +466,35 @@ var rowMenuButton = {
456
466
  rowIndex: Number(el.getAttribute("data-row-index"))
457
467
  })
458
468
  };
469
+ var sortButton = {
470
+ attr: BUTTON_IDENTIFIERS.sort,
471
+ icon: "ph--sort-ascending--regular",
472
+ render: ({ fieldId, direction }) => {
473
+ return createButton({
474
+ attr: BUTTON_IDENTIFIERS.sort,
475
+ icon: direction === "desc" ? "ph--sort-descending--regular" : "ph--sort-ascending--regular",
476
+ data: {
477
+ "data-field-id": fieldId,
478
+ "data-direction": direction ?? ""
479
+ },
480
+ testId: "table-sort-button",
481
+ // TODO(ZaymonFC): All buttons should be rendered into an absolutely positioned flex container.
482
+ // This is a bit hacky.
483
+ type: "secondary"
484
+ });
485
+ },
486
+ getData: (el) => ({
487
+ type: "sort",
488
+ fieldId: el.getAttribute("data-field-id"),
489
+ direction: el.getAttribute("data-direction")
490
+ })
491
+ };
459
492
  var tableButtons = {
460
493
  addColumn: addColumnButton,
461
494
  columnSettings: columnSettingsButton,
462
495
  referencedCell: referencedCellButton,
463
- rowMenu: rowMenuButton
496
+ rowMenu: rowMenuButton,
497
+ sort: sortButton
464
498
  };
465
499
 
466
500
  // packages/ui/react-ui-table/src/util/table-controls.ts
@@ -597,8 +631,165 @@ var SelectionModel = class extends Resource {
597
631
  }
598
632
  };
599
633
 
634
+ // packages/ui/react-ui-table/src/model/table-sorting.ts
635
+ import { computed as computed2, signal as signal2 } from "@preact/signals-core";
636
+ import orderBy from "lodash.orderby";
637
+ import { getValue, FormatEnum as FormatEnum2, TypeEnum as TypeEnum2 } from "@dxos/echo-schema";
638
+ import { formatForDisplay } from "@dxos/react-ui-form";
639
+ var TableSorting = class {
640
+ constructor(rows, _view, _projection) {
641
+ this._view = _view;
642
+ this._projection = _projection;
643
+ this._displayToDataIndex = /* @__PURE__ */ new Map();
644
+ this._localSort = signal2(void 0);
645
+ this._rows = rows;
646
+ this._isDirty = computed2(() => {
647
+ const local = this._localSort.value;
648
+ const viewSort = this._view?.query.sort?.[0];
649
+ if (local?.type === "cleared") {
650
+ return viewSort !== void 0;
651
+ }
652
+ if (local?.type === "active") {
653
+ if (!viewSort) {
654
+ return true;
655
+ }
656
+ return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
657
+ }
658
+ return false;
659
+ });
660
+ this.sortedRows = this.initialiseSortedRows();
661
+ }
662
+ /**
663
+ * @reactive
664
+ * @returns Local sort if present, falls back to view sort.
665
+ */
666
+ get sorting() {
667
+ const local = this._localSort.value;
668
+ if (local?.type === "cleared") {
669
+ return void 0;
670
+ }
671
+ if (local?.type === "active") {
672
+ return local.sort;
673
+ }
674
+ return this._view?.query.sort?.[0];
675
+ }
676
+ /**
677
+ * @reactive
678
+ * @returns Whether local sort differs from view sort.
679
+ */
680
+ get isDirty() {
681
+ return this._isDirty.value;
682
+ }
683
+ getDataIndex(displayIndex) {
684
+ return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
685
+ }
686
+ setSort(fieldId, direction) {
687
+ this._localSort.value = {
688
+ type: "active",
689
+ sort: {
690
+ fieldId,
691
+ direction
692
+ }
693
+ };
694
+ }
695
+ toggleSort(fieldId) {
696
+ if (!this._view || !this.sorting || this.sorting.fieldId !== fieldId) {
697
+ return;
698
+ }
699
+ switch (this.sorting.direction) {
700
+ case "asc": {
701
+ this.setSort(fieldId, "desc");
702
+ break;
703
+ }
704
+ case "desc": {
705
+ this.setSort(fieldId, "asc");
706
+ break;
707
+ }
708
+ }
709
+ }
710
+ clearSort() {
711
+ this._localSort.value = {
712
+ type: "cleared"
713
+ };
714
+ }
715
+ save() {
716
+ if (this._view && this._localSort.value !== void 0) {
717
+ if (this._localSort.value.type === "active") {
718
+ this._view.query.sort = [
719
+ this._localSort.value.sort
720
+ ];
721
+ } else {
722
+ this._view.query.sort = [];
723
+ }
724
+ this._localSort.value = void 0;
725
+ }
726
+ }
727
+ //
728
+ // Initialisation.
729
+ //
730
+ initialiseSortedRows() {
731
+ return computed2(() => {
732
+ this._displayToDataIndex.clear();
733
+ const sort = this.sorting;
734
+ if (!sort || !this._view) {
735
+ return this._rows.value;
736
+ }
737
+ const field = this._view.fields.find((f) => f.id === sort.fieldId);
738
+ if (!field) {
739
+ return this._rows.value;
740
+ }
741
+ const { props } = this._projection.getFieldProjection(field.id);
742
+ const dataWithIndices = this._rows.value.map((item, index) => {
743
+ const sortValue = this.getSortValue(props, field, item);
744
+ const isEmpty = !(props.type === TypeEnum2.Boolean) && (sortValue == null || sortValue === "");
745
+ return {
746
+ item,
747
+ index,
748
+ value: sortValue,
749
+ isEmpty
750
+ };
751
+ });
752
+ const ordered = orderBy(dataWithIndices, [
753
+ "isEmpty",
754
+ "value"
755
+ ], [
756
+ "asc",
757
+ sort.direction
758
+ ]);
759
+ ordered.forEach(({ index: dataIndex }, displayIndex) => {
760
+ if (displayIndex !== dataIndex) {
761
+ this._displayToDataIndex.set(displayIndex, dataIndex);
762
+ }
763
+ });
764
+ return ordered.map(({ item }) => item);
765
+ });
766
+ }
767
+ getSortValue(props, field, item) {
768
+ const { type, format } = props;
769
+ const value = getValue(item, field.path);
770
+ if (type === TypeEnum2.Boolean) {
771
+ return !!value;
772
+ }
773
+ if (type === TypeEnum2.Number) {
774
+ return value;
775
+ }
776
+ if (value == null) {
777
+ return value;
778
+ }
779
+ if (format === FormatEnum2.Ref && field.referencePath) {
780
+ const refValue = getValue(value.target, field.referencePath);
781
+ return refValue;
782
+ }
783
+ return formatForDisplay({
784
+ type,
785
+ format,
786
+ value
787
+ });
788
+ }
789
+ };
790
+
600
791
  // packages/ui/react-ui-table/src/model/table-model.ts
601
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
792
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
602
793
  var TableModel = class extends Resource2 {
603
794
  constructor({ table, projection, sorting = [], pinnedRows = {
604
795
  top: [],
@@ -606,7 +797,7 @@ var TableModel = class extends Resource2 {
606
797
  }, onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChanged }) {
607
798
  super();
608
799
  this.id = `table-model-${PublicKey2.random().truncate()}`;
609
- this._visibleRange = signal2({
800
+ this._visibleRange = signal3({
610
801
  start: {
611
802
  row: 0,
612
803
  col: 0
@@ -616,14 +807,7 @@ var TableModel = class extends Resource2 {
616
807
  col: 0
617
808
  }
618
809
  });
619
- /**
620
- * Maps display indices to data indices.
621
- * Used for translating between sorted/displayed order and original data order.
622
- * Keys are display indices, values are corresponding data indices.
623
- */
624
- this._displayToDataIndex = /* @__PURE__ */ new Map();
625
- this._sorting = signal2(void 0);
626
- this._rows = signal2([]);
810
+ this._rows = signal3([]);
627
811
  //
628
812
  // Data
629
813
  //
@@ -633,11 +817,11 @@ var TableModel = class extends Resource2 {
633
817
  this.getRowCount = () => this._rows.value.length;
634
818
  this.getColumnCount = () => this.table.view?.target?.fields.length ?? 0;
635
819
  this.insertRow = (rowIndex) => {
636
- const row = rowIndex !== void 0 ? this._displayToDataIndex.get(rowIndex) ?? rowIndex : this._rows.value.length;
820
+ const row = rowIndex !== void 0 ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
637
821
  this._onInsertRow?.(row);
638
822
  };
639
823
  this.deleteRow = (rowIndex) => {
640
- const row = this._displayToDataIndex.get(rowIndex) ?? rowIndex;
824
+ const row = this._sorting.getDataIndex(rowIndex);
641
825
  const obj = this._rows.value[row];
642
826
  const objectsToDelete = [];
643
827
  if (this._selection.hasSelection.value) {
@@ -654,18 +838,18 @@ var TableModel = class extends Resource2 {
654
838
  return void 0;
655
839
  }
656
840
  const field = fields[col];
657
- const dataIndex = this._displayToDataIndex.get(row) ?? row;
658
- const value = getValue(this._rows.value[dataIndex], field.path);
841
+ const dataIndex = this._sorting.getDataIndex(row);
842
+ const value = getValue2(this._rows.value[dataIndex], field.path);
659
843
  if (value == null) {
660
844
  return "";
661
845
  }
662
846
  const { props } = this._projection.getFieldProjection(field.id);
663
847
  switch (props.format) {
664
- case FormatEnum2.Ref: {
848
+ case FormatEnum3.Ref: {
665
849
  if (!field.referencePath) {
666
850
  return "";
667
851
  }
668
- return getValue(value.target, field.referencePath);
852
+ return getValue2(value.target, field.referencePath);
669
853
  }
670
854
  default: {
671
855
  return formatForEditing({
@@ -677,7 +861,7 @@ var TableModel = class extends Resource2 {
677
861
  }
678
862
  };
679
863
  this.setCellData = ({ col, row }, value) => {
680
- const rowIdx = this._displayToDataIndex.get(row) ?? row;
864
+ const rowIdx = this._sorting.getDataIndex(row);
681
865
  const fields = this.table.view?.target?.fields ?? [];
682
866
  if (col < 0 || col >= fields.length) {
683
867
  return;
@@ -685,7 +869,7 @@ var TableModel = class extends Resource2 {
685
869
  const field = fields[col];
686
870
  const { props } = this._projection.getFieldProjection(field.id);
687
871
  switch (props.format) {
688
- case FormatEnum2.Ref: {
872
+ case FormatEnum3.Ref: {
689
873
  if (isReactiveObject(value)) {
690
874
  setValue(this._rows.value[rowIdx], field.path, makeRef2(value));
691
875
  }
@@ -703,7 +887,11 @@ var TableModel = class extends Resource2 {
703
887
  };
704
888
  this._table = table;
705
889
  this._projection = projection;
706
- this._sorting.value = sorting.at(0);
890
+ this._sorting = new TableSorting(this._rows, table.view?.target, projection);
891
+ if (sorting.length > 0) {
892
+ const [sort] = sorting;
893
+ this._sorting.setSort(sort.fieldId, sort.direction);
894
+ }
707
895
  this._pinnedRows = pinnedRows;
708
896
  this._onInsertRow = onInsertRow;
709
897
  this._onDeleteRows = onDeleteRows;
@@ -718,14 +906,20 @@ var TableModel = class extends Resource2 {
718
906
  return this._projection;
719
907
  }
720
908
  get rows() {
721
- return this._sortedRows;
909
+ return this._sorting.sortedRows;
910
+ }
911
+ /**
912
+ * @reactive
913
+ */
914
+ get sorting() {
915
+ return this._sorting;
722
916
  }
723
917
  get pinnedRows() {
724
918
  return this._pinnedRows;
725
919
  }
726
920
  get columnMeta() {
727
921
  invariant(this._columnMeta, void 0, {
728
- F: __dxlog_file2,
922
+ F: __dxlog_file,
729
923
  L: 117,
730
924
  S: this,
731
925
  A: [
@@ -735,24 +929,24 @@ var TableModel = class extends Resource2 {
735
929
  });
736
930
  return this._columnMeta;
737
931
  }
738
- get sorting() {
739
- return this._sorting.value;
740
- }
741
932
  get selection() {
742
933
  return this._selection;
743
934
  }
935
+ /** @reactive */
936
+ get isViewDirty() {
937
+ return this._sorting.isDirty;
938
+ }
744
939
  //
745
940
  // Initialisation
746
941
  //
747
942
  async _open() {
748
943
  this.initializeColumnMeta();
749
- this.initializeSorting();
750
944
  this.initializeEffects();
751
- this._selection = new SelectionModel(this._sortedRows, () => this._onRowOrderChanged?.());
945
+ this._selection = new SelectionModel(this._sorting.sortedRows, () => this._onRowOrderChanged?.());
752
946
  await this._selection.open(this._ctx);
753
947
  }
754
948
  initializeColumnMeta() {
755
- this._columnMeta = computed2(() => {
949
+ this._columnMeta = computed3(() => {
756
950
  const fields = this._table.view?.target?.fields ?? [];
757
951
  const meta = Object.fromEntries(fields.map((field, index) => [
758
952
  index,
@@ -778,60 +972,9 @@ var TableModel = class extends Resource2 {
778
972
  };
779
973
  });
780
974
  }
781
- initializeSorting() {
782
- this._sortedRows = computed2(() => {
783
- this._displayToDataIndex.clear();
784
- const sort = this._sorting.value;
785
- if (!sort) {
786
- return this._rows.value;
787
- }
788
- const field = this._table.view?.target?.fields.find((field2) => field2.id === sort.fieldId);
789
- if (!field) {
790
- return this._rows.value;
791
- }
792
- const { props } = this._projection.getFieldProjection(field.id);
793
- const dataWithIndices = this._rows.value.map((item, index) => {
794
- const rawValue = getValue(item, field.path);
795
- let sortValue = rawValue;
796
- if (props.type.startsWith("boolean")) {
797
- sortValue = !!rawValue;
798
- } else if (rawValue != null) {
799
- if (props.format === FormatEnum2.Ref && field.referencePath) {
800
- sortValue = getValue(rawValue, field.referencePath);
801
- } else {
802
- sortValue = formatForDisplay({
803
- type: props.type,
804
- format: props.format,
805
- value: rawValue
806
- });
807
- }
808
- }
809
- return {
810
- item,
811
- index,
812
- value: sortValue,
813
- isEmpty: !props.type.startsWith("boolean") && (sortValue == null || sortValue === "")
814
- };
815
- });
816
- const sorted = orderBy(dataWithIndices, [
817
- "isEmpty",
818
- "value"
819
- ], [
820
- "asc",
821
- sort.direction
822
- ]);
823
- for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
824
- const { index: dataIndex } = sorted[displayIndex];
825
- if (displayIndex !== dataIndex) {
826
- this._displayToDataIndex.set(displayIndex, dataIndex);
827
- }
828
- }
829
- return sorted.map(({ item }) => item);
830
- });
831
- }
832
975
  initializeEffects() {
833
976
  const rowOrderWatcher = effect2(() => {
834
- touch(this._sortedRows.value);
977
+ touch(this._sorting.sortedRows.value);
835
978
  this._onRowOrderChanged?.();
836
979
  });
837
980
  this._ctx.onDispose(rowOrderWatcher);
@@ -840,8 +983,8 @@ var TableModel = class extends Resource2 {
840
983
  const rowEffects = [];
841
984
  for (let row = start.row; row <= end.row; row++) {
842
985
  rowEffects.push(effect2(() => {
843
- const obj = this._sortedRows.value[row];
844
- this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
986
+ const obj = this._sorting.sortedRows.value[row];
987
+ this?._table?.view?.target?.fields.forEach((field) => touch(getValue2(obj, field.path)));
845
988
  this._onCellUpdate?.({
846
989
  row,
847
990
  col: start.col,
@@ -859,10 +1002,10 @@ var TableModel = class extends Resource2 {
859
1002
  * @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
860
1003
  */
861
1004
  updateCellData({ col, row }, update) {
862
- const dataRow = this._displayToDataIndex.get(row) ?? row;
1005
+ const dataRow = this._sorting.getDataIndex(row);
863
1006
  const fields = this.table.view?.target?.fields ?? [];
864
1007
  const field = fields[col];
865
- const value = getValue(this._rows.value[dataRow], field.path);
1008
+ const value = getValue2(this._rows.value[dataRow], field.path);
866
1009
  const updatedValue = update(value);
867
1010
  setValue(this._rows.value[dataRow], field.path, updatedValue);
868
1011
  }
@@ -875,9 +1018,6 @@ var TableModel = class extends Resource2 {
875
1018
  }
876
1019
  const field = this.table.view.target?.fields.find((field2) => field2.id === fieldId);
877
1020
  if (field && this._onDeleteColumn) {
878
- if (this._sorting.value?.fieldId === fieldId) {
879
- this.clearSort();
880
- }
881
1021
  this._onDeleteColumn(field.id);
882
1022
  }
883
1023
  }
@@ -895,18 +1035,6 @@ var TableModel = class extends Resource2 {
895
1035
  }
896
1036
  }
897
1037
  //
898
- // Sorting
899
- //
900
- setSort(fieldId, direction) {
901
- this._sorting.value = {
902
- fieldId,
903
- direction
904
- };
905
- }
906
- clearSort() {
907
- this._sorting.value = void 0;
908
- }
909
- //
910
1038
  // Pinning
911
1039
  //
912
1040
  // TODO(burdon): Change to setPinned(on/off).
@@ -917,15 +1045,21 @@ var TableModel = class extends Resource2 {
917
1045
  this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
918
1046
  this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
919
1047
  }
1048
+ //
1049
+ // View operations
1050
+ //
1051
+ saveView() {
1052
+ this._sorting.save();
1053
+ }
920
1054
  };
921
1055
 
922
1056
  // packages/ui/react-ui-table/src/model/modal-controller.ts
923
- import { signal as signal3 } from "@preact/signals-core";
924
- import { log as log2 } from "@dxos/log";
925
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
1057
+ import { signal as signal4 } from "@preact/signals-core";
1058
+ import { log } from "@dxos/log";
1059
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
926
1060
  var ModalController = class {
927
1061
  constructor() {
928
- this._state = signal3({
1062
+ this._state = signal4({
929
1063
  type: "closed"
930
1064
  });
931
1065
  this._triggerRef = {
@@ -985,8 +1119,8 @@ var ModalController = class {
985
1119
  onCreate
986
1120
  };
987
1121
  } else {
988
- log2.warn("anchor cell not found while creating new ref", void 0, {
989
- F: __dxlog_file3,
1122
+ log.warn("anchor cell not found while creating new ref", void 0, {
1123
+ F: __dxlog_file2,
990
1124
  L: 86,
991
1125
  S: this,
992
1126
  C: (f, a) => f(...a)
@@ -1008,14 +1142,14 @@ var ModalController = class {
1008
1142
  };
1009
1143
 
1010
1144
  // packages/ui/react-ui-table/src/model/table-presentation.ts
1011
- import { signal as signal4 } from "@preact/signals-core";
1012
- import { FormatEnum as FormatEnum3, getValue as getValue2 } from "@dxos/echo-schema";
1145
+ import { signal as signal5 } from "@preact/signals-core";
1146
+ import { FormatEnum as FormatEnum4, getValue as getValue3 } from "@dxos/echo-schema";
1013
1147
  import { cellClassesForFieldType, cellClassesForRowSelection, formatForDisplay as formatForDisplay2 } from "@dxos/react-ui-form";
1014
1148
  import { toPlaneCellIndex } from "@dxos/react-ui-grid";
1015
1149
  import { mx } from "@dxos/react-ui-theme";
1016
1150
  import { VIEW_FIELD_LIMIT } from "@dxos/schema";
1017
1151
  var TablePresentation = class {
1018
- constructor(model, _visibleRange = signal4({
1152
+ constructor(model, _visibleRange = signal5({
1019
1153
  start: {
1020
1154
  row: 0,
1021
1155
  col: 0
@@ -1054,19 +1188,19 @@ var TablePresentation = class {
1054
1188
  const { props } = this.model.projection.getFieldProjection(field.id);
1055
1189
  const cell = {
1056
1190
  get value() {
1057
- const value = getValue2(obj, field.path);
1191
+ const value = getValue3(obj, field.path);
1058
1192
  if (value == null) {
1059
1193
  return "";
1060
1194
  }
1061
1195
  switch (props.format) {
1062
- case FormatEnum3.Boolean: {
1196
+ case FormatEnum4.Boolean: {
1063
1197
  return "";
1064
1198
  }
1065
- case FormatEnum3.Ref: {
1199
+ case FormatEnum4.Ref: {
1066
1200
  if (!field.referencePath) {
1067
1201
  return "";
1068
1202
  }
1069
- return getValue2(value.target, field.referencePath);
1203
+ return getValue3(value.target, field.referencePath);
1070
1204
  }
1071
1205
  default: {
1072
1206
  return formatForDisplay2({
@@ -1093,8 +1227,8 @@ var TablePresentation = class {
1093
1227
  if (classes.length > 0) {
1094
1228
  cell.className = mx(classes.flat());
1095
1229
  }
1096
- if (props.format === FormatEnum3.Ref && props.referenceSchema) {
1097
- const targetObj = getValue2(obj, field.path)?.target;
1230
+ if (props.format === FormatEnum4.Ref && props.referenceSchema) {
1231
+ const targetObj = getValue3(obj, field.path)?.target;
1098
1232
  if (targetObj) {
1099
1233
  cell.accessoryHtml = tableButtons.referencedCell.render({
1100
1234
  targetId: targetObj.id,
@@ -1102,8 +1236,8 @@ var TablePresentation = class {
1102
1236
  });
1103
1237
  }
1104
1238
  }
1105
- if (props.format === FormatEnum3.Boolean) {
1106
- const value = getValue2(obj, field.path);
1239
+ if (props.format === FormatEnum4.Boolean) {
1240
+ const value = getValue3(obj, field.path);
1107
1241
  cell.accessoryHtml = tableControls.switch.render({
1108
1242
  colIndex,
1109
1243
  rowIndex: displayIndex,
@@ -1133,6 +1267,8 @@ var TablePresentation = class {
1133
1267
  const fields = this.model.table.view?.target?.fields ?? [];
1134
1268
  for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1135
1269
  const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
1270
+ const sorting = this.model.sorting?.sorting;
1271
+ const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
1136
1272
  cells[toPlaneCellIndex({
1137
1273
  col,
1138
1274
  row: 0
@@ -1141,9 +1277,15 @@ var TablePresentation = class {
1141
1277
  value: props.title ?? field.path,
1142
1278
  readonly: true,
1143
1279
  resizeHandle: "col",
1144
- accessoryHtml: tableButtons.columnSettings.render({
1280
+ accessoryHtml: `
1281
+ ${direction !== void 0 ? tableButtons.sort.render({
1282
+ fieldId: field.id,
1283
+ direction
1284
+ }) : ""}
1285
+ ${tableButtons.columnSettings.render({
1145
1286
  fieldId: field.id
1146
- }),
1287
+ })}
1288
+ `,
1147
1289
  className: "!bg-gridHeader"
1148
1290
  };
1149
1291
  }
@@ -1224,7 +1366,7 @@ var TablePresentation = class {
1224
1366
 
1225
1367
  // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
1226
1368
  import React6, { useCallback as useCallback3, useMemo as useMemo3, useRef } from "react";
1227
- import { FormatEnum as FormatEnum4 } from "@dxos/echo-schema";
1369
+ import { FormatEnum as FormatEnum5 } from "@dxos/echo-schema";
1228
1370
  import { invariant as invariant2 } from "@dxos/invariant";
1229
1371
  import { cellQuery, editorKeys, parseCellIndex, useGridContext as useGridContext2, GridCellEditor } from "@dxos/react-ui-grid";
1230
1372
 
@@ -1268,7 +1410,7 @@ var completion = ({ debug, onQuery, onMatch }) => {
1268
1410
  };
1269
1411
 
1270
1412
  // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
1271
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
1413
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
1272
1414
  var newValue = Symbol.for("newValue");
1273
1415
  var createOption = (text) => ({
1274
1416
  [newValue]: true,
@@ -1286,7 +1428,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
1286
1428
  const field = model.projection.view.fields[col];
1287
1429
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
1288
1430
  invariant2(fieldProjection2, void 0, {
1289
- F: __dxlog_file4,
1431
+ F: __dxlog_file3,
1290
1432
  L: 67,
1291
1433
  S: void 0,
1292
1434
  A: [
@@ -1360,7 +1502,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
1360
1502
  ];
1361
1503
  if (onQuery) {
1362
1504
  switch (fieldProjection.props.format) {
1363
- case FormatEnum4.Ref: {
1505
+ case FormatEnum5.Ref: {
1364
1506
  extension2.push([
1365
1507
  completion({
1366
1508
  onQuery: (text) => onQuery(fieldProjection, text),
@@ -1436,7 +1578,7 @@ var determineNavigationDelta = ({ key, shift }) => {
1436
1578
  };
1437
1579
 
1438
1580
  // packages/ui/react-ui-table/src/components/Table/Table.tsx
1439
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
1581
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
1440
1582
  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";
1441
1583
  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";
1442
1584
  var frozen = {
@@ -1504,6 +1646,10 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1504
1646
  modals.showReferencePanel(data.targetId, data.schemaId);
1505
1647
  break;
1506
1648
  }
1649
+ case "sort": {
1650
+ model?.sorting?.toggleSort(data.fieldId);
1651
+ break;
1652
+ }
1507
1653
  }
1508
1654
  return;
1509
1655
  }
@@ -1604,8 +1750,8 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1604
1750
  if (model && props.referenceSchema && field.referencePath) {
1605
1751
  const space = getSpace4(model.table);
1606
1752
  invariant3(space, void 0, {
1607
- F: __dxlog_file5,
1608
- L: 260,
1753
+ F: __dxlog_file4,
1754
+ L: 264,
1609
1755
  S: void 0,
1610
1756
  A: [
1611
1757
  "space",
@@ -1616,7 +1762,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1616
1762
  if (schema) {
1617
1763
  const { objects } = await space.db.query(Filter.schema(schema)).run();
1618
1764
  const options = objects.map((obj) => {
1619
- const value = getValue3(obj, field.referencePath);
1765
+ const value = getValue4(obj, field.referencePath);
1620
1766
  if (!value || typeof value !== "string") {
1621
1767
  return void 0;
1622
1768
  }
@@ -1689,79 +1835,76 @@ var Table = {
1689
1835
  Main: TableMain
1690
1836
  };
1691
1837
 
1692
- // packages/ui/react-ui-table/src/components/Toolbar/Toolbar.tsx
1693
- import { createContext } from "@radix-ui/react-context";
1694
- import React9 from "react";
1695
- import { Toolbar as NaturalToolbar2, useTranslation as useTranslation4 } from "@dxos/react-ui";
1696
-
1697
- // packages/ui/react-ui-table/src/components/Toolbar/common.tsx
1698
- import React8 from "react";
1699
- import { Icon, Toolbar as NaturalToolbar, Tooltip } from "@dxos/react-ui";
1700
- var buttonStyles = "min-bs-0 p-2";
1701
- var tooltipProps = {
1702
- side: "top",
1703
- classNames: "z-10"
1704
- };
1705
- var ToolbarSeparator = () => /* @__PURE__ */ React8.createElement("div", {
1706
- role: "separator",
1707
- className: "grow"
1708
- });
1709
- var ToolbarButton = ({ icon, children, ...props }) => {
1710
- return /* @__PURE__ */ React8.createElement(Tooltip.Root, null, /* @__PURE__ */ React8.createElement(Tooltip.Trigger, {
1711
- asChild: true
1712
- }, /* @__PURE__ */ React8.createElement(NaturalToolbar.Button, {
1713
- variant: "ghost",
1714
- ...props,
1715
- classNames: buttonStyles
1716
- }, /* @__PURE__ */ React8.createElement(Icon, {
1717
- icon,
1718
- size: 5
1719
- }), /* @__PURE__ */ React8.createElement("span", {
1720
- className: "sr-only"
1721
- }, children))), /* @__PURE__ */ React8.createElement(Tooltip.Portal, null, /* @__PURE__ */ React8.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React8.createElement(Tooltip.Arrow, null))));
1722
- };
1723
-
1724
- // packages/ui/react-ui-table/src/components/Toolbar/Toolbar.tsx
1725
- var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
1726
- var ToolbarRoot = ({ classNames, children, onAction }) => {
1727
- return /* @__PURE__ */ React9.createElement(ToolbarContextProvider, {
1728
- onAction
1729
- }, /* @__PURE__ */ React9.createElement(NaturalToolbar2.Root, {
1730
- classNames: [
1731
- "is-full shrink-0 overflow-x-auto overflow-y-hidden p-1 attention-surface",
1732
- classNames
1733
- ]
1734
- }, children));
1735
- };
1736
- var Editing = () => {
1737
- const { onAction } = useToolbarContext("Editing");
1738
- const { t } = useTranslation4(translationKey);
1739
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(ToolbarButton, {
1740
- value: "add-row",
1838
+ // packages/ui/react-ui-table/src/components/TableToolbar/TableToolbar.tsx
1839
+ import React8, { useCallback as useCallback5, useMemo as useMemo5 } from "react";
1840
+ import { create as create3 } from "@dxos/live-object";
1841
+ import { createGapSeparator, createMenuAction, MenuProvider, ToolbarMenu, useMenuActions } from "@dxos/react-ui-menu";
1842
+ var createTableToolbarActions = (state) => {
1843
+ const add = createMenuAction("add-row", {
1844
+ type: "add-row",
1741
1845
  icon: "ph--plus--regular",
1742
- "data-testid": "table.toolbar.add-row",
1743
- onClick: () => onAction?.({
1744
- type: "add-row"
1745
- })
1746
- }, t("add row")));
1747
- };
1748
- var Actions = () => {
1749
- const { onAction } = useToolbarContext("Actions");
1750
- const { t } = useTranslation4(translationKey);
1751
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(ToolbarButton, {
1752
- value: "comment",
1846
+ label: [
1847
+ "add row",
1848
+ {
1849
+ ns: translationKey
1850
+ }
1851
+ ],
1852
+ testId: "table.toolbar.add-row"
1853
+ });
1854
+ const save = createMenuAction("save-view", {
1855
+ type: "save-view",
1856
+ icon: "ph--floppy-disk--regular",
1857
+ label: [
1858
+ "save view label",
1859
+ {
1860
+ ns: translationKey
1861
+ }
1862
+ ],
1863
+ testId: "table.toolbar.save-view",
1864
+ iconOnly: false,
1865
+ hidden: !state.viewDirty
1866
+ });
1867
+ const gap = createGapSeparator();
1868
+ const comment = createMenuAction("comment", {
1869
+ type: "comment",
1753
1870
  icon: "ph--chat-text--regular",
1754
- "data-testid": "table.toolbar.comment",
1755
- onClick: () => onAction?.({
1756
- type: "comment"
1757
- })
1758
- }, t("create comment")));
1871
+ label: [
1872
+ "create comment",
1873
+ {
1874
+ ns: translationKey
1875
+ }
1876
+ ],
1877
+ testId: "table.toolbar.comment"
1878
+ });
1879
+ const nodes = [
1880
+ add,
1881
+ save,
1882
+ ...gap.nodes,
1883
+ comment
1884
+ ];
1885
+ return {
1886
+ nodes,
1887
+ edges: nodes.map(({ id: target }) => ({
1888
+ source: "root",
1889
+ target
1890
+ }))
1891
+ };
1759
1892
  };
1760
- var Toolbar = {
1761
- Root: ToolbarRoot,
1762
- Editing,
1763
- Actions,
1764
- Separator: ToolbarSeparator
1893
+ var TableToolbar = ({ classNames, viewDirty, attendableId, onAction }) => {
1894
+ const state = useMemo5(() => create3({
1895
+ viewDirty
1896
+ }), []);
1897
+ const actionsCreator = useCallback5(() => createTableToolbarActions(state), [
1898
+ state
1899
+ ]);
1900
+ const menu = useMenuActions(actionsCreator);
1901
+ return /* @__PURE__ */ React8.createElement(MenuProvider, {
1902
+ ...menu,
1903
+ attendableId,
1904
+ onAction
1905
+ }, /* @__PURE__ */ React8.createElement(ToolbarMenu, {
1906
+ classNames
1907
+ }));
1765
1908
  };
1766
1909
 
1767
1910
  // packages/ui/react-ui-table/src/hooks/useTableModel.ts
@@ -1808,8 +1951,8 @@ export {
1808
1951
  TableModel,
1809
1952
  TablePresentation,
1810
1953
  TableSchema,
1954
+ TableToolbar,
1811
1955
  TableType,
1812
- Toolbar,
1813
1956
  initializeTable,
1814
1957
  renderCheckbox,
1815
1958
  renderSwitch,
@@ -1817,7 +1960,6 @@ export {
1817
1960
  tableControls,
1818
1961
  touch,
1819
1962
  translations_default as translations,
1820
- useTableModel,
1821
- useToolbarContext
1963
+ useTableModel
1822
1964
  };
1823
1965
  //# sourceMappingURL=index.mjs.map