@dxos/react-ui-table 0.8.4-main.5ea62a8 → 0.8.4-main.ae835ea
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.
- package/dist/lib/browser/{chunk-63UUF4IG.mjs → chunk-4K7JPALT.mjs} +5 -5
- package/dist/lib/browser/chunk-4K7JPALT.mjs.map +7 -0
- package/dist/lib/browser/{chunk-RQYXNWWN.mjs → chunk-G7MGPBSR.mjs} +444 -490
- package/dist/lib/browser/chunk-G7MGPBSR.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +132 -283
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +9 -6
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-QNDUGIS7.mjs → chunk-LD5JEBA6.mjs} +444 -490
- package/dist/lib/node-esm/chunk-LD5JEBA6.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-K734DJB7.mjs → chunk-MK6OVUF6.mjs} +5 -5
- package/dist/lib/node-esm/chunk-MK6OVUF6.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +132 -283
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +9 -6
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +2 -2
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +3 -3
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +71 -7
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +5 -2
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +5 -6
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +7 -5
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -3
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +1 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/index.d.ts +1 -0
- package/dist/types/src/components/TableCellEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +3 -3
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +0 -7
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +2 -2
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts.map +1 -1
- package/dist/types/src/model/table-sorting.d.ts +1 -2
- package/dist/types/src/model/table-sorting.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +3 -17
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +3 -3
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +4 -5
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/schema.d.ts +1 -1
- package/dist/types/src/util/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +51 -51
- package/src/components/Table/DynamicTable.stories.tsx +8 -7
- package/src/components/Table/DynamicTable.tsx +4 -7
- package/src/components/Table/Relations.stories.tsx +119 -11
- package/src/components/Table/Table.stories.tsx +36 -35
- package/src/components/Table/Table.tsx +13 -9
- package/src/components/TableCellEditor/FormCellEditor.tsx +51 -41
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +17 -45
- package/src/components/TableCellEditor/TableCellEditor.tsx +17 -112
- package/src/components/TableCellEditor/index.ts +1 -0
- package/src/components/TableToolbar/TableToolbar.tsx +3 -0
- package/src/components/testing.ts +2 -2
- package/src/hooks/useAddRow.ts +1 -1
- package/src/hooks/useTableModel.ts +1 -1
- package/src/model/modal-controller.ts +0 -28
- package/src/model/table-model.test.ts +16 -6
- package/src/model/table-model.ts +10 -3
- package/src/model/table-presentation.test.ts +19 -17
- package/src/model/table-presentation.ts +4 -5
- package/src/model/table-sorting.ts +15 -6
- package/src/playwright/smoke.spec.ts +3 -3
- package/src/testing/useTestTableModel.ts +7 -4
- package/src/types/schema.ts +4 -4
- package/src/util/dynamic-table.ts +8 -8
- package/src/util/schema.ts +4 -4
- package/dist/lib/browser/chunk-63UUF4IG.mjs.map +0 -7
- package/dist/lib/browser/chunk-RQYXNWWN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K734DJB7.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-QNDUGIS7.mjs.map +0 -7
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +0 -9
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +0 -1
- package/src/components/Table/CreateRefPanel.tsx +0 -103
|
@@ -5,21 +5,12 @@
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
6
|
import { type MutableRefObject, type RefObject } from 'react';
|
|
7
7
|
|
|
8
|
-
import { type Live } from '@dxos/live-object';
|
|
9
|
-
import { log } from '@dxos/log';
|
|
10
|
-
|
|
11
8
|
export type ColumnSettingsMode = { type: 'create' } | { type: 'edit'; fieldId: string };
|
|
12
9
|
|
|
13
10
|
export type ModalState =
|
|
14
11
|
| { type: 'row'; rowIndex: number }
|
|
15
12
|
| { type: 'column'; fieldId: string }
|
|
16
13
|
| { type: 'refPanel'; targetId: string; typename: string }
|
|
17
|
-
| {
|
|
18
|
-
type: 'createRefPanel';
|
|
19
|
-
typename: string;
|
|
20
|
-
initialValues?: Record<string, string>;
|
|
21
|
-
onCreate?: (obj: Live<any>) => void;
|
|
22
|
-
}
|
|
23
14
|
| { type: 'columnSettings'; mode: ColumnSettingsMode }
|
|
24
15
|
| { type: 'closed' };
|
|
25
16
|
|
|
@@ -68,25 +59,6 @@ export class ModalController {
|
|
|
68
59
|
}
|
|
69
60
|
};
|
|
70
61
|
|
|
71
|
-
public openCreateRef = (
|
|
72
|
-
typename: string,
|
|
73
|
-
anchorCell: Element | null,
|
|
74
|
-
initialValues?: Record<string, string>,
|
|
75
|
-
onCreate?: (obj: Live<any>) => void,
|
|
76
|
-
) => {
|
|
77
|
-
if (anchorCell) {
|
|
78
|
-
this._triggerRef.current = anchorCell as HTMLElement;
|
|
79
|
-
this._state.value = {
|
|
80
|
-
type: 'createRefPanel',
|
|
81
|
-
typename,
|
|
82
|
-
initialValues,
|
|
83
|
-
onCreate,
|
|
84
|
-
};
|
|
85
|
-
} else {
|
|
86
|
-
log.warn('anchor cell not found while creating new ref');
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
62
|
public close = () => {
|
|
91
63
|
this._state.value = { type: 'closed' };
|
|
92
64
|
};
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { computed } from '@preact/signals-core';
|
|
6
|
-
import
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
15
|
import { createView } from '@dxos/schema';
|
|
15
16
|
|
|
16
17
|
import { Table } from '../types';
|
|
@@ -24,7 +25,7 @@ registerSignalsRuntime();
|
|
|
24
25
|
|
|
25
26
|
describe('TableModel', () => {
|
|
26
27
|
let updateCount = 0;
|
|
27
|
-
let model:
|
|
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 () => {
|
|
@@ -107,6 +113,10 @@ class Test extends TypedObject({ typename: 'example.com/type/Test', version: '0.
|
|
|
107
113
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
108
114
|
const schema = createEchoSchema(Test);
|
|
109
115
|
const table = Table.make();
|
|
110
|
-
const view = createView({
|
|
116
|
+
const view = createView({
|
|
117
|
+
query: Query.select(Filter.type(schema)),
|
|
118
|
+
jsonSchema: schema.jsonSchema,
|
|
119
|
+
presentation: table,
|
|
120
|
+
});
|
|
111
121
|
return new TableModel({ view, schema: schema.jsonSchema, ...props });
|
|
112
122
|
};
|
package/src/model/table-model.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { type ReadonlySignal, computed, effect, signal } from '@preact/signals-c
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
import { Obj, Ref } from '@dxos/echo';
|
|
9
9
|
import {
|
|
10
|
-
type FieldSortType,
|
|
11
10
|
FormatEnum,
|
|
12
11
|
type JsonProp,
|
|
13
12
|
type JsonSchemaType,
|
|
@@ -15,7 +14,7 @@ import {
|
|
|
15
14
|
getValue,
|
|
16
15
|
setValue,
|
|
17
16
|
toEffectSchema,
|
|
18
|
-
} from '@dxos/echo
|
|
17
|
+
} from '@dxos/echo/internal';
|
|
19
18
|
import { invariant } from '@dxos/invariant';
|
|
20
19
|
import { ObjectId } from '@dxos/keys';
|
|
21
20
|
import { getSnapshot, isLiveObject } from '@dxos/live-object';
|
|
@@ -28,7 +27,14 @@ import {
|
|
|
28
27
|
type DxGridPlaneRange,
|
|
29
28
|
type DxGridPosition,
|
|
30
29
|
} from '@dxos/react-ui-grid';
|
|
31
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
type DataType,
|
|
32
|
+
type FieldSortType,
|
|
33
|
+
ProjectionModel,
|
|
34
|
+
type PropertyType,
|
|
35
|
+
type ValidationError,
|
|
36
|
+
validateSchema,
|
|
37
|
+
} from '@dxos/schema';
|
|
32
38
|
|
|
33
39
|
import { Table } from '../types';
|
|
34
40
|
import { touch } from '../util';
|
|
@@ -135,6 +141,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
135
141
|
super();
|
|
136
142
|
this._view = view;
|
|
137
143
|
this._projection = projection ?? new ProjectionModel(schema, view.projection);
|
|
144
|
+
this._projection.normalizeView();
|
|
138
145
|
|
|
139
146
|
// TODO(ZaymonFC): Use our more robust config merging module?
|
|
140
147
|
this._features = { ...defaultFeatures, ...features };
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
7
7
|
|
|
8
|
-
import {
|
|
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
12
|
import { createView } from '@dxos/schema';
|
|
12
13
|
|
|
13
14
|
import { Table } from '../types';
|
|
@@ -26,8 +27,8 @@ describe('TablePresentation', () => {
|
|
|
26
27
|
updateCount = 0;
|
|
27
28
|
({ data } = live({
|
|
28
29
|
data: [
|
|
29
|
-
{
|
|
30
|
-
{
|
|
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].
|
|
48
|
-
expect(updateCount).toBe(
|
|
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({
|
|
60
|
-
expect(updateCount).toBe(
|
|
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].
|
|
76
|
-
data.push({
|
|
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(
|
|
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
|
-
|
|
93
|
+
count: Schema.Number,
|
|
96
94
|
}) {}
|
|
97
95
|
|
|
98
96
|
const createTableModel = (props: Partial<TableModelProps> = {}): TableModel => {
|
|
99
97
|
const schema = createEchoSchema(Test);
|
|
100
98
|
const table = Table.make();
|
|
101
|
-
const view = createView({
|
|
99
|
+
const view = createView({
|
|
100
|
+
query: Query.select(Filter.type(schema)),
|
|
101
|
+
jsonSchema: schema.jsonSchema,
|
|
102
|
+
presentation: table,
|
|
103
|
+
});
|
|
102
104
|
return new TableModel({ view, schema: schema.jsonSchema, ...props });
|
|
103
105
|
};
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { signal } from '@preact/signals-core';
|
|
6
|
-
import
|
|
6
|
+
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
-
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo
|
|
9
|
+
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
10
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
11
|
import {
|
|
12
12
|
type DxGridCellValue,
|
|
@@ -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
|
})
|
|
@@ -205,7 +205,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
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-
|
|
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
|
|
|
@@ -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 {
|
|
8
|
+
import { FormatEnum, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
9
9
|
import { formatForDisplay } from '@dxos/react-ui-form';
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
DataType,
|
|
12
|
+
FieldSortType,
|
|
13
|
+
FieldType,
|
|
14
|
+
ProjectionModel,
|
|
15
|
+
PropertyType,
|
|
16
|
+
SortDirectionType,
|
|
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.
|
|
@@ -55,7 +64,7 @@ export class TableSorting<T extends TableRow> {
|
|
|
55
64
|
this._rows = rows;
|
|
56
65
|
this._isDirty = computed(() => {
|
|
57
66
|
const local = this._localSort.value;
|
|
58
|
-
const viewSort = this._view.
|
|
67
|
+
const viewSort = this._view.sort?.[0];
|
|
59
68
|
if (local?.type === 'cleared') {
|
|
60
69
|
return viewSort !== undefined;
|
|
61
70
|
}
|
|
@@ -83,7 +92,7 @@ export class TableSorting<T extends TableRow> {
|
|
|
83
92
|
return local.sort;
|
|
84
93
|
}
|
|
85
94
|
|
|
86
|
-
return this._view.
|
|
95
|
+
return this._view.sort?.[0];
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
/**
|
|
@@ -129,9 +138,9 @@ export class TableSorting<T extends TableRow> {
|
|
|
129
138
|
public save(): void {
|
|
130
139
|
if (this._localSort.value !== undefined) {
|
|
131
140
|
if (this._localSort.value.type === 'active') {
|
|
132
|
-
this._view.
|
|
141
|
+
this._view.sort = [this._localSort.value.sort];
|
|
133
142
|
} else {
|
|
134
|
-
this._view.
|
|
143
|
+
this._view.sort = [];
|
|
135
144
|
}
|
|
136
145
|
this._localSort.value = undefined;
|
|
137
146
|
}
|
|
@@ -187,7 +187,7 @@ test.describe('Table', () => {
|
|
|
187
187
|
// Click on the combobox to open options
|
|
188
188
|
await page.getByRole('combobox').click();
|
|
189
189
|
await page.pause();
|
|
190
|
-
await page.getByPlaceholder('Search
|
|
190
|
+
await page.getByPlaceholder('Search…').focus();
|
|
191
191
|
|
|
192
192
|
// Type the first few letters of an org name.
|
|
193
193
|
const orgName =
|
|
@@ -240,7 +240,7 @@ test.describe('Table', () => {
|
|
|
240
240
|
|
|
241
241
|
// Click on the combobox to open options
|
|
242
242
|
await page.getByRole('combobox').click();
|
|
243
|
-
await page.getByPlaceholder('Search
|
|
243
|
+
await page.getByPlaceholder('Search…').focus();
|
|
244
244
|
|
|
245
245
|
// Type the first few letters of an org name.
|
|
246
246
|
const orgName = 'Sally';
|
|
@@ -254,7 +254,7 @@ test.describe('Table', () => {
|
|
|
254
254
|
await page.pause();
|
|
255
255
|
|
|
256
256
|
// Click the save button in the popover
|
|
257
|
-
await page.getByTestId('save-button').click();
|
|
257
|
+
await page.getByTestId('create-referenced-object-form').getByTestId('save-button').click();
|
|
258
258
|
|
|
259
259
|
// Assert that the cell element has the org name
|
|
260
260
|
await expect(targetCell).toHaveText(orgName);
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import { useCallback, useMemo, useRef } from 'react';
|
|
6
6
|
|
|
7
|
-
import { isMutable, toJsonSchema } from '@dxos/echo
|
|
7
|
+
import { isMutable, toJsonSchema } from '@dxos/echo/internal';
|
|
8
8
|
import { useGlobalFilteredObjects } from '@dxos/plugin-search';
|
|
9
9
|
import { faker } from '@dxos/random';
|
|
10
10
|
import { useClient } from '@dxos/react-client';
|
|
11
11
|
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
12
12
|
import { useClientProvider } from '@dxos/react-client/testing';
|
|
13
|
-
import { DataType, ProjectionModel } from '@dxos/schema';
|
|
13
|
+
import { DataType, ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
|
|
14
14
|
|
|
15
15
|
import { type TableController } from '../components';
|
|
16
16
|
import { useAddRow, useTableModel } from '../hooks';
|
|
@@ -28,12 +28,15 @@ export const useTestTableModel = () => {
|
|
|
28
28
|
|
|
29
29
|
const views = useQuery(space, Filter.type(DataType.View));
|
|
30
30
|
const view = useMemo(() => views.at(0), [views]);
|
|
31
|
-
const
|
|
31
|
+
const typename = view?.query ? getTypenameFromQuery(view.query.ast) : undefined;
|
|
32
|
+
const schema = useSchema(client, space, typename);
|
|
32
33
|
const jsonSchema = useMemo(() => (schema ? toJsonSchema(schema) : undefined), [schema]);
|
|
33
34
|
|
|
34
35
|
const projection = useMemo(() => {
|
|
35
36
|
if (schema && view?.projection) {
|
|
36
|
-
|
|
37
|
+
const projection = new ProjectionModel(toJsonSchema(schema), view.projection);
|
|
38
|
+
projection.normalizeView();
|
|
39
|
+
return projection;
|
|
37
40
|
}
|
|
38
41
|
}, [schema, view?.projection]);
|
|
39
42
|
|
package/src/types/schema.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Match from 'effect/Match';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
7
|
|
|
8
8
|
import { Obj, Type } from '@dxos/echo';
|
|
9
|
-
import { JsonPath, type JsonSchemaType, toEffectSchema } from '@dxos/echo
|
|
10
|
-
import { ViewAnnotation } from '@dxos/echo
|
|
9
|
+
import { JsonPath, type JsonSchemaType, toEffectSchema } from '@dxos/echo/internal';
|
|
10
|
+
import { ViewAnnotation } from '@dxos/echo/internal';
|
|
11
11
|
import { type SimpleType } from '@dxos/effect';
|
|
12
12
|
import { type CreateViewFromSpaceProps, type DataType, createViewFromSpace, getSchemaProperties } from '@dxos/schema';
|
|
13
13
|
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import type * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
-
import { Obj } from '@dxos/echo';
|
|
8
|
-
import { getTypename, toJsonSchema } from '@dxos/echo
|
|
9
|
-
import type { JsonSchemaType
|
|
7
|
+
import { Filter, Obj, Query } from '@dxos/echo';
|
|
8
|
+
import { getTypename, toJsonSchema } from '@dxos/echo/internal';
|
|
9
|
+
import type { JsonSchemaType } from '@dxos/echo/internal';
|
|
10
10
|
import {
|
|
11
11
|
type DataType,
|
|
12
12
|
ProjectionModel,
|
|
13
13
|
type SchemaPropertyDefinition,
|
|
14
|
+
type SortDirectionType,
|
|
14
15
|
createView,
|
|
15
16
|
getSchemaFromPropertyDefinitions,
|
|
16
17
|
} from '@dxos/schema';
|
|
@@ -54,23 +55,22 @@ export const getBaseSchema = ({
|
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export const makeDynamicTable = ({
|
|
57
|
-
typename,
|
|
58
58
|
jsonSchema,
|
|
59
59
|
properties,
|
|
60
60
|
}: {
|
|
61
|
-
typename: string;
|
|
62
61
|
jsonSchema: JsonSchemaType;
|
|
63
62
|
properties?: TablePropertyDefinition[];
|
|
64
63
|
}): { projection: ProjectionModel; view: DataType.View } => {
|
|
65
64
|
const table = Obj.make(Table.Table, { sizes: {} });
|
|
66
65
|
const view = createView({
|
|
67
|
-
|
|
66
|
+
query: Query.select(Filter.everything()),
|
|
68
67
|
jsonSchema,
|
|
69
68
|
presentation: table,
|
|
70
69
|
...(properties && { fields: properties.map((property) => property.name) }),
|
|
71
70
|
});
|
|
72
71
|
|
|
73
72
|
const projection = new ProjectionModel(jsonSchema, view.projection);
|
|
73
|
+
projection.normalizeView();
|
|
74
74
|
if (properties && projection.fields) {
|
|
75
75
|
setProperties(view, projection, table, properties);
|
|
76
76
|
}
|
|
@@ -101,7 +101,7 @@ const setProperties = (
|
|
|
101
101
|
|
|
102
102
|
if (property.sort) {
|
|
103
103
|
const fieldId = field.id;
|
|
104
|
-
view.
|
|
104
|
+
view.sort = [{ fieldId, direction: property.sort }];
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
package/src/util/schema.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates a narrowed schema from an original schema that only includes
|
|
@@ -20,14 +20,14 @@ export const narrowSchema = <S extends Schema.Schema.AnyNoContext>(
|
|
|
20
20
|
): Schema.Schema<unknown, unknown> | undefined => {
|
|
21
21
|
const ast = (schema as any)?.ast;
|
|
22
22
|
|
|
23
|
-
if (isTypeLiteral(ast)) {
|
|
23
|
+
if (SchemaAST.isTypeLiteral(ast)) {
|
|
24
24
|
// Filter property signatures that match any of the provided paths
|
|
25
25
|
const propertySignatures = ast.propertySignatures.filter((signature) => paths.includes(signature.name.toString()));
|
|
26
26
|
|
|
27
27
|
// If we found at least one matching property
|
|
28
28
|
if (propertySignatures.length > 0) {
|
|
29
29
|
// Create a new TypeLiteral with only the matching properties
|
|
30
|
-
const narrowType = new TypeLiteral(propertySignatures, []);
|
|
30
|
+
const narrowType = new SchemaAST.TypeLiteral(propertySignatures, []);
|
|
31
31
|
return Schema.make(narrowType);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/types/schema.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Schema } from 'effect';\nimport { Match } from 'effect';\n\nimport { Obj, Type } from '@dxos/echo';\nimport { JsonPath, type JsonSchemaType, toEffectSchema } from '@dxos/echo-schema';\nimport { ViewAnnotation } from '@dxos/echo-schema';\nimport { type SimpleType } from '@dxos/effect';\nimport { type CreateViewFromSpaceProps, type DataType, createViewFromSpace, getSchemaProperties } from '@dxos/schema';\n\nexport const Table = Schema.Struct({\n sizes: Schema.Record({\n key: JsonPath,\n value: Schema.Number,\n }).pipe(Schema.mutable),\n}).pipe(\n Type.Obj({\n typename: 'dxos.org/type/Table',\n version: '0.1.0',\n }),\n ViewAnnotation.set(true),\n);\n\nexport type Table = Schema.Schema.Type<typeof Table>;\n\n/**\n * Make a table view.\n */\nexport const make = (props: Partial<Obj.MakeProps<typeof Table>> = {}) => Obj.make(Table, { sizes: {}, ...props });\n\ntype MakeViewProps = Omit<CreateViewFromSpaceProps, 'presentation'> & {\n sizes?: Record<string, number>;\n};\n\nexport const makeView = async ({\n sizes,\n ...props\n}: MakeViewProps): Promise<{\n jsonSchema: JsonSchemaType;\n view: DataType.View;\n schema: ReturnType<typeof toEffectSchema>;\n}> => {\n const table = Obj.make(Table, { sizes: {} });\n const { jsonSchema, view } = await createViewFromSpace({ ...props, presentation: table });\n\n // Preset sizes.\n const schema = toEffectSchema(jsonSchema);\n const shouldIncludeId = props.fields?.find((field) => field === 'id') !== undefined;\n const properties = getSchemaProperties(schema.ast, {}, shouldIncludeId);\n for (const property of properties) {\n if (sizes?.[property.name]) {\n table.sizes[property.name as JsonPath] = sizes[property.name];\n continue;\n }\n\n Match.type<SimpleType>().pipe(\n Match.when('boolean', () => {\n table.sizes[property.name as JsonPath] = 100;\n }),\n Match.when('number', () => {\n table.sizes[property.name as JsonPath] = 100;\n }),\n Match.orElse(() => {\n // Noop.\n }),\n )(property.type);\n }\n\n return { jsonSchema, schema, view };\n};\n"],
|
|
5
|
-
"mappings": ";;;;;;;AAAA;;;;;;AAIA,SAASA,cAAc;AACvB,SAASC,aAAa;AAEtB,SAASC,KAAKC,YAAY;AAC1B,SAASC,UAA+BC,sBAAsB;AAC9D,SAASC,sBAAsB;AAE/B,SAAuDC,qBAAqBC,2BAA2B;AAEhG,IAAMC,QAAQC,OAAOC,OAAO;EACjCC,OAAOF,OAAOG,OAAO;IACnBC,KAAKC;IACLC,OAAON,OAAOO;EAChB,CAAA,EAAGC,KAAKR,OAAOS,OAAO;AACxB,CAAA,EAAGD,KACDE,KAAKC,IAAI;EACPC,UAAU;EACVC,SAAS;AACX,CAAA,GACAC,eAAeC,IAAI,IAAA,CAAA;AAQd,IAAMC,OAAO,CAACC,QAA8C,CAAC,MAAMN,IAAIK,KAAKjB,OAAO;EAAEG,OAAO,CAAC;EAAG,GAAGe;AAAM,CAAA;AAMzG,IAAMC,WAAW,OAAO,EAC7BhB,OACA,GAAGe,MAAAA,MACW;AAKd,QAAME,QAAQR,IAAIK,KAAKjB,OAAO;IAAEG,OAAO,CAAC;EAAE,CAAA;AAC1C,QAAM,EAAEkB,YAAYC,KAAI,IAAK,MAAMC,oBAAoB;IAAE,GAAGL;IAAOM,cAAcJ;EAAM,CAAA;AAGvF,QAAMK,SAASC,eAAeL,UAAAA;AAC9B,QAAMM,kBAAkBT,MAAMU,QAAQC,KAAK,CAACC,UAAUA,UAAU,IAAA,MAAUC;AAC1E,QAAMC,aAAaC,oBAAoBR,OAAOS,KAAK,CAAC,GAAGP,eAAAA;AACvD,aAAWQ,YAAYH,YAAY;AACjC,QAAI7B,QAAQgC,SAASC,IAAI,GAAG;AAC1BhB,YAAMjB,MAAMgC,SAASC,IAAI,IAAgBjC,MAAMgC,SAASC,IAAI;AAC5D;IACF;AAEAC,UAAMC,KAAI,EAAe7B,KACvB4B,MAAME,KAAK,WAAW,MAAA;AACpBnB,YAAMjB,MAAMgC,SAASC,IAAI,IAAgB;IAC3C,CAAA,GACAC,MAAME,KAAK,UAAU,MAAA;AACnBnB,YAAMjB,MAAMgC,SAASC,IAAI,IAAgB;IAC3C,CAAA,GACAC,MAAMG,OAAO,MAAA;IAEb,CAAA,CAAA,EACAL,SAASG,IAAI;EACjB;AAEA,SAAO;IAAEjB;IAAYI;IAAQH;EAAK;AACpC;",
|
|
6
|
-
"names": ["Schema", "Match", "Obj", "Type", "JsonPath", "toEffectSchema", "ViewAnnotation", "createViewFromSpace", "getSchemaProperties", "Table", "Schema", "Struct", "sizes", "Record", "key", "JsonPath", "value", "Number", "pipe", "mutable", "Type", "Obj", "typename", "version", "ViewAnnotation", "set", "make", "props", "makeView", "table", "jsonSchema", "view", "createViewFromSpace", "presentation", "schema", "toEffectSchema", "shouldIncludeId", "fields", "find", "field", "undefined", "properties", "getSchemaProperties", "ast", "property", "name", "Match", "type", "when", "orElse"]
|
|
7
|
-
}
|