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