@dxos/react-ui-table 0.7.5-main.499c70c → 0.7.5-main.6a330ac
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{chunk-AT7O4RAD.mjs → chunk-3FR6NHPD.mjs} +6 -4
- package/dist/lib/browser/chunk-3FR6NHPD.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +482 -284
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node/{chunk-YSBXNBDJ.cjs → chunk-NB3SFHGA.cjs} +8 -6
- package/dist/lib/node/chunk-NB3SFHGA.cjs.map +7 -0
- package/dist/lib/node/index.cjs +474 -270
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types/index.cjs +3 -3
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{chunk-6OH2NHUX.mjs → chunk-RLVVOZW5.mjs} +6 -4
- package/dist/lib/node-esm/chunk-RLVVOZW5.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +482 -284
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/_deprecated/components/Cell/Cell.d.ts +2 -2
- package/dist/types/src/_deprecated/components/Cell/Cell.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts +1 -2
- package/dist/types/src/_deprecated/components/Cell/CellCombobox.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts +2 -3
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts +6 -7
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnMenu.stories.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts +1 -2
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts +6 -7
- package/dist/types/src/_deprecated/components/ColumnMenu/ColumnSettings.stories.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/Table/Table.d.ts +5 -5
- package/dist/types/src/_deprecated/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts +8 -9
- package/dist/types/src/_deprecated/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/Table/TableBody.d.ts +1 -2
- package/dist/types/src/_deprecated/components/Table/TableBody.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/Table/TableContext.d.ts +1 -1
- package/dist/types/src/_deprecated/components/Table/TableContext.d.ts.map +1 -1
- package/dist/types/src/_deprecated/components/Table/TableHead.d.ts +1 -2
- package/dist/types/src/_deprecated/components/Table/TableHead.d.ts.map +1 -1
- package/dist/types/src/_deprecated/schema/Schema.stories.d.ts +2 -3
- package/dist/types/src/_deprecated/schema/Schema.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/ColumnSettings.d.ts +1 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/CreateRefPanel.d.ts +1 -2
- package/dist/types/src/components/Table/CreateRefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/RefPanel.d.ts +1 -2
- package/dist/types/src/components/Table/RefPanel.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts +1 -2
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +1 -1
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +1 -4
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +1 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +17 -0
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -0
- package/dist/types/src/components/TableToolbar/index.d.ts +2 -0
- package/dist/types/src/components/TableToolbar/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +12 -35
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +11 -19
- 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 +53 -0
- package/dist/types/src/model/table-sorting.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +2 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/table.d.ts +21 -90
- package/dist/types/src/types/table.d.ts.map +1 -1
- package/dist/types/src/util/table-buttons.d.ts +15 -1
- package/dist/types/src/util/table-buttons.d.ts.map +1 -1
- package/dist/types/src/util/table.d.ts +2 -1
- package/dist/types/src/util/table.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +36 -34
- package/src/_deprecated/schema/Schema.stories.tsx +1 -1
- package/src/components/Table/ColumnActionsMenu.tsx +4 -4
- package/src/components/Table/CreateRefPanel.tsx +3 -4
- package/src/components/Table/Table.stories.tsx +55 -44
- package/src/components/Table/Table.tsx +4 -0
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +1 -1
- package/src/components/TableToolbar/TableToolbar.tsx +72 -0
- package/src/components/{Toolbar → TableToolbar}/index.ts +1 -1
- package/src/components/index.ts +1 -1
- package/src/hooks/useTableModel.ts +21 -0
- package/src/model/table-model.test.ts +0 -5
- package/src/model/table-model.ts +41 -99
- package/src/model/table-presentation.ts +25 -4
- package/src/model/table-sorting.ts +215 -0
- package/src/playwright/smoke.spec.ts +35 -13
- package/src/translations.ts +2 -1
- package/src/types/table.ts +3 -3
- package/src/util/table-buttons.ts +33 -3
- package/src/util/table-controls.ts +2 -2
- package/src/util/table.ts +85 -52
- package/dist/lib/browser/chunk-AT7O4RAD.mjs.map +0 -7
- package/dist/lib/node/chunk-YSBXNBDJ.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-6OH2NHUX.mjs.map +0 -7
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +0 -21
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +0 -1
- package/dist/types/src/components/Toolbar/common.d.ts +0 -14
- package/dist/types/src/components/Toolbar/common.d.ts.map +0 -1
- package/dist/types/src/components/Toolbar/index.d.ts +0 -2
- package/dist/types/src/components/Toolbar/index.d.ts.map +0 -1
- package/dist/types/src/playwright/playwright.config.d.ts +0 -3
- package/dist/types/src/playwright/playwright.config.d.ts.map +0 -1
- package/src/components/Toolbar/Toolbar.tsx +0 -87
- package/src/components/Toolbar/common.tsx +0 -71
- /package/src/playwright/{playwright.config.ts → playwright.config.cts} +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { create } from '@dxos/live-object';
|
|
8
|
+
import { type ThemedClassName } from '@dxos/react-ui';
|
|
9
|
+
import {
|
|
10
|
+
createGapSeparator,
|
|
11
|
+
createMenuAction,
|
|
12
|
+
type MenuAction,
|
|
13
|
+
type MenuActionHandler,
|
|
14
|
+
MenuProvider,
|
|
15
|
+
ToolbarMenu,
|
|
16
|
+
useMenuActions,
|
|
17
|
+
} from '@dxos/react-ui-menu';
|
|
18
|
+
|
|
19
|
+
import { translationKey } from '../../translations';
|
|
20
|
+
|
|
21
|
+
export type TableToolbarActionProperties = { type: TableToolbarActionType };
|
|
22
|
+
|
|
23
|
+
export type TableToolbarAction = MenuAction<TableToolbarActionProperties>;
|
|
24
|
+
|
|
25
|
+
export type TableToolbarActionType = 'add-row' | 'comment' | 'save-view';
|
|
26
|
+
|
|
27
|
+
type TableToolbarState = Partial<{ viewDirty: boolean }>;
|
|
28
|
+
|
|
29
|
+
export type TableToolbarProps = ThemedClassName<
|
|
30
|
+
TableToolbarState & { onAction?: MenuActionHandler<TableToolbarAction>; attendableId?: string }
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
const createTableToolbarActions = (state: TableToolbarState) => {
|
|
34
|
+
const add = createMenuAction<TableToolbarActionProperties>('add-row', {
|
|
35
|
+
type: 'add-row',
|
|
36
|
+
icon: 'ph--plus--regular',
|
|
37
|
+
label: ['add row', { ns: translationKey }],
|
|
38
|
+
testId: 'table.toolbar.add-row',
|
|
39
|
+
});
|
|
40
|
+
const save = createMenuAction<TableToolbarActionProperties>('save-view', {
|
|
41
|
+
type: 'save-view',
|
|
42
|
+
icon: 'ph--floppy-disk--regular',
|
|
43
|
+
label: ['save view label', { ns: translationKey }],
|
|
44
|
+
testId: 'table.toolbar.save-view',
|
|
45
|
+
iconOnly: false,
|
|
46
|
+
hidden: !state.viewDirty,
|
|
47
|
+
});
|
|
48
|
+
const gap = createGapSeparator();
|
|
49
|
+
const comment = createMenuAction('comment', {
|
|
50
|
+
type: 'comment',
|
|
51
|
+
icon: 'ph--chat-text--regular',
|
|
52
|
+
label: ['create comment', { ns: translationKey }],
|
|
53
|
+
testId: 'table.toolbar.comment',
|
|
54
|
+
});
|
|
55
|
+
const nodes = [add, save, ...gap.nodes, comment];
|
|
56
|
+
return {
|
|
57
|
+
nodes,
|
|
58
|
+
edges: nodes.map(({ id: target }) => ({ source: 'root', target })),
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const TableToolbar = ({ classNames, viewDirty, attendableId, onAction }: TableToolbarProps) => {
|
|
63
|
+
const state = useMemo(() => create<TableToolbarState>({ viewDirty }), []);
|
|
64
|
+
const actionsCreator = useCallback(() => createTableToolbarActions(state), [state]);
|
|
65
|
+
const menu = useMenuActions(actionsCreator);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<MenuProvider {...menu} attendableId={attendableId} onAction={onAction as MenuActionHandler}>
|
|
69
|
+
<ToolbarMenu classNames={classNames} />
|
|
70
|
+
</MenuProvider>
|
|
71
|
+
);
|
|
72
|
+
};
|
package/src/components/index.ts
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { effect } from '@preact/signals-core';
|
|
5
6
|
import { useEffect, useState } from 'react';
|
|
6
7
|
|
|
7
8
|
import { type ReactiveObject } from '@dxos/react-client/echo';
|
|
9
|
+
import { useSelectionActions } from '@dxos/react-ui-attention';
|
|
8
10
|
import { type ViewProjection } from '@dxos/schema';
|
|
11
|
+
import { isNonNullable } from '@dxos/util';
|
|
9
12
|
|
|
10
13
|
import { type BaseTableRow, TableModel, type TableModelProps } from '../model';
|
|
11
14
|
import { type TableType } from '../types';
|
|
@@ -48,5 +51,23 @@ export const useTableModel = <T extends BaseTableRow = { id: string }>({
|
|
|
48
51
|
}
|
|
49
52
|
}, [model, objects]);
|
|
50
53
|
|
|
54
|
+
const { select, clear } = useSelectionActions([table?.id, table?.view?.target?.query.type].filter(isNonNullable));
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!model) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const unsubscribe = effect(() => {
|
|
62
|
+
select([...model.selection.selection.value]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Maybe clear the selection here?
|
|
66
|
+
return () => {
|
|
67
|
+
clear();
|
|
68
|
+
unsubscribe();
|
|
69
|
+
};
|
|
70
|
+
}, [model]);
|
|
71
|
+
|
|
51
72
|
return model;
|
|
52
73
|
};
|
|
@@ -37,11 +37,6 @@ describe('TableModel', () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
describe('methods', () => {
|
|
40
|
-
it('should set sorting', () => {
|
|
41
|
-
model.setSort('col2', 'asc');
|
|
42
|
-
expect(model.sorting).toEqual([{ columnId: 'col2', direction: 'asc' }]);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
40
|
it('should pin a row', () => {
|
|
46
41
|
model.pinRow(1, 'top');
|
|
47
42
|
expect(model.pinnedRows.top).toContain(1);
|
package/src/model/table-model.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Copyright
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { computed, effect, signal, type ReadonlySignal } from '@preact/signals-core';
|
|
6
|
-
import orderBy from 'lodash.orderby';
|
|
7
6
|
|
|
8
7
|
import { Resource } from '@dxos/context';
|
|
9
|
-
import { getValue, setValue,
|
|
8
|
+
import { type FieldSortType, FormatEnum, getValue, setValue, type JsonProp } from '@dxos/echo-schema';
|
|
10
9
|
import { invariant } from '@dxos/invariant';
|
|
11
10
|
import { isReactiveObject, makeRef } from '@dxos/live-object';
|
|
12
11
|
import { PublicKey } from '@dxos/react-client';
|
|
13
|
-
import {
|
|
12
|
+
import { formatForEditing, parseValue } from '@dxos/react-ui-form';
|
|
14
13
|
import {
|
|
15
14
|
type DxGridAxisMeta,
|
|
16
15
|
type DxGridPlaneRange,
|
|
@@ -20,18 +19,16 @@ import {
|
|
|
20
19
|
import { type ViewProjection } from '@dxos/schema';
|
|
21
20
|
|
|
22
21
|
import { SelectionModel } from './selection-model';
|
|
22
|
+
import { TableSorting } from './table-sorting';
|
|
23
23
|
import { type TableType } from '../types';
|
|
24
24
|
import { touch } from '../util';
|
|
25
25
|
|
|
26
|
-
export type SortDirection = 'asc' | 'desc';
|
|
27
|
-
export type SortConfig = { fieldId: string; direction: SortDirection };
|
|
28
|
-
|
|
29
26
|
export type BaseTableRow = Record<JsonProp, any> & { id: string };
|
|
30
27
|
|
|
31
28
|
export type TableModelProps<T extends BaseTableRow = { id: string }> = {
|
|
32
29
|
table: TableType;
|
|
33
30
|
projection: ViewProjection;
|
|
34
|
-
sorting?:
|
|
31
|
+
sorting?: FieldSortType[];
|
|
35
32
|
pinnedRows?: { top: number[]; bottom: number[] };
|
|
36
33
|
onInsertRow?: (index?: number) => void;
|
|
37
34
|
onDeleteRows?: (index: number, obj: T[]) => void;
|
|
@@ -51,25 +48,16 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
51
48
|
end: { row: 0, col: 0 },
|
|
52
49
|
});
|
|
53
50
|
|
|
54
|
-
/**
|
|
55
|
-
* Maps display indices to data indices.
|
|
56
|
-
* Used for translating between sorted/displayed order and original data order.
|
|
57
|
-
* Keys are display indices, values are corresponding data indices.
|
|
58
|
-
*/
|
|
59
|
-
private readonly _displayToDataIndex: Map<number, number> = new Map();
|
|
60
|
-
|
|
61
51
|
private readonly _onInsertRow?: TableModelProps<T>['onInsertRow'];
|
|
62
52
|
private readonly _onDeleteRows?: TableModelProps<T>['onDeleteRows'];
|
|
63
53
|
private readonly _onDeleteColumn?: TableModelProps<T>['onDeleteColumn'];
|
|
64
54
|
private readonly _onCellUpdate?: TableModelProps<T>['onCellUpdate'];
|
|
65
55
|
private readonly _onRowOrderChanged?: TableModelProps<T>['onRowOrderChanged'];
|
|
66
56
|
|
|
67
|
-
private readonly
|
|
68
|
-
private
|
|
69
|
-
private _rows = signal<T[]>([]);
|
|
57
|
+
private readonly _rows = signal<T[]>([]);
|
|
58
|
+
private readonly _sorting: TableSorting<T>;
|
|
70
59
|
|
|
71
60
|
private _pinnedRows: NonNullable<TableModelProps<T>['pinnedRows']>;
|
|
72
|
-
|
|
73
61
|
private _selection!: SelectionModel<T>;
|
|
74
62
|
private _columnMeta?: ReadonlySignal<DxGridAxisMeta>;
|
|
75
63
|
|
|
@@ -87,8 +75,13 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
87
75
|
super();
|
|
88
76
|
this._table = table;
|
|
89
77
|
this._projection = projection;
|
|
78
|
+
this._sorting = new TableSorting(this._rows, table.view?.target, projection);
|
|
79
|
+
|
|
80
|
+
if (sorting.length > 0) {
|
|
81
|
+
const [sort] = sorting;
|
|
82
|
+
this._sorting.setSort(sort.fieldId, sort.direction);
|
|
83
|
+
}
|
|
90
84
|
|
|
91
|
-
this._sorting.value = sorting.at(0);
|
|
92
85
|
this._pinnedRows = pinnedRows;
|
|
93
86
|
this._onInsertRow = onInsertRow;
|
|
94
87
|
this._onDeleteRows = onDeleteRows;
|
|
@@ -106,7 +99,14 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
public get rows(): ReadonlySignal<T[]> {
|
|
109
|
-
return this.
|
|
102
|
+
return this._sorting.sortedRows;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @reactive
|
|
107
|
+
*/
|
|
108
|
+
public get sorting(): TableSorting<T> | undefined {
|
|
109
|
+
return this._sorting;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
public get pinnedRows(): NonNullable<TableModelProps<T>['pinnedRows']> {
|
|
@@ -118,23 +118,23 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
118
118
|
return this._columnMeta;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
public get sorting(): SortConfig | undefined {
|
|
122
|
-
return this._sorting.value;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
121
|
public get selection() {
|
|
126
122
|
return this._selection;
|
|
127
123
|
}
|
|
128
124
|
|
|
125
|
+
/** @reactive */
|
|
126
|
+
public get isViewDirty(): boolean {
|
|
127
|
+
return this._sorting.isDirty;
|
|
128
|
+
}
|
|
129
|
+
|
|
129
130
|
//
|
|
130
131
|
// Initialisation
|
|
131
132
|
//
|
|
132
133
|
|
|
133
134
|
protected override async _open() {
|
|
134
135
|
this.initializeColumnMeta();
|
|
135
|
-
this.initializeSorting();
|
|
136
136
|
this.initializeEffects();
|
|
137
|
-
this._selection = new SelectionModel(this.
|
|
137
|
+
this._selection = new SelectionModel(this._sorting.sortedRows, () => this._onRowOrderChanged?.());
|
|
138
138
|
await this._selection.open(this._ctx);
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -153,59 +153,9 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
private initializeSorting(): void {
|
|
157
|
-
this._sortedRows = computed(() => {
|
|
158
|
-
this._displayToDataIndex.clear();
|
|
159
|
-
const sort = this._sorting.value;
|
|
160
|
-
if (!sort) {
|
|
161
|
-
return this._rows.value;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const field = this._table.view?.target?.fields.find((field) => field.id === sort.fieldId);
|
|
165
|
-
if (!field) {
|
|
166
|
-
return this._rows.value;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
170
|
-
|
|
171
|
-
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
172
|
-
const rawValue = getValue(item, field.path);
|
|
173
|
-
let sortValue = rawValue;
|
|
174
|
-
|
|
175
|
-
if (props.type.startsWith('boolean')) {
|
|
176
|
-
sortValue = !!rawValue; // Coerce null/undefined/false to false for booleans
|
|
177
|
-
} else if (rawValue != null) {
|
|
178
|
-
if (props.format === FormatEnum.Ref && field.referencePath) {
|
|
179
|
-
sortValue = getValue(rawValue, field.referencePath);
|
|
180
|
-
} else {
|
|
181
|
-
sortValue = formatForDisplay({ type: props.type, format: props.format, value: rawValue });
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
item,
|
|
187
|
-
index,
|
|
188
|
-
value: sortValue,
|
|
189
|
-
isEmpty: !props.type.startsWith('boolean') && (sortValue == null || sortValue === ''),
|
|
190
|
-
};
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const sorted = orderBy(dataWithIndices, ['isEmpty', 'value'], ['asc', sort.direction]);
|
|
194
|
-
|
|
195
|
-
for (let displayIndex = 0; displayIndex < sorted.length; displayIndex++) {
|
|
196
|
-
const { index: dataIndex } = sorted[displayIndex];
|
|
197
|
-
if (displayIndex !== dataIndex) {
|
|
198
|
-
this._displayToDataIndex.set(displayIndex, dataIndex);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return sorted.map(({ item }) => item);
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
156
|
private initializeEffects(): void {
|
|
207
157
|
const rowOrderWatcher = effect(() => {
|
|
208
|
-
touch(this.
|
|
158
|
+
touch(this._sorting.sortedRows.value);
|
|
209
159
|
this._onRowOrderChanged?.();
|
|
210
160
|
});
|
|
211
161
|
this._ctx.onDispose(rowOrderWatcher);
|
|
@@ -221,7 +171,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
221
171
|
for (let row = start.row; row <= end.row; row++) {
|
|
222
172
|
rowEffects.push(
|
|
223
173
|
effect(() => {
|
|
224
|
-
const obj = this.
|
|
174
|
+
const obj = this._sorting.sortedRows.value[row];
|
|
225
175
|
this?._table?.view?.target?.fields.forEach((field) => touch(getValue(obj, field.path)));
|
|
226
176
|
this._onCellUpdate?.({ row, col: start.col, plane: 'grid' });
|
|
227
177
|
}),
|
|
@@ -246,12 +196,12 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
246
196
|
public getColumnCount = (): number => this.table.view?.target?.fields.length ?? 0;
|
|
247
197
|
|
|
248
198
|
public insertRow = (rowIndex?: number): void => {
|
|
249
|
-
const row = rowIndex !== undefined ? this.
|
|
199
|
+
const row = rowIndex !== undefined ? this._sorting.getDataIndex(rowIndex) : this._rows.value.length;
|
|
250
200
|
this._onInsertRow?.(row);
|
|
251
201
|
};
|
|
252
202
|
|
|
253
203
|
public deleteRow = (rowIndex: number): void => {
|
|
254
|
-
const row = this.
|
|
204
|
+
const row = this._sorting.getDataIndex(rowIndex);
|
|
255
205
|
const obj = this._rows.value[row];
|
|
256
206
|
const objectsToDelete = [];
|
|
257
207
|
|
|
@@ -272,7 +222,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
272
222
|
}
|
|
273
223
|
|
|
274
224
|
const field = fields[col];
|
|
275
|
-
const dataIndex = this.
|
|
225
|
+
const dataIndex = this._sorting.getDataIndex(row);
|
|
276
226
|
const value = getValue(this._rows.value[dataIndex], field.path);
|
|
277
227
|
if (value == null) {
|
|
278
228
|
return '';
|
|
@@ -295,7 +245,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
295
245
|
};
|
|
296
246
|
|
|
297
247
|
public setCellData = ({ col, row }: DxGridPlanePosition, value: any): void => {
|
|
298
|
-
const rowIdx = this.
|
|
248
|
+
const rowIdx = this._sorting.getDataIndex(row);
|
|
299
249
|
const fields = this.table.view?.target?.fields ?? [];
|
|
300
250
|
if (col < 0 || col >= fields.length) {
|
|
301
251
|
return;
|
|
@@ -334,7 +284,7 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
334
284
|
* @param {(value: any) => any} update - A function that takes the current value and returns the updated value.
|
|
335
285
|
*/
|
|
336
286
|
public updateCellData({ col, row }: DxGridPlanePosition, update: (value: any) => any): void {
|
|
337
|
-
const dataRow = this.
|
|
287
|
+
const dataRow = this._sorting.getDataIndex(row);
|
|
338
288
|
const fields = this.table.view?.target?.fields ?? [];
|
|
339
289
|
const field = fields[col];
|
|
340
290
|
|
|
@@ -354,9 +304,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
354
304
|
|
|
355
305
|
const field = this.table.view.target?.fields.find((field) => field.id === fieldId);
|
|
356
306
|
if (field && this._onDeleteColumn) {
|
|
357
|
-
if (this._sorting.value?.fieldId === fieldId) {
|
|
358
|
-
this.clearSort();
|
|
359
|
-
}
|
|
360
307
|
this._onDeleteColumn(field.id);
|
|
361
308
|
}
|
|
362
309
|
}
|
|
@@ -376,18 +323,6 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
376
323
|
}
|
|
377
324
|
}
|
|
378
325
|
|
|
379
|
-
//
|
|
380
|
-
// Sorting
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
public setSort(fieldId: string, direction: SortDirection): void {
|
|
384
|
-
this._sorting.value = { fieldId, direction };
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
public clearSort(): void {
|
|
388
|
-
this._sorting.value = undefined;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
326
|
//
|
|
392
327
|
// Pinning
|
|
393
328
|
//
|
|
@@ -401,4 +336,11 @@ export class TableModel<T extends BaseTableRow = { id: string }> extends Resourc
|
|
|
401
336
|
this._pinnedRows.top = this._pinnedRows.top.filter((index: number) => index !== rowIndex);
|
|
402
337
|
this._pinnedRows.bottom = this._pinnedRows.bottom.filter((index: number) => index !== rowIndex);
|
|
403
338
|
}
|
|
339
|
+
|
|
340
|
+
//
|
|
341
|
+
// View operations
|
|
342
|
+
//
|
|
343
|
+
public saveView(): void {
|
|
344
|
+
this._sorting.save();
|
|
345
|
+
}
|
|
404
346
|
}
|
|
@@ -68,7 +68,8 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
switch (props.format) {
|
|
71
|
-
case FormatEnum.Boolean:
|
|
71
|
+
case FormatEnum.Boolean:
|
|
72
|
+
case FormatEnum.SingleSelect: {
|
|
72
73
|
return '';
|
|
73
74
|
}
|
|
74
75
|
case FormatEnum.Ref: {
|
|
@@ -119,6 +120,17 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
119
120
|
cell.readonly = true;
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
if (props.format === FormatEnum.SingleSelect) {
|
|
124
|
+
const value = getValue(obj, field.path);
|
|
125
|
+
const options = this.model.projection.getFieldProjection(field.id).props.options;
|
|
126
|
+
if (options) {
|
|
127
|
+
const option = options.find((o) => o.id === value);
|
|
128
|
+
if (option) {
|
|
129
|
+
cell.accessoryHtml = `<span class="dx-tag" data-hue="${option.color}">${option.title}</span>`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
const idx = toPlaneCellIndex({ col: colIndex, row: displayIndex });
|
|
123
135
|
cells[idx] = cell;
|
|
124
136
|
};
|
|
@@ -142,12 +154,19 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
142
154
|
const fields = this.model.table.view?.target?.fields ?? [];
|
|
143
155
|
for (let col = range.start.col; col <= range.end.col && col < fields.length; col++) {
|
|
144
156
|
const { field, props } = this.model.projection.getFieldProjection(fields[col].id);
|
|
157
|
+
const sorting = this.model.sorting?.sorting;
|
|
158
|
+
const direction = sorting?.fieldId === field.id ? sorting.direction : undefined;
|
|
159
|
+
|
|
145
160
|
cells[toPlaneCellIndex({ col, row: 0 })] = {
|
|
146
161
|
// TODO(burdon): Use same logic as form for fallback title.
|
|
147
162
|
value: props.title ?? field.path,
|
|
148
163
|
readonly: true,
|
|
149
164
|
resizeHandle: 'col',
|
|
150
|
-
accessoryHtml:
|
|
165
|
+
accessoryHtml: `
|
|
166
|
+
${direction !== undefined ? tableButtons.sort.render({ fieldId: field.id, direction }) : ''}
|
|
167
|
+
${tableButtons.columnSettings.render({ fieldId: field.id })}
|
|
168
|
+
`,
|
|
169
|
+
className: '!bg-gridHeader',
|
|
151
170
|
};
|
|
152
171
|
}
|
|
153
172
|
|
|
@@ -189,13 +208,14 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
189
208
|
private getSelectAllCell(): DxGridPlaneCells {
|
|
190
209
|
return {
|
|
191
210
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
192
|
-
value: '',
|
|
193
211
|
accessoryHtml: tableControls.checkbox.render({
|
|
194
212
|
rowIndex: 0,
|
|
195
213
|
header: true,
|
|
196
214
|
checked: this.model.selection.allRowsSeleted.value,
|
|
197
215
|
}),
|
|
216
|
+
className: '!bg-gridHeader',
|
|
198
217
|
readonly: true,
|
|
218
|
+
value: '',
|
|
199
219
|
},
|
|
200
220
|
};
|
|
201
221
|
}
|
|
@@ -203,11 +223,12 @@ export class TablePresentation<T extends BaseTableRow = { id: string }> {
|
|
|
203
223
|
private getNewColumnCell(): DxGridPlaneCells {
|
|
204
224
|
return {
|
|
205
225
|
[toPlaneCellIndex({ col: 0, row: 0 })]: {
|
|
206
|
-
value: '',
|
|
207
226
|
accessoryHtml: tableButtons.addColumn.render({
|
|
208
227
|
disabled: (this.model.table.view?.target?.fields?.length ?? 0) >= VIEW_FIELD_LIMIT,
|
|
209
228
|
}),
|
|
229
|
+
className: '!bg-gridHeader',
|
|
210
230
|
readonly: true,
|
|
231
|
+
value: '',
|
|
211
232
|
},
|
|
212
233
|
};
|
|
213
234
|
}
|