@dxos/react-ui-table 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6
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-OZBLY7XM.mjs → chunk-BF54SSNL.mjs} +12 -18
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-I5IFW7UF.mjs → chunk-EK6D2CVM.mjs} +37 -32
- package/dist/lib/browser/chunk-EK6D2CVM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +200 -173
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +4 -4
- package/dist/lib/browser/testing/index.mjs.map +2 -2
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-DDJKYJFY.mjs → chunk-LYYDF6JP.mjs} +37 -32
- package/dist/lib/node-esm/chunk-LYYDF6JP.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-WNZSKIRF.mjs → chunk-SIRDIPHZ.mjs} +12 -18
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +200 -173
- 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 +4 -4
- package/dist/lib/node-esm/testing/index.mjs.map +2 -2
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +5 -4
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +20 -22
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +80 -82
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +28 -21
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +82 -84
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/TableContent.d.ts +21 -0
- package/dist/types/src/components/Table/TableContent.d.ts.map +1 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts +25 -0
- package/dist/types/src/components/Table/TableToolbar.d.ts.map +1 -0
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +3 -3
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +2 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +22 -24
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.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/useProjectionModel.d.ts +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +1 -1
- package/dist/types/src/model/table-model.d.ts +5 -4
- 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/playwright/TableManager.d.ts +2 -1
- package/dist/types/src/playwright/TableManager.d.ts.map +1 -1
- package/dist/types/src/testing/useTestTableModel.d.ts +2 -2
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +19 -21
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +1 -6
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +6 -5
- package/dist/types/src/util/dynamic-table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.test.d.ts +2 -0
- package/dist/types/src/util/dynamic-table.test.d.ts.map +1 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -43
- package/src/components/Table/ColumnActionsMenu.tsx +5 -5
- package/src/components/Table/ColumnSettings.tsx +3 -4
- package/src/components/Table/DynamicTable.stories.tsx +6 -5
- package/src/components/Table/DynamicTable.tsx +13 -19
- package/src/components/Table/Relations.stories.tsx +35 -25
- package/src/components/Table/Table.stories.tsx +61 -40
- package/src/components/Table/Table.tsx +41 -434
- package/src/components/Table/TableContent.tsx +422 -0
- package/src/components/{TableToolbar → Table}/TableToolbar.tsx +35 -42
- package/src/components/TableCellEditor/CellValidationMessage.tsx +4 -4
- package/src/components/TableCellEditor/FormCellEditor.tsx +12 -6
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +6 -5
- package/src/components/TableCellEditor/TableCellEditor.tsx +10 -5
- package/src/components/index.ts +0 -1
- package/src/components/testing.ts +1 -1
- package/src/hooks/useAddRow.ts +1 -1
- package/src/hooks/useProjectionModel.ts +3 -3
- package/src/hooks/useTableModel.ts +1 -1
- package/src/model/table-model.test.ts +5 -5
- package/src/model/table-model.ts +13 -12
- package/src/model/table-presentation.test.ts +5 -5
- package/src/model/table-presentation.ts +12 -11
- package/src/playwright/TableManager.ts +10 -3
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +45 -2
- package/src/testing/useTestTableModel.ts +4 -4
- package/src/translations.ts +19 -21
- package/src/types/Table.ts +11 -22
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +17 -16
- package/src/util/query-ast.ts +3 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-I5IFW7UF.mjs.map +0 -7
- package/dist/lib/browser/chunk-OZBLY7XM.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DDJKYJFY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-WNZSKIRF.mjs.map +0 -7
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +0 -21
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +0 -1
- package/dist/types/src/components/TableToolbar/index.d.ts +0 -2
- package/dist/types/src/components/TableToolbar/index.d.ts.map +0 -1
- package/src/components/TableToolbar/index.ts +0 -5
|
@@ -29,9 +29,9 @@ import { type ModalController, type TableModel } from '../../model';
|
|
|
29
29
|
import { CellValidationMessage } from './CellValidationMessage';
|
|
30
30
|
import { FormCellEditor, type OnCreateHandler } from './FormCellEditor';
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const documentSlots = {
|
|
33
33
|
scroll: {
|
|
34
|
-
className: '!
|
|
34
|
+
className: '!py-(--dx-grid-cell-editor-padding-block)',
|
|
35
35
|
},
|
|
36
36
|
};
|
|
37
37
|
|
|
@@ -42,7 +42,7 @@ export type QueryResult = Pick<Completion, 'label'> & {
|
|
|
42
42
|
data: any;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
export type TableCellEditorProps<T extends Type.
|
|
45
|
+
export type TableCellEditorProps<T extends Type.AnyEntity = Type.AnyEntity> = {
|
|
46
46
|
schema?: T;
|
|
47
47
|
model?: TableModel;
|
|
48
48
|
modals?: ModalController;
|
|
@@ -51,7 +51,7 @@ export type TableCellEditorProps<T extends Type.Entity.Any = Type.Entity.Any> =
|
|
|
51
51
|
onSave?: () => void;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
export const TableValueEditor = <T extends Type.
|
|
54
|
+
export const TableValueEditor = <T extends Type.AnyEntity = Type.AnyEntity>({
|
|
55
55
|
__gridScope,
|
|
56
56
|
schema,
|
|
57
57
|
model,
|
|
@@ -250,7 +250,12 @@ export const TableCellEditor = ({
|
|
|
250
250
|
return (
|
|
251
251
|
<>
|
|
252
252
|
<CellValidationMessage validationError={validationError} variant={validationVariant} __gridScope={__gridScope} />
|
|
253
|
-
<GridCellEditor
|
|
253
|
+
<GridCellEditor
|
|
254
|
+
extensions={extensions}
|
|
255
|
+
getCellContent={getCellContent}
|
|
256
|
+
onBlur={handleBlur}
|
|
257
|
+
slots={documentSlots}
|
|
258
|
+
/>
|
|
254
259
|
</>
|
|
255
260
|
);
|
|
256
261
|
};
|
package/src/components/index.ts
CHANGED
package/src/hooks/useAddRow.ts
CHANGED
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
import { type Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import { useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import { Type } from '@dxos/echo';
|
|
8
|
+
import { JsonSchema, Type } from '@dxos/echo';
|
|
9
9
|
import { useAsyncEffect } from '@dxos/react-ui';
|
|
10
10
|
import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
|
|
11
11
|
|
|
12
12
|
import { type Table } from '../types';
|
|
13
13
|
|
|
14
|
-
export const useProjectionModel = <S extends Type.
|
|
14
|
+
export const useProjectionModel = <S extends Type.AnyEntity>(
|
|
15
15
|
schema: S | undefined,
|
|
16
16
|
table: Table.Table | undefined,
|
|
17
17
|
registry: Registry.Registry,
|
|
@@ -23,7 +23,7 @@ export const useProjectionModel = <S extends Type.Entity.Any>(
|
|
|
23
23
|
const view = await table.view.load();
|
|
24
24
|
// For mutable schemas (EchoSchema), use the live jsonSchema reference for reactivity.
|
|
25
25
|
// For immutable schemas, create a snapshot.
|
|
26
|
-
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema :
|
|
26
|
+
const jsonSchema = Type.isMutable(schema) ? schema.jsonSchema : JsonSchema.toJsonSchema(schema);
|
|
27
27
|
|
|
28
28
|
// Always use createEchoChangeCallback since the view is ECHO-backed.
|
|
29
29
|
// Pass schema only when mutable to allow schema mutations.
|
|
@@ -29,7 +29,7 @@ export type UseTableModelProps<T extends TableRow = TableRow> = {
|
|
|
29
29
|
onRowAction?: (actionId: string, data: T) => void;
|
|
30
30
|
} & Pick<
|
|
31
31
|
TableModelProps<T>,
|
|
32
|
-
'features' | 'onInsertRow' | 'onDeleteRows' | '
|
|
32
|
+
'features' | 'onInsertRow' | 'onDeleteRows' | 'onColumnDelete' | 'onCellUpdate' | 'onRowOrderChange'
|
|
33
33
|
>;
|
|
34
34
|
|
|
35
35
|
export const useTableModel = <T extends TableRow = TableRow>({
|
|
@@ -6,9 +6,9 @@ import { Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Filter, Query, Type } from '@dxos/echo';
|
|
9
|
+
import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import { ProjectionModel,
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { Table } from '../types';
|
|
14
14
|
|
|
@@ -121,14 +121,14 @@ const Test = Schema.Struct({
|
|
|
121
121
|
completed: Schema.Boolean,
|
|
122
122
|
}).pipe(
|
|
123
123
|
Type.object({
|
|
124
|
-
typename: 'example.
|
|
124
|
+
typename: 'com.example.type.test',
|
|
125
125
|
version: '0.1.0',
|
|
126
126
|
}),
|
|
127
127
|
);
|
|
128
128
|
|
|
129
129
|
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
130
130
|
const schema = createEchoSchema(Test);
|
|
131
|
-
const view =
|
|
131
|
+
const view = ViewModel.make({
|
|
132
132
|
query: Query.select(Filter.type(schema)),
|
|
133
133
|
jsonSchema: schema.jsonSchema,
|
|
134
134
|
});
|
|
@@ -137,7 +137,7 @@ const createTableModel = (registry: Registry.Registry, props: Partial<TableModel
|
|
|
137
137
|
registry,
|
|
138
138
|
view,
|
|
139
139
|
baseSchema: schema.jsonSchema,
|
|
140
|
-
change: createDirectChangeCallback(view.projection, schema
|
|
140
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
|
|
141
141
|
});
|
|
142
142
|
projection.normalizeView();
|
|
143
143
|
return new TableModel({
|
package/src/model/table-model.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
|
|
7
7
|
import { Resource } from '@dxos/context';
|
|
8
8
|
import { type Database, Format, Obj, Order, Query, type QueryAST, Ref } from '@dxos/echo';
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
9
10
|
import { type JsonProp, type JsonSchemaType, type Mutable, toEffectSchema } from '@dxos/echo/internal';
|
|
10
11
|
import { getSnapshot } from '@dxos/echo/internal';
|
|
11
12
|
import { getValue, setValue } from '@dxos/effect';
|
|
@@ -19,7 +20,7 @@ import {
|
|
|
19
20
|
type DxGridPlaneRange,
|
|
20
21
|
type DxGridPosition,
|
|
21
22
|
} from '@dxos/react-ui-grid';
|
|
22
|
-
import { type ProjectionModel, type PropertyType, type ValidationError,
|
|
23
|
+
import { type ProjectionModel, type PropertyType, type ValidationError, validateSchema } from '@dxos/schema';
|
|
23
24
|
|
|
24
25
|
import { type Table } from '../types';
|
|
25
26
|
import { extractOrder } from '../util';
|
|
@@ -122,7 +123,7 @@ export type TableModelProps<T extends TableRow = TableRow> = {
|
|
|
122
123
|
onResolveSchema?: (typename: string) => Promise<JsonSchemaType>;
|
|
123
124
|
onInsertRow?: (data?: any) => InsertRowResult;
|
|
124
125
|
onDeleteRows?: (index: number, obj: T[]) => void;
|
|
125
|
-
|
|
126
|
+
onColumnDelete?: (fieldId: string) => void;
|
|
126
127
|
onCellUpdate?: (cell: DxGridPosition) => void;
|
|
127
128
|
onRowOrderChange?: () => void;
|
|
128
129
|
onRowAction?: (actionId: string, data: T) => void;
|
|
@@ -139,7 +140,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
139
140
|
|
|
140
141
|
private readonly _onInsertRow?: (data?: any) => InsertRowResult;
|
|
141
142
|
private readonly _onDeleteRows?: TableModelProps<T>['onDeleteRows'];
|
|
142
|
-
private readonly
|
|
143
|
+
private readonly _onColumnDelete?: TableModelProps<T>['onColumnDelete'];
|
|
143
144
|
private readonly _onCellUpdate?: TableModelProps<T>['onCellUpdate'];
|
|
144
145
|
private readonly _onRowOrderChange?: TableModelProps<T>['onRowOrderChange'];
|
|
145
146
|
private readonly _onRowAction?: TableModelProps<T>['onRowAction'];
|
|
@@ -176,7 +177,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
176
177
|
pinnedRows = { top: [], bottom: [] },
|
|
177
178
|
rowActions = [],
|
|
178
179
|
onCellUpdate,
|
|
179
|
-
|
|
180
|
+
onColumnDelete,
|
|
180
181
|
onDeleteRows,
|
|
181
182
|
onInsertRow,
|
|
182
183
|
onRowOrderChange,
|
|
@@ -292,7 +293,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
292
293
|
this._rowActions = rowActions;
|
|
293
294
|
this._onInsertRow = onInsertRow;
|
|
294
295
|
this._onDeleteRows = onDeleteRows;
|
|
295
|
-
this.
|
|
296
|
+
this._onColumnDelete = onColumnDelete;
|
|
296
297
|
this._onCellUpdate = onCellUpdate;
|
|
297
298
|
this._onRowOrderChange = onRowOrderChange;
|
|
298
299
|
this._onRowAction = onRowAction;
|
|
@@ -691,7 +692,7 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
691
692
|
const currentRow = this._registry.get(this._rows)[row];
|
|
692
693
|
invariant(currentRow, 'Invalid row index');
|
|
693
694
|
|
|
694
|
-
const snapshot = getSnapshot(currentRow);
|
|
695
|
+
const snapshot = { ...getSnapshot(currentRow) };
|
|
695
696
|
setValue(snapshot, field.path, transformedValue);
|
|
696
697
|
|
|
697
698
|
const schema = Obj.getSchema(currentRow);
|
|
@@ -796,8 +797,8 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
796
797
|
}
|
|
797
798
|
|
|
798
799
|
const field = this._projection?.getFields().find((field) => field.id === fieldId);
|
|
799
|
-
if (field && this.
|
|
800
|
-
this.
|
|
800
|
+
if (field && this._onColumnDelete) {
|
|
801
|
+
this._onColumnDelete(field.id);
|
|
801
802
|
}
|
|
802
803
|
}
|
|
803
804
|
|
|
@@ -893,14 +894,14 @@ export class TableModel<T extends TableRow = TableRow> extends Resource {
|
|
|
893
894
|
if (field) {
|
|
894
895
|
// Persist sort to view.query.ast
|
|
895
896
|
const newQuery = baseQuery.orderBy(Order.property<any>(field.path as string, inMemorySort.direction));
|
|
896
|
-
Obj.change(view, (
|
|
897
|
-
|
|
897
|
+
Obj.change(view, (obj) => {
|
|
898
|
+
obj.query.ast = newQuery.ast as Mutable<typeof newQuery.ast>;
|
|
898
899
|
});
|
|
899
900
|
}
|
|
900
901
|
} else {
|
|
901
902
|
// Clear sort from view.query.ast
|
|
902
|
-
Obj.change(view, (
|
|
903
|
-
|
|
903
|
+
Obj.change(view, (obj) => {
|
|
904
|
+
obj.query.ast = baseQuery.ast as Mutable<typeof baseQuery.ast>;
|
|
904
905
|
});
|
|
905
906
|
}
|
|
906
907
|
|
|
@@ -6,9 +6,9 @@ import { Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
8
8
|
|
|
9
|
-
import { Filter, Query, Type } from '@dxos/echo';
|
|
9
|
+
import { Filter, JsonSchema, Query, Type } from '@dxos/echo';
|
|
10
10
|
import { createEchoSchema } from '@dxos/echo/testing';
|
|
11
|
-
import { ProjectionModel,
|
|
11
|
+
import { ProjectionModel, ViewModel, createDirectChangeCallback } from '@dxos/schema';
|
|
12
12
|
|
|
13
13
|
import { Table } from '../types';
|
|
14
14
|
|
|
@@ -92,14 +92,14 @@ const Test = Schema.Struct({
|
|
|
92
92
|
count: Schema.Number,
|
|
93
93
|
}).pipe(
|
|
94
94
|
Type.object({
|
|
95
|
-
typename: 'example.
|
|
95
|
+
typename: 'com.example.type.test',
|
|
96
96
|
version: '0.1.0',
|
|
97
97
|
}),
|
|
98
98
|
);
|
|
99
99
|
|
|
100
100
|
const createTableModel = (registry: Registry.Registry, props: Partial<TableModelProps> = {}): TableModel => {
|
|
101
101
|
const schema = createEchoSchema(Test);
|
|
102
|
-
const view =
|
|
102
|
+
const view = ViewModel.make({
|
|
103
103
|
query: Query.select(Filter.type(schema)),
|
|
104
104
|
jsonSchema: schema.jsonSchema,
|
|
105
105
|
});
|
|
@@ -108,7 +108,7 @@ const createTableModel = (registry: Registry.Registry, props: Partial<TableModel
|
|
|
108
108
|
registry,
|
|
109
109
|
view,
|
|
110
110
|
baseSchema: schema.jsonSchema,
|
|
111
|
-
change: createDirectChangeCallback(view.projection, schema
|
|
111
|
+
change: createDirectChangeCallback(view.projection, JsonSchema.toJsonSchema(schema)),
|
|
112
112
|
});
|
|
113
113
|
projection.normalizeView();
|
|
114
114
|
return new TableModel({ registry, object, projection, ...props });
|
|
@@ -6,6 +6,7 @@ import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
|
6
6
|
import * as Predicate from 'effect/Predicate';
|
|
7
7
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
|
+
import { type View } from '@dxos/echo';
|
|
9
10
|
import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
10
11
|
import { cellClassesForFieldType, formatForDisplay } from '@dxos/react-ui-form';
|
|
11
12
|
import {
|
|
@@ -15,7 +16,7 @@ import {
|
|
|
15
16
|
type DxGridPlaneRange,
|
|
16
17
|
toPlaneCellIndex,
|
|
17
18
|
} from '@dxos/react-ui-grid';
|
|
18
|
-
import {
|
|
19
|
+
import { VIEW_FIELD_LIMIT } from '@dxos/schema';
|
|
19
20
|
import { mx } from '@dxos/ui-theme';
|
|
20
21
|
|
|
21
22
|
import { tableButtons, tableControls } from '../util';
|
|
@@ -107,7 +108,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
107
108
|
private createDataCell(
|
|
108
109
|
cells: DxGridPlaneCells,
|
|
109
110
|
obj: T,
|
|
110
|
-
field: FieldType,
|
|
111
|
+
field: View.FieldType,
|
|
111
112
|
colIndex: number,
|
|
112
113
|
displayIndex: number,
|
|
113
114
|
): void {
|
|
@@ -210,7 +211,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
210
211
|
const targetObj = getValue(obj, field.path)?.target;
|
|
211
212
|
if (targetObj) {
|
|
212
213
|
const dxn = Obj.getDXN(targetObj)?.toString();
|
|
213
|
-
cell.accessoryHtml = `<div role="none" class="absolute
|
|
214
|
+
cell.accessoryHtml = `<div role="none" class="absolute end-0 inset-y-0 p-(--dx-grid-cell-content-padding-block)"><dx-anchor dxn=${dxn} class="dx-button w-6 aspect-square min-h-0" data-dx-grid-action="accessory"><dx-icon icon="ph--link-simple--regular"/></dx-anchor></div>`;
|
|
214
215
|
}
|
|
215
216
|
}
|
|
216
217
|
|
|
@@ -335,11 +336,11 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
335
336
|
value: '',
|
|
336
337
|
resizeHandle: 'col',
|
|
337
338
|
accessoryHtml: `
|
|
338
|
-
<span class="grow min-
|
|
339
|
+
<span class="grow min-w-0 truncate">${props.title ?? field.path}</span>
|
|
339
340
|
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
340
341
|
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
341
342
|
`,
|
|
342
|
-
className: '!bg-
|
|
343
|
+
className: '!bg-toolbar-surface text-description! [&>div]:flex [&>div]:items-stretch',
|
|
343
344
|
};
|
|
344
345
|
}
|
|
345
346
|
|
|
@@ -382,7 +383,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
382
383
|
if (!this.model.features.selection.enabled || this.model.selection.selectionMode === 'single') {
|
|
383
384
|
return {
|
|
384
385
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
385
|
-
className: '!bg-
|
|
386
|
+
className: '!bg-toolbar-surface',
|
|
386
387
|
readonly: true,
|
|
387
388
|
value: '',
|
|
388
389
|
},
|
|
@@ -396,7 +397,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
396
397
|
header: true,
|
|
397
398
|
checked: this.model.selection.allRowsSelected,
|
|
398
399
|
}),
|
|
399
|
-
className: '!bg-
|
|
400
|
+
className: '!bg-toolbar-surface',
|
|
400
401
|
readonly: true,
|
|
401
402
|
value: '',
|
|
402
403
|
},
|
|
@@ -411,7 +412,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
411
412
|
disabled: (this.model.projection?.getFields()?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
412
413
|
})
|
|
413
414
|
: undefined,
|
|
414
|
-
className: '!bg-
|
|
415
|
+
className: '!bg-toolbar-surface',
|
|
415
416
|
readonly: true,
|
|
416
417
|
value: '',
|
|
417
418
|
},
|
|
@@ -465,7 +466,7 @@ export class TablePresentation<T extends TableRow = TableRow> {
|
|
|
465
466
|
export const cellClassesForRowSelection = (selected: boolean, selectionMode: SelectionMode) => {
|
|
466
467
|
if (!selected) {
|
|
467
468
|
if (selectionMode === 'single') {
|
|
468
|
-
return ['
|
|
469
|
+
return ['cursor-pointer!'];
|
|
469
470
|
} else {
|
|
470
471
|
return undefined;
|
|
471
472
|
}
|
|
@@ -473,8 +474,8 @@ export const cellClassesForRowSelection = (selected: boolean, selectionMode: Sel
|
|
|
473
474
|
|
|
474
475
|
switch (selectionMode) {
|
|
475
476
|
case 'single':
|
|
476
|
-
return ['
|
|
477
|
+
return ['dx-grid__cell--no-focus-unfurl !bg-current-surface hover:bg-hover-surface cursor-pointer!'];
|
|
477
478
|
case 'multiple':
|
|
478
|
-
return ['!bg-
|
|
479
|
+
return ['!bg-grid-cell-selected'];
|
|
479
480
|
}
|
|
480
481
|
};
|
|
@@ -17,6 +17,7 @@ const TABLE_SELECTORS = {
|
|
|
17
17
|
},
|
|
18
18
|
columnDelete: 'column-delete',
|
|
19
19
|
rowDelete: 'row-menu-delete',
|
|
20
|
+
addRow: 'table.toolbar.add-row',
|
|
20
21
|
} as const;
|
|
21
22
|
|
|
22
23
|
export class TableManager {
|
|
@@ -53,10 +54,16 @@ export class TableManager {
|
|
|
53
54
|
await this.page.getByTestId(TABLE_SELECTORS.columnSort[direction]).click();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
public async
|
|
57
|
+
public async addRow(): Promise<void> {
|
|
58
|
+
await this.page.getByTestId(TABLE_SELECTORS.addRow).click();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async addColumn({ label, format }: { label: string; format?: string }): Promise<void> {
|
|
57
62
|
await this.page.getByTestId(TABLE_SELECTORS.newColumnButton).click();
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
if (format) {
|
|
64
|
+
await this.page.getByRole('combobox').click();
|
|
65
|
+
await this.page.getByLabel(format).click();
|
|
66
|
+
}
|
|
60
67
|
await this.page.getByPlaceholder('Property label').click();
|
|
61
68
|
await this.page.getByPlaceholder('Property label').fill(label);
|
|
62
69
|
await this.page.getByRole('button', { name: 'Save' }).click();
|
|
@@ -10,7 +10,7 @@ export default defineConfig({
|
|
|
10
10
|
...e2ePreset(import.meta.dirname),
|
|
11
11
|
// TODO(wittjosiah): Avoid hard-coding ports.
|
|
12
12
|
webServer: {
|
|
13
|
-
command: '
|
|
13
|
+
command: 'pnpm storybook dev --ci --quiet --port=9004 --config-dir=.storybook',
|
|
14
14
|
port: 9004,
|
|
15
15
|
reuseExistingServer: false,
|
|
16
16
|
},
|
|
@@ -121,8 +121,22 @@ test.describe('Table', () => {
|
|
|
121
121
|
await page.close();
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
test('add column without changing format', async ({ browser, browserName }) => {
|
|
125
|
+
test.skip(browserName === 'webkit');
|
|
126
|
+
test.skip(browserName === 'firefox');
|
|
127
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
128
|
+
const table = new TableManager(page);
|
|
129
|
+
|
|
130
|
+
await table.grid.ready();
|
|
131
|
+
const newColumnLabel = 'DEFAULT FORMAT COL';
|
|
132
|
+
|
|
133
|
+
await table.addColumn({ label: newColumnLabel });
|
|
134
|
+
|
|
135
|
+
await expect(page.getByRole('gridcell', { name: newColumnLabel })).toBeVisible();
|
|
136
|
+
await page.close();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('add column with format', async ({ browser, browserName }) => {
|
|
126
140
|
test.skip(browserName === 'webkit');
|
|
127
141
|
test.skip(browserName === 'firefox');
|
|
128
142
|
const { page } = await setupPage(browser, { url: storyUrl });
|
|
@@ -160,6 +174,35 @@ test.describe('Table', () => {
|
|
|
160
174
|
await page.close();
|
|
161
175
|
});
|
|
162
176
|
|
|
177
|
+
test('add row and edit cell', async ({ browser, browserName }) => {
|
|
178
|
+
test.skip(browserName === 'webkit');
|
|
179
|
+
test.skip(browserName === 'firefox');
|
|
180
|
+
const { page } = await setupPage(browser, { url: storyUrl });
|
|
181
|
+
const table = new TableManager(page);
|
|
182
|
+
|
|
183
|
+
await table.grid.ready();
|
|
184
|
+
|
|
185
|
+
// Click the CTA row at the bottom to add a new row.
|
|
186
|
+
await table.grid.cell(0, 0, 'frozenRowsEnd').click();
|
|
187
|
+
|
|
188
|
+
// All schema fields are optional so the row is added directly to the grid (not as a draft).
|
|
189
|
+
const newCell = table.grid.cell(0, 10, 'grid');
|
|
190
|
+
await newCell.waitFor({ state: 'visible' });
|
|
191
|
+
|
|
192
|
+
// Focus should already be on the new row; engage edit mode.
|
|
193
|
+
await page.keyboard.press('Enter');
|
|
194
|
+
await page.getByTestId('grid.cell-editor').waitFor({ state: 'visible' });
|
|
195
|
+
|
|
196
|
+
// Type text and confirm.
|
|
197
|
+
const cellContent = 'New row content';
|
|
198
|
+
await page.keyboard.type(cellContent);
|
|
199
|
+
await page.keyboard.press('Enter');
|
|
200
|
+
|
|
201
|
+
// Assert the cell content is saved.
|
|
202
|
+
await expect(newCell).toHaveText(cellContent);
|
|
203
|
+
await page.close();
|
|
204
|
+
});
|
|
205
|
+
|
|
163
206
|
// TODO(wittjosiah): Remove? Conflicts with story play function which is run as a unit test anyways.
|
|
164
207
|
test.skip('extant relations work as expected', async ({ browser, browserName }) => {
|
|
165
208
|
test.skip(browserName === 'webkit');
|
|
@@ -10,7 +10,7 @@ import { isMutable } from '@dxos/echo/internal';
|
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
11
|
import { Filter, useQuery, useSchema } from '@dxos/react-client/echo';
|
|
12
12
|
import { useClientStory } from '@dxos/react-client/testing';
|
|
13
|
-
import { useGlobalFilteredObjects } from '@dxos/react-ui-
|
|
13
|
+
import { useGlobalFilteredObjects } from '@dxos/react-ui-search';
|
|
14
14
|
import { type ProjectionModel, getTypenameFromQuery } from '@dxos/schema';
|
|
15
15
|
|
|
16
16
|
import { type TableController } from '../components';
|
|
@@ -20,7 +20,7 @@ import { Table } from '../types';
|
|
|
20
20
|
|
|
21
21
|
faker.seed(0); // NOTE(ZaymonFC): Required for smoke tests.
|
|
22
22
|
|
|
23
|
-
export type TestTableModel<T extends Type.
|
|
23
|
+
export type TestTableModel<T extends Type.AnyObj = Type.AnyObj> = {
|
|
24
24
|
schema: T | undefined;
|
|
25
25
|
table: Table.Table | undefined;
|
|
26
26
|
projection: ProjectionModel | undefined;
|
|
@@ -38,7 +38,7 @@ export type TestTableModel<T extends Type.Obj.Any = Type.Obj.Any> = {
|
|
|
38
38
|
* Custom hook to create and manage a test table model for storybook demonstrations.
|
|
39
39
|
* Provides table data, schema, and handlers for table operations.
|
|
40
40
|
*/
|
|
41
|
-
export const useTestTableModel = <T extends Type.
|
|
41
|
+
export const useTestTableModel = <T extends Type.AnyObj = Type.AnyObj>(): TestTableModel<T> => {
|
|
42
42
|
const registry = useContext(RegistryContext);
|
|
43
43
|
const { space } = useClientStory();
|
|
44
44
|
const db = space?.db;
|
|
@@ -97,7 +97,7 @@ export const useTestTableModel = <T extends Type.Obj.Any = Type.Obj.Any>(): Test
|
|
|
97
97
|
rows: filteredObjects,
|
|
98
98
|
onInsertRow: addRow,
|
|
99
99
|
onDeleteRows: handleDeleteRows,
|
|
100
|
-
|
|
100
|
+
onColumnDelete: handleDeleteColumn,
|
|
101
101
|
onCellUpdate: handleCellUpdate,
|
|
102
102
|
onRowOrderChange: handleRowOrderChange,
|
|
103
103
|
});
|
package/src/translations.ts
CHANGED
|
@@ -10,27 +10,25 @@ export const translations = [
|
|
|
10
10
|
{
|
|
11
11
|
'en-US': {
|
|
12
12
|
[translationKey]: {
|
|
13
|
-
'object
|
|
14
|
-
'create
|
|
15
|
-
'table
|
|
16
|
-
'settings
|
|
17
|
-
'table
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'column
|
|
24
|
-
'column
|
|
25
|
-
'column
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'create new object label': 'Create new object “{{text}}”',
|
|
33
|
-
'save view label': 'Save changes to view',
|
|
13
|
+
'object.placeholder': 'New table',
|
|
14
|
+
'create-object.label': 'Create table',
|
|
15
|
+
'table-name.placeholder': 'Table name',
|
|
16
|
+
'settings.title': 'Table settings',
|
|
17
|
+
'table-schema.label': 'Type',
|
|
18
|
+
'continue.label': 'Continue',
|
|
19
|
+
'create-stack-section.label': 'Create table',
|
|
20
|
+
'add-row.label': 'Add row',
|
|
21
|
+
'column-action-sort-descending.menu': 'Sort Descending',
|
|
22
|
+
'column-action-sort-ascending.menu': 'Sort Ascending',
|
|
23
|
+
'column-action-clear-sorting.menu': 'Clear Sorting',
|
|
24
|
+
'column-action-settings.menu': 'Column Settings',
|
|
25
|
+
'column-action-delete.menu': 'Delete Column',
|
|
26
|
+
'delete-row.label': 'Delete row',
|
|
27
|
+
'bulk-delete-row.label': 'Delete selected rows',
|
|
28
|
+
'column-deleted.label': 'Column deleted',
|
|
29
|
+
'new-column-button.label': 'Create column',
|
|
30
|
+
'create-new-object.label': 'Create new object “{{text}}”',
|
|
31
|
+
'save-view.label': 'Save changes to view',
|
|
34
32
|
},
|
|
35
33
|
},
|
|
36
34
|
},
|
package/src/types/Table.ts
CHANGED
|
@@ -6,14 +6,15 @@ import * as Match from 'effect/Match';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
8
8
|
|
|
9
|
-
import { Obj, Ref, Type } from '@dxos/echo';
|
|
9
|
+
import { Annotation, JsonSchema, Obj, Ref, Type } from '@dxos/echo';
|
|
10
|
+
import { View } from '@dxos/echo';
|
|
10
11
|
import { FormInputAnnotation, type JsonPath, type JsonSchemaType, LabelAnnotation } from '@dxos/echo/internal';
|
|
11
|
-
import {
|
|
12
|
+
import { ViewAnnotation } from '@dxos/schema';
|
|
12
13
|
|
|
13
14
|
export const Table = Schema.Struct({
|
|
14
15
|
name: Schema.String.pipe(Schema.optional),
|
|
15
16
|
|
|
16
|
-
view:
|
|
17
|
+
view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false)),
|
|
17
18
|
|
|
18
19
|
sizes: Schema.Record({
|
|
19
20
|
// TODO(wittjosiah): Should be JsonPath.
|
|
@@ -22,11 +23,15 @@ export const Table = Schema.Struct({
|
|
|
22
23
|
}).pipe(Schema.mutable, FormInputAnnotation.set(false)),
|
|
23
24
|
}).pipe(
|
|
24
25
|
Type.object({
|
|
25
|
-
typename: 'dxos.
|
|
26
|
-
version: '0.
|
|
26
|
+
typename: 'org.dxos.type.table',
|
|
27
|
+
version: '0.1.0',
|
|
27
28
|
}),
|
|
28
29
|
LabelAnnotation.set(['name']),
|
|
29
30
|
ViewAnnotation.set(true),
|
|
31
|
+
Annotation.IconAnnotation.set({
|
|
32
|
+
icon: 'ph--table--regular',
|
|
33
|
+
hue: 'green',
|
|
34
|
+
}),
|
|
30
35
|
);
|
|
31
36
|
|
|
32
37
|
export interface Table extends Schema.Schema.Type<typeof Table> {}
|
|
@@ -47,7 +52,7 @@ export const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table =
|
|
|
47
52
|
|
|
48
53
|
// Preset sizes.
|
|
49
54
|
if (jsonSchema) {
|
|
50
|
-
const schema =
|
|
55
|
+
const schema = JsonSchema.toEffectSchema(jsonSchema);
|
|
51
56
|
const properties = SchemaAST.getPropertySignatures(schema.ast);
|
|
52
57
|
for (const property of properties) {
|
|
53
58
|
const name = property.name.toString() as JsonPath;
|
|
@@ -72,19 +77,3 @@ export const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table =
|
|
|
72
77
|
|
|
73
78
|
return table;
|
|
74
79
|
};
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
// V1
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
export const TableV1 = Schema.Struct({
|
|
81
|
-
sizes: Schema.Record({
|
|
82
|
-
key: Schema.String,
|
|
83
|
-
value: Schema.Number,
|
|
84
|
-
}).pipe(Schema.mutable),
|
|
85
|
-
}).pipe(
|
|
86
|
-
Type.object({
|
|
87
|
-
typename: 'dxos.org/type/Table',
|
|
88
|
-
version: '0.1.0',
|
|
89
|
-
}),
|
|
90
|
-
);
|