@dxos/react-ui-table 0.8.4-main.5ea62a8 → 0.8.4-main.66e292d

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 (115) hide show
  1. package/dist/lib/browser/chunk-5XWSB3JP.mjs +71 -0
  2. package/dist/lib/browser/chunk-5XWSB3JP.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-RQYXNWWN.mjs → chunk-KNC7XYT6.mjs} +529 -568
  4. package/dist/lib/browser/chunk-KNC7XYT6.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +187 -322
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/testing/index.mjs +15 -25
  9. package/dist/lib/browser/testing/index.mjs.map +3 -3
  10. package/dist/lib/browser/types/index.mjs +3 -3
  11. package/dist/lib/node-esm/{chunk-QNDUGIS7.mjs → chunk-CWSGS3Y2.mjs} +529 -568
  12. package/dist/lib/node-esm/chunk-CWSGS3Y2.mjs.map +7 -0
  13. package/dist/lib/node-esm/chunk-MKTMOY46.mjs +72 -0
  14. package/dist/lib/node-esm/chunk-MKTMOY46.mjs.map +7 -0
  15. package/dist/lib/node-esm/index.mjs +187 -322
  16. package/dist/lib/node-esm/index.mjs.map +4 -4
  17. package/dist/lib/node-esm/meta.json +1 -1
  18. package/dist/lib/node-esm/testing/index.mjs +15 -25
  19. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/types/index.mjs +3 -3
  21. package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
  22. package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
  23. package/dist/types/src/components/Table/DynamicTable.stories.d.ts +5 -5
  24. package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Table/Relations.stories.d.ts +73 -9
  26. package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
  28. package/dist/types/src/components/Table/Table.d.ts +5 -2
  29. package/dist/types/src/components/Table/Table.d.ts.map +1 -1
  30. package/dist/types/src/components/Table/Table.stories.d.ts +72 -10
  31. package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
  32. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -5
  33. package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
  34. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -3
  35. package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +3 -4
  37. package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
  39. package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
  40. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +2 -2
  41. package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
  42. package/dist/types/src/components/testing.d.ts +3 -5
  43. package/dist/types/src/components/testing.d.ts.map +1 -1
  44. package/dist/types/src/hooks/index.d.ts +2 -1
  45. package/dist/types/src/hooks/index.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useAddRow.d.ts +1 -1
  47. package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
  48. package/dist/types/src/hooks/useProjectionModel.d.ts +5 -0
  49. package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -0
  50. package/dist/types/src/hooks/useTableModel.d.ts +4 -5
  51. package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
  52. package/dist/types/src/model/modal-controller.d.ts +0 -7
  53. package/dist/types/src/model/modal-controller.d.ts.map +1 -1
  54. package/dist/types/src/model/table-model.d.ts +8 -10
  55. package/dist/types/src/model/table-model.d.ts.map +1 -1
  56. package/dist/types/src/model/table-presentation.d.ts.map +1 -1
  57. package/dist/types/src/model/table-sorting.d.ts +8 -5
  58. package/dist/types/src/model/table-sorting.d.ts.map +1 -1
  59. package/dist/types/src/playwright/TableManager.d.ts +1 -1
  60. package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
  61. package/dist/types/src/testing/useTestTableModel.d.ts +21 -29
  62. package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
  63. package/dist/types/src/translations.d.ts +2 -2
  64. package/dist/types/src/types/Table.d.ts +28 -0
  65. package/dist/types/src/types/Table.d.ts.map +1 -0
  66. package/dist/types/src/types/index.d.ts +1 -1
  67. package/dist/types/src/types/index.d.ts.map +1 -1
  68. package/dist/types/src/util/dynamic-table.d.ts +9 -9
  69. package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
  70. package/dist/types/src/util/schema.d.ts +1 -1
  71. package/dist/types/src/util/schema.d.ts.map +1 -1
  72. package/dist/types/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +53 -52
  74. package/src/components/Table/DynamicTable.stories.tsx +14 -13
  75. package/src/components/Table/DynamicTable.tsx +15 -10
  76. package/src/components/Table/Relations.stories.tsx +133 -29
  77. package/src/components/Table/RowActionsMenu.tsx +21 -13
  78. package/src/components/Table/Table.stories.tsx +85 -79
  79. package/src/components/Table/Table.tsx +13 -9
  80. package/src/components/TableCellEditor/FormCellEditor.tsx +51 -41
  81. package/src/components/TableCellEditor/TableCellEditor.stories.tsx +21 -47
  82. package/src/components/TableCellEditor/TableCellEditor.tsx +20 -115
  83. package/src/components/TableCellEditor/index.ts +1 -0
  84. package/src/components/TableToolbar/TableToolbar.tsx +32 -22
  85. package/src/components/testing.ts +4 -4
  86. package/src/hooks/index.ts +2 -1
  87. package/src/hooks/useAddRow.ts +1 -1
  88. package/src/hooks/useProjectionModel.ts +19 -0
  89. package/src/hooks/useTableModel.ts +11 -13
  90. package/src/model/modal-controller.ts +0 -28
  91. package/src/model/table-model.test.ts +19 -9
  92. package/src/model/table-model.ts +42 -41
  93. package/src/model/table-presentation.test.ts +22 -20
  94. package/src/model/table-presentation.ts +12 -13
  95. package/src/model/table-sorting.ts +31 -13
  96. package/src/playwright/TableManager.ts +3 -3
  97. package/src/playwright/smoke.spec.ts +8 -6
  98. package/src/testing/useTestTableModel.ts +33 -22
  99. package/src/translations.ts +2 -2
  100. package/src/types/Table.ts +89 -0
  101. package/src/types/index.ts +1 -1
  102. package/src/util/dynamic-table.ts +17 -21
  103. package/src/util/schema.ts +4 -4
  104. package/dist/lib/browser/chunk-63UUF4IG.mjs +0 -66
  105. package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
  106. package/dist/lib/browser/chunk-RQYXNWWN.mjs.map +0 -7
  107. package/dist/lib/node-esm/chunk-K734DJB7.mjs +0 -67
  108. package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
  109. package/dist/lib/node-esm/chunk-QNDUGIS7.mjs.map +0 -7
  110. package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
  111. package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
  112. package/dist/types/src/types/schema.d.ts +0 -28
  113. package/dist/types/src/types/schema.d.ts.map +0 -1
  114. package/src/components/Table/CreateRefPanel.tsx +0 -103
  115. package/src/types/schema.ts +0 -73
@@ -3,15 +3,16 @@
3
3
  //
4
4
 
5
5
  import { computed } from '@preact/signals-core';
6
- import { Schema } from 'effect';
6
+ import * as Schema from 'effect/Schema';
7
7
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
8
8
 
9
- import { TypedObject } from '@dxos/echo-schema';
10
- import { updateCounter } from '@dxos/echo-schema/testing';
9
+ import { Filter, Query } from '@dxos/echo';
10
+ import { TypedObject } from '@dxos/echo/internal';
11
+ import { updateCounter } from '@dxos/echo/testing';
12
+ import { createEchoSchema } from '@dxos/echo/testing';
11
13
  import { registerSignalsRuntime } from '@dxos/echo-signals';
12
14
  import { live } from '@dxos/live-object';
13
- import { createEchoSchema } from '@dxos/live-object/testing';
14
- import { createView } from '@dxos/schema';
15
+ import { ProjectionModel, View } from '@dxos/schema';
15
16
 
16
17
  import { Table } from '../types';
17
18
 
@@ -24,7 +25,7 @@ registerSignalsRuntime();
24
25
 
25
26
  describe('TableModel', () => {
26
27
  let updateCount = 0;
27
- let model: TableModel;
28
+ let model: any;
28
29
 
29
30
  beforeEach(async () => {
30
31
  updateCount = 0;
@@ -32,6 +33,11 @@ describe('TableModel', () => {
32
33
  onCellUpdate: () => updateCount++,
33
34
  });
34
35
  await model.open();
36
+ model.setRows([
37
+ { id: '1', title: 'Test', completed: false },
38
+ { id: '2', title: 'Test 2', completed: true },
39
+ { id: '3', title: 'Test 3', completed: false },
40
+ ]);
35
41
  });
36
42
 
37
43
  afterEach(async () => {
@@ -106,7 +112,11 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
106
112
 
107
113
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
108
114
  const schema = createEchoSchema(Test);
109
- const table = Table.make();
110
- const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
111
- return new TableModel({ view, schema: schema.jsonSchema, ...props });
115
+ const view = View.make({
116
+ query: Query.select(Filter.type(schema)),
117
+ jsonSchema: schema.jsonSchema,
118
+ });
119
+ const object = Table.make({ view });
120
+ const projection = new ProjectionModel(schema.jsonSchema, view.projection);
121
+ return new TableModel({ object, projection, ...props });
112
122
  };
@@ -5,21 +5,11 @@
5
5
  import { type ReadonlySignal, computed, effect, signal } from '@preact/signals-core';
6
6
 
7
7
  import { Resource } from '@dxos/context';
8
- import { Obj, Ref } from '@dxos/echo';
9
- import {
10
- type FieldSortType,
11
- FormatEnum,
12
- type JsonProp,
13
- type JsonSchemaType,
14
- getSchema,
15
- getValue,
16
- setValue,
17
- toEffectSchema,
18
- } from '@dxos/echo-schema';
8
+ import { Format, Obj, Ref } from '@dxos/echo';
9
+ import { type JsonProp, type JsonSchemaType, getValue, setValue, toEffectSchema } from '@dxos/echo/internal';
19
10
  import { invariant } from '@dxos/invariant';
20
11
  import { ObjectId } from '@dxos/keys';
21
12
  import { getSnapshot, isLiveObject } from '@dxos/live-object';
22
- import { fullyQualifiedId } from '@dxos/react-client/echo';
23
13
  import { type Label } from '@dxos/react-ui';
24
14
  import { formatForEditing, parseValue } from '@dxos/react-ui-form';
25
15
  import {
@@ -28,9 +18,16 @@ import {
28
18
  type DxGridPlaneRange,
29
19
  type DxGridPosition,
30
20
  } from '@dxos/react-ui-grid';
31
- import { type DataType, ProjectionModel, type PropertyType, type ValidationError, validateSchema } from '@dxos/schema';
32
-
33
- import { Table } from '../types';
21
+ import {
22
+ type FieldSortType,
23
+ type ProjectionModel,
24
+ type PropertyType,
25
+ type ValidationError,
26
+ type View,
27
+ validateSchema,
28
+ } from '@dxos/schema';
29
+
30
+ import { type Table } from '../types';
34
31
  import { touch } from '../util';
35
32
  import { extractTagIds } from '../util/tag';
36
33
 
@@ -72,9 +69,8 @@ const defaultFeatures: TableFeatures = {
72
69
  export type InsertRowResult = 'draft' | 'final';
73
70
 
74
71
  export type TableModelProps<T extends TableRow = TableRow> = {
75
- view: DataType.View;
76
- schema: JsonSchemaType;
77
- projection?: ProjectionModel;
72
+ object: Table.Table;
73
+ projection: ProjectionModel;
78
74
  features?: Partial<TableFeatures>;
79
75
  sorting?: FieldSortType[];
80
76
  initialSelection?: string[];
@@ -90,9 +86,8 @@ export type TableModelProps<T extends TableRow = TableRow> = {
90
86
  };
91
87
 
92
88
  export class TableModel<T extends TableRow = TableRow> extends Resource {
93
- private readonly _view: DataType.View;
89
+ private readonly _object: Table.Table;
94
90
  private readonly _projection: ProjectionModel;
95
- private _table?: Table.Table;
96
91
 
97
92
  private readonly _visibleRange = signal<DxGridPlaneRange>({
98
93
  start: { row: 0, col: 0 },
@@ -117,8 +112,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
117
112
  private _columnMeta?: ReadonlySignal<DxGridAxisMeta>;
118
113
 
119
114
  constructor({
120
- view,
121
- schema,
115
+ object,
122
116
  projection,
123
117
  features = {},
124
118
  sorting = [],
@@ -133,8 +127,9 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
133
127
  onRowAction,
134
128
  }: TableModelProps<T>) {
135
129
  super();
136
- this._view = view;
137
- this._projection = projection ?? new ProjectionModel(schema, view.projection);
130
+ this._object = object;
131
+ this._projection = projection;
132
+ this._projection.normalizeView();
138
133
 
139
134
  // TODO(ZaymonFC): Use our more robust config merging module?
140
135
  this._features = { ...defaultFeatures, ...features };
@@ -144,7 +139,11 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
144
139
  'Single selection is not compatible with editable tables.',
145
140
  );
146
141
 
147
- this._sorting = new TableSorting(this._rows, this._view, this._projection);
142
+ this._sorting = new TableSorting({
143
+ rows: this._rows,
144
+ getView: () => this.view,
145
+ projection: this._projection,
146
+ });
148
147
 
149
148
  if (sorting.length > 0) {
150
149
  const [sort] = sorting;
@@ -169,16 +168,17 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
169
168
  }
170
169
 
171
170
  public get id(): string {
172
- return fullyQualifiedId(this._view);
171
+ return Obj.getDXN(this._object).toString();
173
172
  }
174
173
 
175
- public get view(): DataType.View {
176
- return this._view;
174
+ public get view(): View.View {
175
+ const view = this._object.view.target;
176
+ invariant(view, 'Table model not initialized');
177
+ return view;
177
178
  }
178
179
 
179
180
  public get table(): Table.Table {
180
- invariant(this._table, 'Model not initialized');
181
- return this._table;
181
+ return this._object;
182
182
  }
183
183
 
184
184
  public get projection(): ProjectionModel {
@@ -238,10 +238,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
238
238
  //
239
239
 
240
240
  protected override async _open(): Promise<void> {
241
- const presentation = this._view.presentation.target ?? (await this._view.presentation.load());
242
- invariant(Obj.instanceOf(Table.Table, presentation));
243
- this._table = presentation;
244
-
241
+ await this._object.view.load();
245
242
  this.initializeColumnMeta();
246
243
  this.initializeEffects();
247
244
  await this._selection.open(this._ctx);
@@ -464,7 +461,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
464
461
 
465
462
  const { props } = this._projection.getFieldProjection(field.id);
466
463
  switch (props.format) {
467
- case FormatEnum.Ref: {
464
+ case Format.TypeFormat.Ref: {
468
465
  if (!field.referencePath) {
469
466
  return ''; // TODO(burdon): Show error.
470
467
  }
@@ -473,7 +470,11 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
473
470
  }
474
471
 
475
472
  default: {
476
- return formatForEditing({ type: props.type, format: props.format, value });
473
+ return formatForEditing({
474
+ type: props.type,
475
+ format: props.format,
476
+ value,
477
+ });
477
478
  }
478
479
  }
479
480
  };
@@ -517,7 +518,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
517
518
  const snapshot = getSnapshot(currentRow);
518
519
  setValue(snapshot, field.path, transformedValue);
519
520
 
520
- const schema = getSchema(currentRow);
521
+ const schema = Obj.getSchema(currentRow);
521
522
  invariant(schema);
522
523
 
523
524
  const validationResult = validateSchema(schema, snapshot);
@@ -543,7 +544,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
543
544
  const transformedValue = editorTextToCellValue(props, value);
544
545
 
545
546
  // Special handling for Ref format to preserve existing behavior
546
- if (props.format === FormatEnum.Ref && !isLiveObject(value)) {
547
+ if (props.format === Format.TypeFormat.Ref && !isLiveObject(value)) {
547
548
  return;
548
549
  }
549
550
 
@@ -658,7 +659,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
658
659
 
659
660
  const editorTextToCellValue = (props: PropertyType, value: any): any => {
660
661
  switch (props.format) {
661
- case FormatEnum.Ref: {
662
+ case Format.TypeFormat.Ref: {
662
663
  if (isLiveObject(value)) {
663
664
  return Ref.make(value);
664
665
  } else {
@@ -666,7 +667,7 @@ const editorTextToCellValue = (props: PropertyType, value: any): any => {
666
667
  }
667
668
  }
668
669
 
669
- case FormatEnum.SingleSelect: {
670
+ case Format.TypeFormat.SingleSelect: {
670
671
  const ids = extractTagIds(value);
671
672
  if (ids && ids.length > 0) {
672
673
  return ids[0];
@@ -675,7 +676,7 @@ const editorTextToCellValue = (props: PropertyType, value: any): any => {
675
676
  }
676
677
  }
677
678
 
678
- case FormatEnum.MultiSelect: {
679
+ case Format.TypeFormat.MultiSelect: {
679
680
  const ids = extractTagIds(value);
680
681
  return ids || value;
681
682
  }
@@ -2,13 +2,14 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
  import { beforeEach, describe, expect, it } from 'vitest';
7
7
 
8
- import { TypedObject } from '@dxos/echo-schema';
8
+ import { Filter, Query } from '@dxos/echo';
9
+ import { TypedObject } from '@dxos/echo/internal';
10
+ import { createEchoSchema } from '@dxos/echo/testing';
9
11
  import { live } from '@dxos/live-object';
10
- import { createEchoSchema } from '@dxos/live-object/testing';
11
- import { createView } from '@dxos/schema';
12
+ import { ProjectionModel, View } from '@dxos/schema';
12
13
 
13
14
  import { Table } from '../types';
14
15
 
@@ -26,8 +27,8 @@ describe('TablePresentation', () => {
26
27
  updateCount = 0;
27
28
  ({ data } = live({
28
29
  data: [
29
- { col1: 'A', col2: 1, col3: true },
30
- { col1: 'B', col2: 2, col3: false },
30
+ { title: 'A', count: 1 },
31
+ { title: 'B', count: 2 },
31
32
  ],
32
33
  }));
33
34
 
@@ -44,8 +45,8 @@ describe('TablePresentation', () => {
44
45
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
45
46
 
46
47
  // Trigger a row update.
47
- data[0].col1 = 'New Value';
48
- expect(updateCount).toBe(1);
48
+ data[0].title = 'New Value';
49
+ expect(updateCount).toBe(2);
49
50
 
50
51
  // Verify the new value through getCells.
51
52
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 0, col: 0 } }, 'grid');
@@ -56,15 +57,14 @@ describe('TablePresentation', () => {
56
57
  // Set up visible range to include our test data.
57
58
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
58
59
 
59
- data.push({ col1: 'C', col2: 3, col3: true });
60
- expect(updateCount).toBe(1);
60
+ data.push({ title: 'C', count: 3 });
61
+ expect(updateCount).toBe(2);
61
62
 
62
63
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
63
64
 
64
65
  // Verify the new row's data.
65
66
  expect(cells['0,2']?.value).toBe('C');
66
67
  expect(cells['1,2']?.value).toBe('3');
67
- expect(cells['2,2']?.value).toBe('true');
68
68
  });
69
69
 
70
70
  it('should handle combined operations reactively', () => {
@@ -72,32 +72,34 @@ describe('TablePresentation', () => {
72
72
  presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 2, col: 2 } }, 'grid');
73
73
 
74
74
  // Multiple operations.
75
- data[0].col1 = 'Updated A';
76
- data.push({ col1: 'C', col2: 3, col3: true });
75
+ data[0].title = 'Updated A';
76
+ data.push({ title: 'C', count: 3, completed: true });
77
77
  data.splice(1, 1);
78
78
 
79
79
  // We expect one update per operation affecting visible rows.
80
- expect(updateCount).toBe(3);
80
+ expect(updateCount).toBe(4);
81
81
 
82
82
  const cells = presentation.getCells({ start: { row: 0, col: 0 }, end: { row: 1, col: 2 } }, 'grid');
83
83
 
84
84
  // Verify final state.
85
85
  expect(cells['0,0']?.value).toBe('Updated A');
86
86
  expect(cells['0,1']?.value).toBe('C');
87
- expect(cells['1,1']?.value).toBe('3');
88
- expect(cells['2,1']?.value).toBe('true');
89
87
  });
90
88
  });
91
89
  });
92
90
 
93
91
  class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.1.0' })({
94
92
  title: Schema.String,
95
- completed: Schema.Boolean,
93
+ count: Schema.Number,
96
94
  }) {}
97
95
 
98
96
  const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
99
97
  const schema = createEchoSchema(Test);
100
- const table = Table.make();
101
- const view = createView({ typename: schema.typename, jsonSchema: schema.jsonSchema, presentation: table });
102
- return new TableModel({ view, schema: schema.jsonSchema, ...props });
98
+ const view = View.make({
99
+ query: Query.select(Filter.type(schema)),
100
+ jsonSchema: schema.jsonSchema,
101
+ });
102
+ const object = Table.make({ view });
103
+ const projection = new ProjectionModel(schema.jsonSchema, view.projection);
104
+ return new TableModel({ object, projection, ...props });
103
105
  };
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import { signal } from '@preact/signals-core';
6
- import { isNotNullable } from 'effect/Predicate';
6
+ import * as Predicate from 'effect/Predicate';
7
7
 
8
8
  import { Obj } from '@dxos/echo';
9
- import { FormatEnum, TypeEnum, getValue } from '@dxos/echo-schema';
9
+ import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
10
10
  import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
11
11
  import {
12
12
  type DxGridCellValue,
@@ -120,12 +120,12 @@ export class TablePresentation<T extends TableRow = TableRow> {
120
120
  }
121
121
 
122
122
  switch (props.format) {
123
- case FormatEnum.Boolean:
124
- case FormatEnum.SingleSelect:
125
- case FormatEnum.MultiSelect: {
123
+ case Format.TypeFormat.Boolean:
124
+ case Format.TypeFormat.SingleSelect:
125
+ case Format.TypeFormat.MultiSelect: {
126
126
  return '';
127
127
  }
128
- case FormatEnum.Ref: {
128
+ case Format.TypeFormat.Ref: {
129
129
  if (!field.referencePath) {
130
130
  return ''; // TODO(burdon): Show error.
131
131
  }
@@ -190,7 +190,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
190
190
 
191
191
  const tags = targetArray
192
192
  .map(getLabel)
193
- .filter(isNotNullable)
193
+ .filter(Predicate.isNotNullable)
194
194
  .map((title) => {
195
195
  return `<span class="dx-tag" data-hue="neutral">${title}</span>`;
196
196
  })
@@ -201,16 +201,16 @@ export class TablePresentation<T extends TableRow = TableRow> {
201
201
  }
202
202
 
203
203
  // References.
204
- if (props.format === FormatEnum.Ref && props.referenceSchema) {
204
+ if (props.format === Format.TypeFormat.Ref && props.referenceSchema) {
205
205
  const targetObj = getValue(obj, field.path)?.target;
206
206
  if (targetObj) {
207
207
  const dxn = Obj.getDXN(targetObj)?.toString();
208
- cell.accessoryHtml = `<dx-ref-tag refId=${dxn} class="dx-button is-6 pli-[3px] pbe-[2px] min-bs-0 absolute inline-end-2 block-start-[.2rem]" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-ref-tag>`;
208
+ cell.accessoryHtml = `<div role="none" class="absolute inline-end-0 inset-block-0 p-[--dx-grid-cell-content-padding-block]"><dx-anchor refId=${dxn} class="dx-button is-6 aspect-square min-bs-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
209
209
  }
210
210
  }
211
211
 
212
212
  // Booleans.
213
- if (props.format === FormatEnum.Boolean) {
213
+ if (props.format === Format.TypeFormat.Boolean) {
214
214
  const value = getValue(obj, field.path);
215
215
  cell.accessoryHtml = tableControls.switch.render({
216
216
  colIndex,
@@ -221,7 +221,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
221
221
  }
222
222
 
223
223
  // Single-Selects.
224
- if (props.format === FormatEnum.SingleSelect) {
224
+ if (props.format === Format.TypeFormat.SingleSelect) {
225
225
  const value = getValue(obj, field.path);
226
226
  const options = this.model.projection.getFieldProjection(field.id).props.options;
227
227
  if (options) {
@@ -233,7 +233,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
233
233
  }
234
234
 
235
235
  // Multi-Selects.
236
- if (props.format === FormatEnum.MultiSelect) {
236
+ if (props.format === Format.TypeFormat.MultiSelect) {
237
237
  const values = getValue(obj, field.path) as string[] | undefined;
238
238
  const options = this.model.projection.getFieldProjection(field.id).props.options;
239
239
  if (options && values && values.length > 0) {
@@ -468,7 +468,6 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
468
468
 
469
469
  switch (selectionMode) {
470
470
  case 'single':
471
- // TODO(ZaymonFC): @thure, do we need a grid version of 'currentRelated'?
472
471
  return ['!bg-currentRelated dx-grid__cell--no-focus-unfurl hover:bg-hoverSurface !cursor-pointer'];
473
472
  case 'multiple':
474
473
  return ['!bg-gridCellSelected'];
@@ -5,12 +5,21 @@
5
5
  import { type ReadonlySignal, type Signal, computed, signal } from '@preact/signals-core';
6
6
  import orderBy from 'lodash.orderby';
7
7
 
8
- import { type FieldSortType, FormatEnum, type SortDirectionType, TypeEnum, getValue } from '@dxos/echo-schema';
8
+ import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
9
9
  import { formatForDisplay } from '@dxos/react-ui-form';
10
- import type { DataType, FieldType, ProjectionModel, PropertyType } from '@dxos/schema';
10
+ import {
11
+ type FieldSortType,
12
+ type FieldType,
13
+ type ProjectionModel,
14
+ type PropertyType,
15
+ type SortDirectionType,
16
+ type View,
17
+ } from '@dxos/schema';
11
18
 
12
19
  import { type TableRow } from './table-model';
13
20
 
21
+ // TODO(wittjosiah): Remove. Use ordering in queries instead.
22
+
14
23
  /**
15
24
  * Represents the local sort state.
16
25
  * - undefined: No local sort set, fallback to view sort.
@@ -43,19 +52,27 @@ type LocalSort = { type: 'cleared' } | { type: 'active'; sort: FieldSortType } |
43
52
  export class TableSorting<T extends TableRow> {
44
53
  private readonly _displayToDataIndex = new Map<number, number>();
45
54
  private readonly _rows: Signal<T[]>;
55
+ private readonly _getView: () => View.View;
56
+ private readonly _projection: ProjectionModel;
46
57
  private readonly _localSort = signal<LocalSort>(undefined);
47
58
  private readonly _isDirty: ReadonlySignal<boolean>;
48
59
  public readonly sortedRows: ReadonlySignal<T[]>;
49
60
 
50
- constructor(
51
- rows: Signal<T[]>,
52
- private readonly _view: DataType.View,
53
- private readonly _projection: ProjectionModel,
54
- ) {
61
+ constructor({
62
+ rows,
63
+ projection,
64
+ getView,
65
+ }: {
66
+ rows: Signal<T[]>;
67
+ getView: () => View.View;
68
+ projection: ProjectionModel;
69
+ }) {
55
70
  this._rows = rows;
71
+ this._getView = getView;
72
+ this._projection = projection;
56
73
  this._isDirty = computed(() => {
57
74
  const local = this._localSort.value;
58
- const viewSort = this._view.query.sort?.[0];
75
+ const viewSort = this._getView().sort?.[0];
59
76
  if (local?.type === 'cleared') {
60
77
  return viewSort !== undefined;
61
78
  }
@@ -83,7 +100,7 @@ export class TableSorting<T extends TableRow> {
83
100
  return local.sort;
84
101
  }
85
102
 
86
- return this._view.query.sort?.[0];
103
+ return this._getView().sort?.[0];
87
104
  }
88
105
 
89
106
  /**
@@ -128,10 +145,11 @@ export class TableSorting<T extends TableRow> {
128
145
 
129
146
  public save(): void {
130
147
  if (this._localSort.value !== undefined) {
148
+ const view = this._getView();
131
149
  if (this._localSort.value.type === 'active') {
132
- this._view.query.sort = [this._localSort.value.sort];
150
+ view.sort = [this._localSort.value.sort];
133
151
  } else {
134
- this._view.query.sort = [];
152
+ view.sort = [];
135
153
  }
136
154
  this._localSort.value = undefined;
137
155
  }
@@ -193,7 +211,7 @@ export class TableSorting<T extends TableRow> {
193
211
  * For single select fields, the sort value is determined by the index of the option in the
194
212
  * options array. This ensures sort order matches the configured option order.
195
213
  */
196
- if (format === FormatEnum.SingleSelect) {
214
+ if (format === Format.TypeFormat.SingleSelect) {
197
215
  const options = props.options;
198
216
  if (!options) {
199
217
  return undefined;
@@ -206,7 +224,7 @@ export class TableSorting<T extends TableRow> {
206
224
  // TODO(ZaymonFC): Should we format this returned value for display based on the referenced prop format?
207
225
  // Right now we're going to be sorting based on the raw values.
208
226
  // Maybe we need to recurse this function with the referenced prop format and field?
209
- if (format === FormatEnum.Ref && field.referencePath) {
227
+ if (format === Format.TypeFormat.Ref && field.referencePath) {
210
228
  const refValue = getValue(value.target, field.referencePath);
211
229
  return refValue;
212
230
  }
@@ -53,12 +53,12 @@ export class TableManager {
53
53
  await this.page.getByTestId(TABLE_SELECTORS.columnSort[direction]).click();
54
54
  }
55
55
 
56
- public async addColumn({ label, format }: { label: string; format: 'number' }): Promise<void> {
56
+ public async addColumn({ label, format }: { label: string; format: string }): Promise<void> {
57
57
  await this.page.getByTestId(TABLE_SELECTORS.newColumnButton).click();
58
58
  await this.page.getByRole('combobox').click();
59
- await this.page.getByLabel(`format ${format}`).click();
59
+ await this.page.getByLabel(format).click();
60
60
  await this.page.getByPlaceholder('Property label.').click();
61
61
  await this.page.getByPlaceholder('Property label.').fill(label);
62
- await this.page.getByRole('button', { name: 'button save' }).click();
62
+ await this.page.getByRole('button', { name: 'Save' }).click();
63
63
  }
64
64
  }
@@ -130,7 +130,7 @@ test.describe('Table', () => {
130
130
  await table.grid.ready();
131
131
  const newColumnLabel = 'TEST LABEL';
132
132
 
133
- await table.addColumn({ label: newColumnLabel, format: 'number' });
133
+ await table.addColumn({ label: newColumnLabel, format: 'Number' });
134
134
 
135
135
  await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
136
136
  await page.close();
@@ -159,7 +159,8 @@ test.describe('Table', () => {
159
159
  await page.close();
160
160
  });
161
161
 
162
- test('extant relations work as expected', async ({ browser, browserName }) => {
162
+ // TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
163
+ test.skip('extant relations work as expected', async ({ browser, browserName }) => {
163
164
  test.skip(browserName === 'webkit');
164
165
  test.skip(browserName === 'firefox');
165
166
  const { page } = await setupPage(browser, { url: relationsStoryUrl });
@@ -187,7 +188,7 @@ test.describe('Table', () => {
187
188
  // Click on the combobox to open options
188
189
  await page.getByRole('combobox').click();
189
190
  await page.pause();
190
- await page.getByPlaceholder('Search...').focus();
191
+ await page.getByPlaceholder('Search…').focus();
191
192
 
192
193
  // Type the first few letters of an org name.
193
194
  const orgName =
@@ -213,7 +214,8 @@ test.describe('Table', () => {
213
214
  await page.close();
214
215
  });
215
216
 
216
- test('new relations work as expected', async ({ browser, browserName }) => {
217
+ // TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
218
+ test.skip('new relations work as expected', async ({ browser, browserName }) => {
217
219
  test.skip(browserName === 'webkit');
218
220
  test.skip(browserName === 'firefox');
219
221
  const { page } = await setupPage(browser, { url: relationsStoryUrl });
@@ -240,7 +242,7 @@ test.describe('Table', () => {
240
242
 
241
243
  // Click on the combobox to open options
242
244
  await page.getByRole('combobox').click();
243
- await page.getByPlaceholder('Search...').focus();
245
+ await page.getByPlaceholder('Search…').focus();
244
246
 
245
247
  // Type the first few letters of an org name.
246
248
  const orgName = 'Sally';
@@ -254,7 +256,7 @@ test.describe('Table', () => {
254
256
  await page.pause();
255
257
 
256
258
  // Click the save button in the popover
257
- await page.getByTestId('save-button').click();
259
+ await page.getByTestId('create-referenced-object-form').getByTestId('save-button').click();
258
260
 
259
261
  // Assert that the cell element has the org name
260
262
  await expect(targetCell).toHaveText(orgName);