@dxos/react-ui-table 0.7.5-main.499c70c → 0.7.5-main.6a330ac

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