@dxos/react-ui-table 0.8.4-main.7ace549 → 0.8.4-main.8360d9e660
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-5XWSB3JP.mjs → chunk-BF54SSNL.mjs} +27 -28
- package/dist/lib/browser/chunk-BF54SSNL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-KNC7XYT6.mjs → chunk-PTHMYAAM.mjs} +620 -652
- package/dist/lib/browser/chunk-PTHMYAAM.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1154 -1036
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +20 -19
- 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-CWSGS3Y2.mjs → chunk-GI72MQSG.mjs} +620 -651
- package/dist/lib/node-esm/chunk-GI72MQSG.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-MKTMOY46.mjs → chunk-SIRDIPHZ.mjs} +27 -28
- package/dist/lib/node-esm/chunk-SIRDIPHZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1154 -1036
- 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 +20 -19
- 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/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 +2 -2
- package/dist/types/src/components/Table/ColumnSettings.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.d.ts +7 -7
- package/dist/types/src/components/Table/DynamicTable.d.ts.map +1 -1
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts +2 -16
- package/dist/types/src/components/Table/DynamicTable.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/Relations.stories.d.ts +6 -3
- package/dist/types/src/components/Table/Relations.stories.d.ts.map +1 -1
- package/dist/types/src/components/Table/RowActionsMenu.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.d.ts +11 -11
- package/dist/types/src/components/Table/Table.d.ts.map +1 -1
- package/dist/types/src/components/Table/Table.stories.d.ts +6 -3
- package/dist/types/src/components/Table/Table.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/CellValidationMessage.d.ts +1 -1
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts +10 -12
- package/dist/types/src/components/TableCellEditor/FormCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts +5 -7
- package/dist/types/src/components/TableCellEditor/TableCellEditor.d.ts.map +1 -1
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts +2 -2
- package/dist/types/src/components/TableCellEditor/TableCellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts +3 -3
- package/dist/types/src/components/TableToolbar/TableToolbar.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +22 -13
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/hooks/useAddRow.d.ts +5 -6
- package/dist/types/src/hooks/useAddRow.d.ts.map +1 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts +2 -1
- package/dist/types/src/hooks/useProjectionModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useTableModel.d.ts +6 -5
- package/dist/types/src/hooks/useTableModel.d.ts.map +1 -1
- package/dist/types/src/model/modal-controller.d.ts +5 -1
- package/dist/types/src/model/modal-controller.d.ts.map +1 -1
- package/dist/types/src/model/selection-model.d.ts +17 -8
- package/dist/types/src/model/selection-model.d.ts.map +1 -1
- package/dist/types/src/model/table-model.d.ts +100 -18
- package/dist/types/src/model/table-model.d.ts.map +1 -1
- package/dist/types/src/model/table-presentation.d.ts +3 -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 +5 -8
- package/dist/types/src/testing/useTestTableModel.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Table.d.ts +13 -15
- package/dist/types/src/types/Table.d.ts.map +1 -1
- package/dist/types/src/util/dynamic-table.d.ts +12 -10
- 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/index.d.ts +2 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/query-ast.d.ts +7 -0
- package/dist/types/src/util/query-ast.d.ts.map +1 -0
- package/dist/types/src/util/sort.d.ts +12 -0
- package/dist/types/src/util/sort.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -55
- package/src/components/Table/ColumnActionsMenu.tsx +10 -6
- package/src/components/Table/ColumnSettings.tsx +8 -9
- package/src/components/Table/DynamicTable.stories.tsx +11 -11
- package/src/components/Table/DynamicTable.tsx +30 -30
- package/src/components/Table/Relations.stories.tsx +34 -31
- package/src/components/Table/RowActionsMenu.tsx +3 -2
- package/src/components/Table/Table.stories.tsx +80 -67
- package/src/components/Table/Table.tsx +324 -299
- package/src/components/TableCellEditor/CellValidationMessage.tsx +5 -5
- package/src/components/TableCellEditor/FormCellEditor.tsx +118 -135
- package/src/components/TableCellEditor/TableCellEditor.stories.tsx +10 -7
- package/src/components/TableCellEditor/TableCellEditor.tsx +27 -27
- package/src/components/TableToolbar/TableToolbar.tsx +24 -18
- package/src/components/testing.ts +37 -11
- package/src/hooks/useAddRow.ts +8 -10
- package/src/hooks/useProjectionModel.ts +29 -9
- package/src/hooks/useTableModel.ts +27 -23
- package/src/model/modal-controller.ts +23 -12
- package/src/model/selection-model.ts +75 -44
- package/src/model/table-model.test.ts +81 -53
- package/src/model/table-model.ts +410 -138
- package/src/model/table-presentation.test.ts +58 -48
- package/src/model/table-presentation.ts +34 -28
- package/src/playwright/TableManager.ts +12 -5
- package/src/playwright/playwright.config.ts +1 -1
- package/src/playwright/smoke.spec.ts +48 -4
- package/src/testing/useTestTableModel.ts +24 -25
- package/src/translations.ts +1 -1
- package/src/types/Table.ts +28 -38
- package/src/util/dynamic-table.test.ts +70 -0
- package/src/util/dynamic-table.ts +36 -19
- package/src/util/index.ts +2 -0
- package/src/util/query-ast.ts +22 -0
- package/src/util/sort.ts +90 -0
- package/src/util/table-buttons.ts +1 -1
- package/src/util/table-controls.ts +2 -2
- package/dist/lib/browser/chunk-5XWSB3JP.mjs.map +0 -7
- package/dist/lib/browser/chunk-KNC7XYT6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CWSGS3Y2.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MKTMOY46.mjs.map +0 -7
- package/dist/types/src/model/table-sorting.d.ts +0 -56
- package/dist/types/src/model/table-sorting.d.ts.map +0 -1
- package/src/model/table-sorting.ts +0 -234
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/types/Table.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Match from 'effect/Match';\nimport * as Schema from 'effect/Schema';\n\nimport { Obj, Ref, Type } from '@dxos/echo';\nimport { FormInputAnnotation, type JsonPath, type JsonSchemaType, LabelAnnotation } from '@dxos/echo/internal';\nimport { type SimpleType } from '@dxos/effect';\nimport { View, ViewAnnotation, getSchemaProperties } from '@dxos/schema';\n\nconst TableSchema = Schema.Struct({\n name: Schema.String.pipe(Schema.optional),\n\n view: Type.Ref(View.View).pipe(FormInputAnnotation.set(false)),\n\n sizes: Schema.Record({\n // TODO(wittjosiah): Should be JsonPath.\n key: Schema.String,\n value: Schema.Number,\n }).pipe(Schema.mutable, FormInputAnnotation.set(false)),\n}).pipe(\n Type.Obj({\n typename: 'dxos.org/type/Table',\n version: '0.2.0',\n }),\n LabelAnnotation.set(['name']),\n ViewAnnotation.set(true),\n);\n\nexport interface Table extends Schema.Schema.Type<typeof TableSchema> {}\nexport interface TableEncoded extends Schema.Schema.Encoded<typeof TableSchema> {}\nexport const Table: Schema.Schema<Table, TableEncoded> = TableSchema;\n\ntype MakeProps = Omit<Partial<Obj.MakeProps<typeof Table>>, 'view'> & {\n view: View.View;\n /** Required to auto-size columns. */\n jsonSchema?: JsonSchemaType;\n};\n\n/**\n * Make a table as a view of a data set.\n */\nexport const make = ({ name, sizes = {}, view, jsonSchema }: MakeProps): Table => {\n const table = Obj.make(Table, { name, view: Ref.make(view), sizes });\n\n // Preset sizes.\n if (jsonSchema) {\n const schema = Type.toEffectSchema(jsonSchema);\n const properties = getSchemaProperties(schema.ast, {});\n for (const property of properties) {\n if (sizes?.[property.name]) {\n table.sizes[property.name] = 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\n return table;\n};\n\n//\n// V1\n//\n\nexport const TableV1 = Schema.Struct({\n sizes: Schema.Record({\n key: Schema.String,\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);\n"],
|
|
5
|
-
"mappings": ";;;;;;;;AAAA;;;;;;AAIA,YAAYA,WAAW;AACvB,YAAYC,YAAY;AAExB,SAASC,KAAKC,KAAKC,YAAY;AAC/B,SAASC,qBAAyDC,uBAAuB;AAEzF,SAASC,MAAMC,gBAAgBC,2BAA2B;AAE1D,IAAMC,cAAqBC,cAAO;EAChCC,MAAaC,cAAOC,KAAYC,eAAQ;EAExCC,MAAMC,KAAKC,IAAIC,KAAKA,IAAI,EAAEL,KAAKM,oBAAoBC,IAAI,KAAA,CAAA;EAEvDC,OAAcC,cAAO;;IAEnBC,KAAYX;IACZY,OAAcC;EAChB,CAAA,EAAGZ,KAAYa,gBAASP,oBAAoBC,IAAI,KAAA,CAAA;AAClD,CAAA,EAAGP,KACDG,KAAKW,IAAI;EACPC,UAAU;EACVC,SAAS;AACX,CAAA,GACAC,gBAAgBV,IAAI;EAAC;CAAO,GAC5BW,eAAeX,IAAI,IAAA,CAAA;AAKd,IAAMY,QAA4CvB;AAWlD,IAAMwB,OAAO,CAAC,EAAEtB,MAAMU,QAAQ,CAAC,GAAGN,MAAMmB,WAAU,MAAa;AACpE,QAAMC,QAAQR,IAAIM,KAAKD,OAAO;IAAErB;IAAMI,MAAME,IAAIgB,KAAKlB,IAAAA;IAAOM;EAAM,CAAA;AAGlE,MAAIa,YAAY;AACd,UAAME,SAASpB,KAAKqB,eAAeH,UAAAA;AACnC,UAAMI,aAAaC,oBAAoBH,OAAOI,KAAK,CAAC,CAAA;AACpD,eAAWC,YAAYH,YAAY;AACjC,UAAIjB,QAAQoB,SAAS9B,IAAI,GAAG;AAC1BwB,cAAMd,MAAMoB,SAAS9B,IAAI,IAAIU,MAAMoB,SAAS9B,IAAI;AAChD;MACF;AAEA+B,MAAMC,WAAI,EAAe9B,KACjB+B,WAAK,WAAW,MAAA;AACpBT,cAAMd,MAAMoB,SAAS9B,IAAI,IAAgB;MAC3C,CAAA,GACMiC,WAAK,UAAU,MAAA;AACnBT,cAAMd,MAAMoB,SAAS9B,IAAI,IAAgB;MAC3C,CAAA,GACMkC,aAAO,MAAA;MAEb,CAAA,CAAA,EACAJ,SAASE,IAAI;IACjB;EACF;AAEA,SAAOR;AACT;AAMO,IAAMW,UAAiBpC,cAAO;EACnCW,OAAcC,cAAO;IACnBC,KAAYX;IACZY,OAAcC;EAChB,CAAA,EAAGZ,KAAYa,cAAO;AACxB,CAAA,EAAGb,KACDG,KAAKW,IAAI;EACPC,UAAU;EACVC,SAAS;AACX,CAAA,CAAA;",
|
|
6
|
-
"names": ["Match", "Schema", "Obj", "Ref", "Type", "FormInputAnnotation", "LabelAnnotation", "View", "ViewAnnotation", "getSchemaProperties", "TableSchema", "Struct", "name", "String", "pipe", "optional", "view", "Type", "Ref", "View", "FormInputAnnotation", "set", "sizes", "Record", "key", "value", "Number", "mutable", "Obj", "typename", "version", "LabelAnnotation", "ViewAnnotation", "Table", "make", "jsonSchema", "table", "schema", "toEffectSchema", "properties", "getSchemaProperties", "ast", "property", "Match", "type", "when", "orElse", "TableV1"]
|
|
7
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { type ReadonlySignal, type Signal } from '@preact/signals-core';
|
|
2
|
-
import { type FieldSortType, type ProjectionModel, type SortDirectionType, type View } from '@dxos/schema';
|
|
3
|
-
import { type TableRow } from './table-model';
|
|
4
|
-
/**
|
|
5
|
-
* Manages table sorting functionality through display and data index mapping.
|
|
6
|
-
*
|
|
7
|
-
* When a table is sorted, rows visually reorder but we need to maintain references
|
|
8
|
-
* to the original data positions. This is handled through two types of indices:
|
|
9
|
-
*
|
|
10
|
-
* - Display indices: The visual positions after sorting
|
|
11
|
-
* - Data indices: The original positions in the source data
|
|
12
|
-
*
|
|
13
|
-
* The _displayToDataIndex map maintains these relationships, enabling lookups
|
|
14
|
-
* between visual and source positions.
|
|
15
|
-
*
|
|
16
|
-
* Example mapping between display and data indices:
|
|
17
|
-
*
|
|
18
|
-
* Original Data: Sorted Display: Mapping: (Display → Data)
|
|
19
|
-
* ┌──────────────┐ ┌──────────────┐ ┌────────┐
|
|
20
|
-
* │ 0: "Banana" │ => │ 0: "Apple" │ => │ 0 => 1 │
|
|
21
|
-
* │ 1: "Apple" │ => │ 1: "Banana" │ => │ 1 => 0 │
|
|
22
|
-
* │ 2: "Cherry" │ => │ 2: "Cherry" │ => │ 2 => 2 │
|
|
23
|
-
* └──────────────┘ └──────────────┘ └────────┘
|
|
24
|
-
*/
|
|
25
|
-
export declare class TableSorting<T extends TableRow> {
|
|
26
|
-
private readonly _displayToDataIndex;
|
|
27
|
-
private readonly _rows;
|
|
28
|
-
private readonly _getView;
|
|
29
|
-
private readonly _projection;
|
|
30
|
-
private readonly _localSort;
|
|
31
|
-
private readonly _isDirty;
|
|
32
|
-
readonly sortedRows: ReadonlySignal<T[]>;
|
|
33
|
-
constructor({ rows, projection, getView, }: {
|
|
34
|
-
rows: Signal<T[]>;
|
|
35
|
-
getView: () => View.View;
|
|
36
|
-
projection: ProjectionModel;
|
|
37
|
-
});
|
|
38
|
-
/**
|
|
39
|
-
* @reactive
|
|
40
|
-
* @returns Local sort if present, falls back to view sort.
|
|
41
|
-
*/
|
|
42
|
-
get sorting(): FieldSortType | undefined;
|
|
43
|
-
/**
|
|
44
|
-
* @reactive
|
|
45
|
-
* @returns Whether local sort differs from view sort.
|
|
46
|
-
*/
|
|
47
|
-
get isDirty(): boolean;
|
|
48
|
-
getDataIndex(displayIndex: number): number;
|
|
49
|
-
setSort(fieldId: string, direction: SortDirectionType): void;
|
|
50
|
-
toggleSort(fieldId: string): void;
|
|
51
|
-
clearSort(): void;
|
|
52
|
-
save(): void;
|
|
53
|
-
private initialiseSortedRows;
|
|
54
|
-
private getSortValue;
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=table-sorting.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"table-sorting.d.ts","sourceRoot":"","sources":["../../../../src/model/table-sorting.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAoB,MAAM,sBAAsB,CAAC;AAK1F,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAY9C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS,QAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA6B;IACjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;IACnD,SAAgB,UAAU,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;gBAEpC,EACV,IAAI,EACJ,UAAU,EACV,OAAO,GACR,EAAE;QACD,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC;QACzB,UAAU,EAAE,eAAe,CAAC;KAC7B;IAqBD;;;OAGG;IACH,IAAW,OAAO,IAAI,aAAa,GAAG,SAAS,CAU9C;IAED;;;OAGG;IACH,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAI1C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAO5D,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAiBjC,SAAS,IAAI,IAAI;IAIjB,IAAI,IAAI,IAAI;IAgBnB,OAAO,CAAC,oBAAoB;IAkC5B,OAAO,CAAC,YAAY;CAsCrB"}
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type ReadonlySignal, type Signal, computed, signal } from '@preact/signals-core';
|
|
6
|
-
import orderBy from 'lodash.orderby';
|
|
7
|
-
|
|
8
|
-
import { Format, TypeEnum, getValue } from '@dxos/echo/internal';
|
|
9
|
-
import { formatForDisplay } from '@dxos/react-ui-form';
|
|
10
|
-
import {
|
|
11
|
-
type FieldSortType,
|
|
12
|
-
type FieldType,
|
|
13
|
-
type ProjectionModel,
|
|
14
|
-
type PropertyType,
|
|
15
|
-
type SortDirectionType,
|
|
16
|
-
type View,
|
|
17
|
-
} from '@dxos/schema';
|
|
18
|
-
|
|
19
|
-
import { type TableRow } from './table-model';
|
|
20
|
-
|
|
21
|
-
// TODO(wittjosiah): Remove. Use ordering in queries instead.
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Represents the local sort state.
|
|
25
|
-
* - undefined: No local sort set, fallback to view sort.
|
|
26
|
-
* - cleared: Sort explicitly cleared by user.
|
|
27
|
-
* - active: Active local sort with sort configuration.
|
|
28
|
-
*/
|
|
29
|
-
type LocalSort = { type: 'cleared' } | { type: 'active'; sort: FieldSortType } | undefined;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Manages table sorting functionality through display and data index mapping.
|
|
33
|
-
*
|
|
34
|
-
* When a table is sorted, rows visually reorder but we need to maintain references
|
|
35
|
-
* to the original data positions. This is handled through two types of indices:
|
|
36
|
-
*
|
|
37
|
-
* - Display indices: The visual positions after sorting
|
|
38
|
-
* - Data indices: The original positions in the source data
|
|
39
|
-
*
|
|
40
|
-
* The _displayToDataIndex map maintains these relationships, enabling lookups
|
|
41
|
-
* between visual and source positions.
|
|
42
|
-
*
|
|
43
|
-
* Example mapping between display and data indices:
|
|
44
|
-
*
|
|
45
|
-
* Original Data: Sorted Display: Mapping: (Display → Data)
|
|
46
|
-
* ┌──────────────┐ ┌──────────────┐ ┌────────┐
|
|
47
|
-
* │ 0: "Banana" │ => │ 0: "Apple" │ => │ 0 => 1 │
|
|
48
|
-
* │ 1: "Apple" │ => │ 1: "Banana" │ => │ 1 => 0 │
|
|
49
|
-
* │ 2: "Cherry" │ => │ 2: "Cherry" │ => │ 2 => 2 │
|
|
50
|
-
* └──────────────┘ └──────────────┘ └────────┘
|
|
51
|
-
*/
|
|
52
|
-
export class TableSorting<T extends TableRow> {
|
|
53
|
-
private readonly _displayToDataIndex = new Map<number, number>();
|
|
54
|
-
private readonly _rows: Signal<T[]>;
|
|
55
|
-
private readonly _getView: () => View.View;
|
|
56
|
-
private readonly _projection: ProjectionModel;
|
|
57
|
-
private readonly _localSort = signal<LocalSort>(undefined);
|
|
58
|
-
private readonly _isDirty: ReadonlySignal<boolean>;
|
|
59
|
-
public readonly sortedRows: ReadonlySignal<T[]>;
|
|
60
|
-
|
|
61
|
-
constructor({
|
|
62
|
-
rows,
|
|
63
|
-
projection,
|
|
64
|
-
getView,
|
|
65
|
-
}: {
|
|
66
|
-
rows: Signal<T[]>;
|
|
67
|
-
getView: () => View.View;
|
|
68
|
-
projection: ProjectionModel;
|
|
69
|
-
}) {
|
|
70
|
-
this._rows = rows;
|
|
71
|
-
this._getView = getView;
|
|
72
|
-
this._projection = projection;
|
|
73
|
-
this._isDirty = computed(() => {
|
|
74
|
-
const local = this._localSort.value;
|
|
75
|
-
const viewSort = this._getView().sort?.[0];
|
|
76
|
-
if (local?.type === 'cleared') {
|
|
77
|
-
return viewSort !== undefined;
|
|
78
|
-
}
|
|
79
|
-
if (local?.type === 'active') {
|
|
80
|
-
if (!viewSort) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
return local.sort.fieldId !== viewSort.fieldId || local.sort.direction !== viewSort.direction;
|
|
84
|
-
}
|
|
85
|
-
return false;
|
|
86
|
-
});
|
|
87
|
-
this.sortedRows = this.initialiseSortedRows();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* @reactive
|
|
92
|
-
* @returns Local sort if present, falls back to view sort.
|
|
93
|
-
*/
|
|
94
|
-
public get sorting(): FieldSortType | undefined {
|
|
95
|
-
const local = this._localSort.value;
|
|
96
|
-
if (local?.type === 'cleared') {
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
if (local?.type === 'active') {
|
|
100
|
-
return local.sort;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return this._getView().sort?.[0];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* @reactive
|
|
108
|
-
* @returns Whether local sort differs from view sort.
|
|
109
|
-
*/
|
|
110
|
-
public get isDirty(): boolean {
|
|
111
|
-
return this._isDirty.value;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public getDataIndex(displayIndex: number): number {
|
|
115
|
-
return this._displayToDataIndex.get(displayIndex) ?? displayIndex;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public setSort(fieldId: string, direction: SortDirectionType): void {
|
|
119
|
-
this._localSort.value = {
|
|
120
|
-
type: 'active',
|
|
121
|
-
sort: { fieldId, direction },
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
public toggleSort(fieldId: string): void {
|
|
126
|
-
if (!this.sorting || this.sorting.fieldId !== fieldId) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
switch (this.sorting.direction) {
|
|
131
|
-
case 'asc': {
|
|
132
|
-
this.setSort(fieldId, 'desc');
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
case 'desc': {
|
|
136
|
-
this.setSort(fieldId, 'asc');
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
public clearSort(): void {
|
|
143
|
-
this._localSort.value = { type: 'cleared' };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
public save(): void {
|
|
147
|
-
if (this._localSort.value !== undefined) {
|
|
148
|
-
const view = this._getView();
|
|
149
|
-
if (this._localSort.value.type === 'active') {
|
|
150
|
-
view.sort = [this._localSort.value.sort];
|
|
151
|
-
} else {
|
|
152
|
-
view.sort = [];
|
|
153
|
-
}
|
|
154
|
-
this._localSort.value = undefined;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
// Initialisation.
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
private initialiseSortedRows(): ReadonlySignal<T[]> {
|
|
163
|
-
return computed(() => {
|
|
164
|
-
this._displayToDataIndex.clear();
|
|
165
|
-
const sort = this.sorting;
|
|
166
|
-
if (!sort) {
|
|
167
|
-
return this._rows.value;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const field = this._projection.fields.find((f) => f.id === sort.fieldId);
|
|
171
|
-
if (!field) {
|
|
172
|
-
return this._rows.value;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const { props } = this._projection.getFieldProjection(field.id);
|
|
176
|
-
|
|
177
|
-
const dataWithIndices = this._rows.value.map((item, index) => {
|
|
178
|
-
const sortValue = this.getSortValue(props, field, item);
|
|
179
|
-
const isEmpty = !(props.type === TypeEnum.Boolean) && (sortValue == null || sortValue === '');
|
|
180
|
-
|
|
181
|
-
return { item, index, value: sortValue, isEmpty };
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const ordered = orderBy(dataWithIndices, ['isEmpty', 'value'], ['asc', sort.direction]);
|
|
185
|
-
|
|
186
|
-
ordered.forEach(({ index: dataIndex }, displayIndex) => {
|
|
187
|
-
if (displayIndex !== dataIndex) {
|
|
188
|
-
this._displayToDataIndex.set(displayIndex, dataIndex);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
return ordered.map(({ item }) => item);
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private getSortValue(props: PropertyType, field: FieldType, item: T) {
|
|
197
|
-
const { type, format } = props;
|
|
198
|
-
const value = getValue(item, field.path);
|
|
199
|
-
|
|
200
|
-
if (type === TypeEnum.Boolean) {
|
|
201
|
-
return !!value; // Coerce null/undefined to false for booleans.
|
|
202
|
-
}
|
|
203
|
-
if (type === TypeEnum.Number) {
|
|
204
|
-
return value;
|
|
205
|
-
}
|
|
206
|
-
if (value == null) {
|
|
207
|
-
return value;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* For single select fields, the sort value is determined by the index of the option in the
|
|
212
|
-
* options array. This ensures sort order matches the configured option order.
|
|
213
|
-
*/
|
|
214
|
-
if (format === Format.TypeFormat.SingleSelect) {
|
|
215
|
-
const options = props.options;
|
|
216
|
-
if (!options) {
|
|
217
|
-
return undefined;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const option = options.find((option) => option.id === value);
|
|
221
|
-
return option ? options.indexOf(option) : undefined;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// TODO(ZaymonFC): Should we format this returned value for display based on the referenced prop format?
|
|
225
|
-
// Right now we're going to be sorting based on the raw values.
|
|
226
|
-
// Maybe we need to recurse this function with the referenced prop format and field?
|
|
227
|
-
if (format === Format.TypeFormat.Ref && field.referencePath) {
|
|
228
|
-
const refValue = getValue(value.target, field.referencePath);
|
|
229
|
-
return refValue;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return formatForDisplay({ type, format, value });
|
|
233
|
-
}
|
|
234
|
-
}
|