@dxos/react-ui-table 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b

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 (70) 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 +446 -282
  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 +438 -268
  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 +446 -282
  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 +1 -4
  22. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  23. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +18 -0
  24. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -0
  25. package/dist/types/src/components/TableToolbar/index.d.ts +2 -0
  26. package/dist/types/src/components/TableToolbar/index.d.ts.map +1 -0
  27. package/dist/types/src/components/index.d.ts +1 -1
  28. package/dist/types/src/components/index.d.ts.map +1 -1
  29. package/dist/types/src/components/testing.d.ts +1 -1
  30. package/dist/types/src/model/table-model.d.ts +11 -19
  31. package/dist/types/src/model/table-model.d.ts.map +1 -1
  32. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  33. package/dist/types/src/model/table-sorting.d.ts +53 -0
  34. package/dist/types/src/model/table-sorting.d.ts.map +1 -0
  35. package/dist/types/src/translations.d.ts +2 -1
  36. package/dist/types/src/translations.d.ts.map +1 -1
  37. package/dist/types/src/types/table.d.ts +3 -3
  38. package/dist/types/src/types/table.d.ts.map +1 -1
  39. package/dist/types/src/util/table-buttons.d.ts +15 -1
  40. package/dist/types/src/util/table-buttons.d.ts.map +1 -1
  41. package/dist/types/src/util/table.d.ts +2 -1
  42. package/dist/types/src/util/table.d.ts.map +1 -1
  43. package/dist/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +35 -34
  45. package/src/_deprecated/schema/Schema.stories.tsx +1 -1
  46. package/src/components/Table/ColumnActionsMenu.tsx +4 -4
  47. package/src/components/Table/Table.stories.tsx +52 -40
  48. package/src/components/Table/Table.tsx +4 -0
  49. package/src/components/TableToolbar/TableToolbar.tsx +72 -0
  50. package/src/components/{Toolbar → TableToolbar}/index.ts +1 -1
  51. package/src/components/index.ts +1 -1
  52. package/src/model/table-model.test.ts +0 -5
  53. package/src/model/table-model.ts +40 -98
  54. package/src/model/table-presentation.ts +25 -4
  55. package/src/model/table-sorting.ts +215 -0
  56. package/src/playwright/smoke.spec.ts +18 -11
  57. package/src/translations.ts +2 -1
  58. package/src/types/table.ts +1 -1
  59. package/src/util/table-buttons.ts +33 -3
  60. package/src/util/table-controls.ts +2 -2
  61. package/src/util/table.ts +78 -52
  62. package/dist/lib/node/chunk-YSBXNBDJ.cjs.map +0 -7
  63. package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -21
  64. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
  65. package/dist/types/src/components/Toolbar/common.d.ts +0 -14
  66. package/dist/types/src/components/Toolbar/common.d.ts.map +0 -1
  67. package/dist/types/src/components/Toolbar/index.d.ts +0 -2
  68. package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
  69. package/src/components/Toolbar/Toolbar.tsx +0 -87
  70. 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="dx-button is-6 pli-0.5 min-bs-0 absolute inset-block-1 ${positionClass}" ${dataAttrs} ${disabled ? "disabled" : ""}><svg><use href="/icons.svg#${icon}"/></svg></button>`;
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
@@ -468,8 +502,8 @@ var CONTROL_IDENTIFIERS = {
468
502
  switch: "data-table-switch"
469
503
  };
470
504
  var BASE_CLASSES = {
471
- checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] ch-checkbox",
472
- switch: "absolute inset-block-[.25rem] inline-end-[.25rem] ch-checkbox--switch"
505
+ checkbox: "absolute inset-block-[.375rem] inline-end-[.375rem] dx-checkbox",
506
+ switch: "absolute inset-block-[.25rem] inline-end-[.25rem] dx-checkbox--switch"
473
507
  };
474
508
  var renderAttributes = (data) => {
475
509
  return Object.entries(data).map(([k, v]) => `${k}="${v}"`).join(" ");
@@ -596,8 +630,173 @@ 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.SingleSelect) {
779
+ const options = props.options;
780
+ if (!options) {
781
+ return void 0;
782
+ }
783
+ const option = options.find((option2) => option2.id === value);
784
+ return option ? options.indexOf(option) : void 0;
785
+ }
786
+ if (format === FormatEnum2.Ref && field.referencePath) {
787
+ const refValue = getValue(value.target, field.referencePath);
788
+ return refValue;
789
+ }
790
+ return formatForDisplay({
791
+ type,
792
+ format,
793
+ value
794
+ });
795
+ }
796
+ };
797
+
599
798
  // 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";
799
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/table-model.ts";
601
800
  var TableModel = class extends Resource2 {
602
801
  constructor({ table, projection, sorting = [], pinnedRows = {
603
802
  top: [],
@@ -605,7 +804,7 @@ var TableModel = class extends Resource2 {
605
804
  }, onCellUpdate, onDeleteColumn, onDeleteRows, onInsertRow, onRowOrderChanged }) {
606
805
  super();
607
806
  this.id = `table-model-${PublicKey2.random().truncate()}`;
608
- this._visibleRange = signal2({
807
+ this._visibleRange = signal3({
609
808
  start: {
610
809
  row: 0,
611
810
  col: 0
@@ -615,14 +814,7 @@ var TableModel = class extends Resource2 {
615
814
  col: 0
616
815
  }
617
816
  });
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([]);
817
+ this._rows = signal3([]);
626
818
  //
627
819
  // Data
628
820
  //
@@ -632,11 +824,11 @@ var TableModel = class extends Resource2 {
632
824
  this.getRowCount = () => this._rows.value.length;
633
825
  this.getColumnCount = () => this.table.view?.target?.fields.length ?? 0;
634
826
  this.insertRow = (rowIndex) => {
635
- const row = rowIndex !== void 0 ? this._displayToDataIndex.get(rowIndex) ?? rowIndex : this._rows.value.length;
827
+ const row = rowIndex !== void 0 ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
636
828
  this._onInsertRow?.(row);
637
829
  };
638
830
  this.deleteRow = (rowIndex) => {
639
- const row = this._displayToDataIndex.get(rowIndex) ?? rowIndex;
831
+ const row = this._sorting.getDataIndex(rowIndex);
640
832
  const obj = this._rows.value[row];
641
833
  const objectsToDelete = [];
642
834
  if (this._selection.hasSelection.value) {
@@ -653,18 +845,18 @@ var TableModel = class extends Resource2 {
653
845
  return void 0;
654
846
  }
655
847
  const field = fields[col];
656
- const dataIndex = this._displayToDataIndex.get(row) ?? row;
657
- const value = getValue(this._rows.value[dataIndex], field.path);
848
+ const dataIndex = this._sorting.getDataIndex(row);
849
+ const value = getValue2(this._rows.value[dataIndex], field.path);
658
850
  if (value == null) {
659
851
  return "";
660
852
  }
661
853
  const { props } = this._projection.getFieldProjection(field.id);
662
854
  switch (props.format) {
663
- case FormatEnum2.Ref: {
855
+ case FormatEnum3.Ref: {
664
856
  if (!field.referencePath) {
665
857
  return "";
666
858
  }
667
- return getValue(value.target, field.referencePath);
859
+ return getValue2(value.target, field.referencePath);
668
860
  }
669
861
  default: {
670
862
  return formatForEditing({
@@ -676,7 +868,7 @@ var TableModel = class extends Resource2 {
676
868
  }
677
869
  };
678
870
  this.setCellData = ({ col, row }, value) => {
679
- const rowIdx = this._displayToDataIndex.get(row) ?? row;
871
+ const rowIdx = this._sorting.getDataIndex(row);
680
872
  const fields = this.table.view?.target?.fields ?? [];
681
873
  if (col < 0 || col >= fields.length) {
682
874
  return;
@@ -684,7 +876,7 @@ var TableModel = class extends Resource2 {
684
876
  const field = fields[col];
685
877
  const { props } = this._projection.getFieldProjection(field.id);
686
878
  switch (props.format) {
687
- case FormatEnum2.Ref: {
879
+ case FormatEnum3.Ref: {
688
880
  if (isReactiveObject(value)) {
689
881
  setValue(this._rows.value[rowIdx], field.path, makeRef2(value));
690
882
  }
@@ -702,7 +894,11 @@ var TableModel = class extends Resource2 {
702
894
  };
703
895
  this._table = table;
704
896
  this._projection = projection;
705
- this._sorting.value = sorting.at(0);
897
+ this._sorting = new TableSorting(this._rows, table.view?.target, projection);
898
+ if (sorting.length > 0) {
899
+ const [sort] = sorting;
900
+ this._sorting.setSort(sort.fieldId, sort.direction);
901
+ }
706
902
  this._pinnedRows = pinnedRows;
707
903
  this._onInsertRow = onInsertRow;
708
904
  this._onDeleteRows = onDeleteRows;
@@ -717,14 +913,20 @@ var TableModel = class extends Resource2 {
717
913
  return this._projection;
718
914
  }
719
915
  get rows() {
720
- return this._sortedRows;
916
+ return this._sorting.sortedRows;
917
+ }
918
+ /**
919
+ * @reactive
920
+ */
921
+ get sorting() {
922
+ return this._sorting;
721
923
  }
722
924
  get pinnedRows() {
723
925
  return this._pinnedRows;
724
926
  }
725
927
  get columnMeta() {
726
928
  invariant(this._columnMeta, void 0, {
727
- F: __dxlog_file2,
929
+ F: __dxlog_file,
728
930
  L: 117,
729
931
  S: this,
730
932
  A: [
@@ -734,24 +936,24 @@ var TableModel = class extends Resource2 {
734
936
  });
735
937
  return this._columnMeta;
736
938
  }
737
- get sorting() {
738
- return this._sorting.value;
739
- }
740
939
  get selection() {
741
940
  return this._selection;
742
941
  }
942
+ /** @reactive */
943
+ get isViewDirty() {
944
+ return this._sorting.isDirty;
945
+ }
743
946
  //
744
947
  // Initialisation
745
948
  //
746
949
  async _open() {
747
950
  this.initializeColumnMeta();
748
- this.initializeSorting();
749
951
  this.initializeEffects();
750
- this._selection = new SelectionModel(this._sortedRows, () => this._onRowOrderChanged?.());
952
+ this._selection = new SelectionModel(this._sorting.sortedRows, () => this._onRowOrderChanged?.());
751
953
  await this._selection.open(this._ctx);
752
954
  }
753
955
  initializeColumnMeta() {
754
- this._columnMeta = computed2(() => {
956
+ this._columnMeta = computed3(() => {
755
957
  const fields = this._table.view?.target?.fields ?? [];
756
958
  const meta = Object.fromEntries(fields.map((field, index) => [
757
959
  index,
@@ -777,60 +979,9 @@ var TableModel = class extends Resource2 {
777
979
  };
778
980
  });
779
981
  }
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
982
  initializeEffects() {
832
983
  const rowOrderWatcher = effect2(() => {
833
- touch(this._sortedRows.value);
984
+ touch(this._sorting.sortedRows.value);
834
985
  this._onRowOrderChanged?.();
835
986
  });
836
987
  this._ctx.onDispose(rowOrderWatcher);
@@ -839,8 +990,8 @@ var TableModel = class extends Resource2 {
839
990
  const rowEffects = [];
840
991
  for (let row = start.row; row <= end.row; row++) {
841
992
  rowEffects.push(effect2(() => {
842
- const obj = this._sortedRows.value[row];
843
- this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
993
+ const obj = this._sorting.sortedRows.value[row];
994
+ this?._table?.view?.target?.fields.forEach((field) => touch(getValue2(obj, field.path)));
844
995
  this._onCellUpdate?.({
845
996
  row,
846
997
  col: start.col,
@@ -858,10 +1009,10 @@ var TableModel = class extends Resource2 {
858
1009
  * @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
859
1010
  */
860
1011
  updateCellData({ col, row }, update) {
861
- const dataRow = this._displayToDataIndex.get(row) ?? row;
1012
+ const dataRow = this._sorting.getDataIndex(row);
862
1013
  const fields = this.table.view?.target?.fields ?? [];
863
1014
  const field = fields[col];
864
- const value = getValue(this._rows.value[dataRow], field.path);
1015
+ const value = getValue2(this._rows.value[dataRow], field.path);
865
1016
  const updatedValue = update(value);
866
1017
  setValue(this._rows.value[dataRow], field.path, updatedValue);
867
1018
  }
@@ -874,9 +1025,6 @@ var TableModel = class extends Resource2 {
874
1025
  }
875
1026
  const field = this.table.view.target?.fields.find((field2) => field2.id === fieldId);
876
1027
  if (field && this._onDeleteColumn) {
877
- if (this._sorting.value?.fieldId === fieldId) {
878
- this.clearSort();
879
- }
880
1028
  this._onDeleteColumn(field.id);
881
1029
  }
882
1030
  }
@@ -894,18 +1042,6 @@ var TableModel = class extends Resource2 {
894
1042
  }
895
1043
  }
896
1044
  //
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
1045
  // Pinning
910
1046
  //
911
1047
  // TODO(burdon): Change to setPinned(on/off).
@@ -916,15 +1052,21 @@ var TableModel = class extends Resource2 {
916
1052
  this._pinnedRows.top = this._pinnedRows.top.filter((index) => index !== rowIndex);
917
1053
  this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index) => index !== rowIndex);
918
1054
  }
1055
+ //
1056
+ // View operations
1057
+ //
1058
+ saveView() {
1059
+ this._sorting.save();
1060
+ }
919
1061
  };
920
1062
 
921
1063
  // 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";
1064
+ import { signal as signal4 } from "@preact/signals-core";
1065
+ import { log } from "@dxos/log";
1066
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/model/modal-controller.ts";
925
1067
  var ModalController = class {
926
1068
  constructor() {
927
- this._state = signal3({
1069
+ this._state = signal4({
928
1070
  type: "closed"
929
1071
  });
930
1072
  this._triggerRef = {
@@ -984,8 +1126,8 @@ var ModalController = class {
984
1126
  onCreate
985
1127
  };
986
1128
  } else {
987
- log2.warn("anchor cell not found while creating new ref", void 0, {
988
- F: __dxlog_file3,
1129
+ log.warn("anchor cell not found while creating new ref", void 0, {
1130
+ F: __dxlog_file2,
989
1131
  L: 86,
990
1132
  S: this,
991
1133
  C: (f, a) => f(...a)
@@ -1007,14 +1149,14 @@ var ModalController = class {
1007
1149
  };
1008
1150
 
1009
1151
  // 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";
1152
+ import { signal as signal5 } from "@preact/signals-core";
1153
+ import { FormatEnum as FormatEnum4, getValue as getValue3 } from "@dxos/echo-schema";
1012
1154
  import { cellClassesForFieldType, cellClassesForRowSelection, formatForDisplay as formatForDisplay2 } from "@dxos/react-ui-form";
1013
1155
  import { toPlaneCellIndex } from "@dxos/react-ui-grid";
1014
1156
  import { mx } from "@dxos/react-ui-theme";
1015
1157
  import { VIEW_FIELD_LIMIT } from "@dxos/schema";
1016
1158
  var TablePresentation = class {
1017
- constructor(model, _visibleRange = signal4({
1159
+ constructor(model, _visibleRange = signal5({
1018
1160
  start: {
1019
1161
  row: 0,
1020
1162
  col: 0
@@ -1053,19 +1195,20 @@ var TablePresentation = class {
1053
1195
  const { props } = this.model.projection.getFieldProjection(field.id);
1054
1196
  const cell = {
1055
1197
  get value() {
1056
- const value = getValue2(obj, field.path);
1198
+ const value = getValue3(obj, field.path);
1057
1199
  if (value == null) {
1058
1200
  return "";
1059
1201
  }
1060
1202
  switch (props.format) {
1061
- case FormatEnum3.Boolean: {
1203
+ case FormatEnum4.Boolean:
1204
+ case FormatEnum4.SingleSelect: {
1062
1205
  return "";
1063
1206
  }
1064
- case FormatEnum3.Ref: {
1207
+ case FormatEnum4.Ref: {
1065
1208
  if (!field.referencePath) {
1066
1209
  return "";
1067
1210
  }
1068
- return getValue2(value.target, field.referencePath);
1211
+ return getValue3(value.target, field.referencePath);
1069
1212
  }
1070
1213
  default: {
1071
1214
  return formatForDisplay2({
@@ -1092,8 +1235,8 @@ var TablePresentation = class {
1092
1235
  if (classes.length > 0) {
1093
1236
  cell.className = mx(classes.flat());
1094
1237
  }
1095
- if (props.format === FormatEnum3.Ref && props.referenceSchema) {
1096
- const targetObj = getValue2(obj, field.path)?.target;
1238
+ if (props.format === FormatEnum4.Ref && props.referenceSchema) {
1239
+ const targetObj = getValue3(obj, field.path)?.target;
1097
1240
  if (targetObj) {
1098
1241
  cell.accessoryHtml = tableButtons.referencedCell.render({
1099
1242
  targetId: targetObj.id,
@@ -1101,8 +1244,8 @@ var TablePresentation = class {
1101
1244
  });
1102
1245
  }
1103
1246
  }
1104
- if (props.format === FormatEnum3.Boolean) {
1105
- const value = getValue2(obj, field.path);
1247
+ if (props.format === FormatEnum4.Boolean) {
1248
+ const value = getValue3(obj, field.path);
1106
1249
  cell.accessoryHtml = tableControls.switch.render({
1107
1250
  colIndex,
1108
1251
  rowIndex: displayIndex,
@@ -1110,6 +1253,16 @@ var TablePresentation = class {
1110
1253
  });
1111
1254
  cell.readonly = true;
1112
1255
  }
1256
+ if (props.format === FormatEnum4.SingleSelect) {
1257
+ const value = getValue3(obj, field.path);
1258
+ const options = this.model.projection.getFieldProjection(field.id).props.options;
1259
+ if (options) {
1260
+ const option = options.find((o) => o.id === value);
1261
+ if (option) {
1262
+ cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
1263
+ }
1264
+ }
1265
+ }
1113
1266
  const idx = toPlaneCellIndex({
1114
1267
  col: colIndex,
1115
1268
  row: displayIndex
@@ -1132,6 +1285,8 @@ var TablePresentation = class {
1132
1285
  const fields = this.model.table.view?.target?.fields ?? [];
1133
1286
  for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
1134
1287
  const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
1288
+ const sorting = this.model.sorting?.sorting;
1289
+ const direction = sorting?.fieldId === field.id ? sorting.direction : void 0;
1135
1290
  cells[toPlaneCellIndex({
1136
1291
  col,
1137
1292
  row: 0
@@ -1140,9 +1295,16 @@ var TablePresentation = class {
1140
1295
  value: props.title ?? field.path,
1141
1296
  readonly: true,
1142
1297
  resizeHandle: "col",
1143
- accessoryHtml: tableButtons.columnSettings.render({
1298
+ accessoryHtml: `
1299
+ ${direction !== void 0 ? tableButtons.sort.render({
1300
+ fieldId: field.id,
1301
+ direction
1302
+ }) : ""}
1303
+ ${tableButtons.columnSettings.render({
1144
1304
  fieldId: field.id
1145
- })
1305
+ })}
1306
+ `,
1307
+ className: "!bg-gridHeader"
1146
1308
  };
1147
1309
  }
1148
1310
  return cells;
@@ -1192,13 +1354,14 @@ var TablePresentation = class {
1192
1354
  col: 0,
1193
1355
  row: 0
1194
1356
  })]: {
1195
- value: "",
1196
1357
  accessoryHtml: tableControls.checkbox.render({
1197
1358
  rowIndex: 0,
1198
1359
  header: true,
1199
1360
  checked: this.model.selection.allRowsSeleted.value
1200
1361
  }),
1201
- readonly: true
1362
+ className: "!bg-gridHeader",
1363
+ readonly: true,
1364
+ value: ""
1202
1365
  }
1203
1366
  };
1204
1367
  }
@@ -1208,11 +1371,12 @@ var TablePresentation = class {
1208
1371
  col: 0,
1209
1372
  row: 0
1210
1373
  })]: {
1211
- value: "",
1212
1374
  accessoryHtml: tableButtons.addColumn.render({
1213
1375
  disabled: (this.model.table.view?.target?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT
1214
1376
  }),
1215
- readonly: true
1377
+ className: "!bg-gridHeader",
1378
+ readonly: true,
1379
+ value: ""
1216
1380
  }
1217
1381
  };
1218
1382
  }
@@ -1220,7 +1384,7 @@ var TablePresentation = class {
1220
1384
 
1221
1385
  // packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx
1222
1386
  import React6, { useCallback as useCallback3, useMemo as useMemo3, useRef } from "react";
1223
- import { FormatEnum as FormatEnum4 } from "@dxos/echo-schema";
1387
+ import { FormatEnum as FormatEnum5 } from "@dxos/echo-schema";
1224
1388
  import { invariant as invariant2 } from "@dxos/invariant";
1225
1389
  import { cellQuery, editorKeys, parseCellIndex, useGridContext as useGridContext2, GridCellEditor } from "@dxos/react-ui-grid";
1226
1390
 
@@ -1264,7 +1428,7 @@ var completion = ({ debug, onQuery, onMatch }) => {
1264
1428
  };
1265
1429
 
1266
1430
  // 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";
1431
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/TableCellEditor/TableCellEditor.tsx";
1268
1432
  var newValue = Symbol.for("newValue");
1269
1433
  var createOption = (text) => ({
1270
1434
  [newValue]: true,
@@ -1282,7 +1446,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
1282
1446
  const field = model.projection.view.fields[col];
1283
1447
  const fieldProjection2 = model.projection.getFieldProjection(field.id);
1284
1448
  invariant2(fieldProjection2, void 0, {
1285
- F: __dxlog_file4,
1449
+ F: __dxlog_file3,
1286
1450
  L: 67,
1287
1451
  S: void 0,
1288
1452
  A: [
@@ -1356,7 +1520,7 @@ var TableCellEditor = ({ model, modals, onEnter, onFocus, onQuery, __gridScope }
1356
1520
  ];
1357
1521
  if (onQuery) {
1358
1522
  switch (fieldProjection.props.format) {
1359
- case FormatEnum4.Ref: {
1523
+ case FormatEnum5.Ref: {
1360
1524
  extension2.push([
1361
1525
  completion({
1362
1526
  onQuery: (text) => onQuery(fieldProjection, text),
@@ -1432,7 +1596,7 @@ var determineNavigationDelta = ({ key, shift }) => {
1432
1596
  };
1433
1597
 
1434
1598
  // 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";
1599
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-table/src/components/Table/Table.tsx";
1436
1600
  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
1601
  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
1602
  var frozen = {
@@ -1500,6 +1664,10 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1500
1664
  modals.showReferencePanel(data.targetId, data.schemaId);
1501
1665
  break;
1502
1666
  }
1667
+ case "sort": {
1668
+ model?.sorting?.toggleSort(data.fieldId);
1669
+ break;
1670
+ }
1503
1671
  }
1504
1672
  return;
1505
1673
  }
@@ -1600,8 +1768,8 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1600
1768
  if (model && props.referenceSchema && field.referencePath) {
1601
1769
  const space = getSpace4(model.table);
1602
1770
  invariant3(space, void 0, {
1603
- F: __dxlog_file5,
1604
- L: 260,
1771
+ F: __dxlog_file4,
1772
+ L: 264,
1605
1773
  S: void 0,
1606
1774
  A: [
1607
1775
  "space",
@@ -1612,7 +1780,7 @@ var TableMain = /* @__PURE__ */ forwardRef(({ model, presentation, ignoreAttenti
1612
1780
  if (schema) {
1613
1781
  const { objects } = await space.db.query(Filter.schema(schema)).run();
1614
1782
  const options = objects.map((obj) => {
1615
- const value = getValue3(obj, field.referencePath);
1783
+ const value = getValue4(obj, field.referencePath);
1616
1784
  if (!value || typeof value !== "string") {
1617
1785
  return void 0;
1618
1786
  }
@@ -1685,79 +1853,76 @@ var Table = {
1685
1853
  Main: TableMain
1686
1854
  };
1687
1855
 
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",
1856
+ // packages/ui/react-ui-table/src/components/TableToolbar/TableToolbar.tsx
1857
+ import React8, { useCallback as useCallback5, useMemo as useMemo5 } from "react";
1858
+ import { create as create3 } from "@dxos/live-object";
1859
+ import { createGapSeparator, createMenuAction, MenuProvider, ToolbarMenu, useMenuActions } from "@dxos/react-ui-menu";
1860
+ var createTableToolbarActions = (state) => {
1861
+ const add = createMenuAction("add-row", {
1862
+ type: "add-row",
1737
1863
  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",
1864
+ label: [
1865
+ "add row",
1866
+ {
1867
+ ns: translationKey
1868
+ }
1869
+ ],
1870
+ testId: "table.toolbar.add-row"
1871
+ });
1872
+ const save = createMenuAction("save-view", {
1873
+ type: "save-view",
1874
+ icon: "ph--floppy-disk--regular",
1875
+ label: [
1876
+ "save view label",
1877
+ {
1878
+ ns: translationKey
1879
+ }
1880
+ ],
1881
+ testId: "table.toolbar.save-view",
1882
+ iconOnly: false,
1883
+ hidden: !state.viewDirty
1884
+ });
1885
+ const gap = createGapSeparator();
1886
+ const comment = createMenuAction("comment", {
1887
+ type: "comment",
1749
1888
  icon: "ph--chat-text--regular",
1750
- "data-testid": "table.toolbar.comment",
1751
- onClick: () => onAction?.({
1752
- type: "comment"
1753
- })
1754
- }, t("create comment")));
1889
+ label: [
1890
+ "create comment",
1891
+ {
1892
+ ns: translationKey
1893
+ }
1894
+ ],
1895
+ testId: "table.toolbar.comment"
1896
+ });
1897
+ const nodes = [
1898
+ add,
1899
+ save,
1900
+ ...gap.nodes,
1901
+ comment
1902
+ ];
1903
+ return {
1904
+ nodes,
1905
+ edges: nodes.map(({ id: target }) => ({
1906
+ source: "root",
1907
+ target
1908
+ }))
1909
+ };
1755
1910
  };
1756
- var Toolbar = {
1757
- Root: ToolbarRoot,
1758
- Editing,
1759
- Actions,
1760
- Separator: ToolbarSeparator
1911
+ var TableToolbar = ({ classNames, viewDirty, attendableId, onAction }) => {
1912
+ const state = useMemo5(() => create3({
1913
+ viewDirty
1914
+ }), []);
1915
+ const actionsCreator = useCallback5(() => createTableToolbarActions(state), [
1916
+ state
1917
+ ]);
1918
+ const menu = useMenuActions(actionsCreator);
1919
+ return /* @__PURE__ */ React8.createElement(MenuProvider, {
1920
+ ...menu,
1921
+ attendableId,
1922
+ onAction
1923
+ }, /* @__PURE__ */ React8.createElement(ToolbarMenu, {
1924
+ classNames
1925
+ }));
1761
1926
  };
1762
1927
 
1763
1928
  // packages/ui/react-ui-table/src/hooks/useTableModel.ts
@@ -1804,8 +1969,8 @@ export {
1804
1969
  TableModel,
1805
1970
  TablePresentation,
1806
1971
  TableSchema,
1972
+ TableToolbar,
1807
1973
  TableType,
1808
- Toolbar,
1809
1974
  initializeTable,
1810
1975
  renderCheckbox,
1811
1976
  renderSwitch,
@@ -1813,7 +1978,6 @@ export {
1813
1978
  tableControls,
1814
1979
  touch,
1815
1980
  translations_default as translations,
1816
- useTableModel,
1817
- useToolbarContext
1981
+ useTableModel
1818
1982
  };
1819
1983
  //# sourceMappingURL=index.mjs.map