@xh/hoist 78.0.0-SNAPSHOT.1763665705643 → 78.0.0-SNAPSHOT.1763737348746
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/CHANGELOG.md +11 -4
- package/build/types/data/cube/BucketSpec.d.ts +28 -8
- package/build/types/data/cube/Cube.d.ts +3 -3
- package/build/types/data/cube/Query.d.ts +1 -3
- package/build/types/data/cube/View.d.ts +4 -3
- package/build/types/data/cube/row/BucketRow.d.ts +1 -1
- package/data/cube/BucketSpec.ts +33 -15
- package/data/cube/Cube.ts +3 -3
- package/data/cube/Query.ts +4 -6
- package/data/cube/View.ts +26 -13
- package/data/cube/row/BucketRow.ts +1 -1
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
### 💥 Breaking Changes
|
|
6
6
|
|
|
7
|
-
* `GridModel.cleanColumnState` is now private (not expected to impact applications).
|
|
8
7
|
* `GridModel.setColumnState` no longer patches existing column state, but instead replaces it
|
|
9
8
|
wholesale. Applications that were relying on the prior patching behavior will need to
|
|
10
9
|
call `GridModel.applyColumnStateChanges` instead.
|
|
10
|
+
* `GridModel.cleanColumnState` is now private (not expected to impact applications).
|
|
11
11
|
|
|
12
12
|
### 🎁 New Features
|
|
13
13
|
|
|
14
|
-
* `FieldFilter`
|
|
14
|
+
* Added new `FieldFilter` operators `not begins` and `not ends`.
|
|
15
|
+
* Added new optional `BucketSpec.dependentFields` config to the Cube API, allowing apps to ensure
|
|
16
|
+
proper re-bucketing of rows during data-only updates where those updates could affect bucketing
|
|
17
|
+
determinations made by the spec.
|
|
15
18
|
|
|
16
19
|
### 🐞 Bug Fixes
|
|
17
20
|
|
|
@@ -19,11 +22,15 @@
|
|
|
19
22
|
numerical ID.
|
|
20
23
|
* Fixed issue where newly added columns appearing in the Displayed Columns section of the column
|
|
21
24
|
chooser after loading grid state that was persisted before the columns were added to the grid.
|
|
25
|
+
* Removed a minor Cube `Query` annoyance - `dimensions` are now automatically added to the `fields`
|
|
26
|
+
list and do not need to be manually repeated there.
|
|
22
27
|
|
|
23
28
|
### ⚙️ Technical
|
|
24
29
|
|
|
25
|
-
* `
|
|
26
|
-
|
|
30
|
+
* Updated the Cube API's `BucketSpecFn` to return either a concrete `BucketSpec` class instance (as
|
|
31
|
+
before) or a plain object conforming to the new `BucketSpecConfig` interface.
|
|
32
|
+
* Enhanced `FetchService` to recognize variants on the `application/json` content-type when
|
|
33
|
+
processing failed responses and decoding exceptions - e.g. `application/problem+json`.
|
|
27
34
|
|
|
28
35
|
## 77.1.1 - 2025-11-12
|
|
29
36
|
|
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
import { BaseRow } from './row/BaseRow';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Spec to define a bucketing level within the hierarchy of data returned by a Query, as identified
|
|
4
|
+
* by a set of rows passed to a {@link BucketSpecFn} configured on that Query (or defaulted from
|
|
5
|
+
* the Cube). If this object is returned for a candidate set of rows, each row is evaluated by the
|
|
6
|
+
* spec's `bucketFn` to determine if it should yield a value for the bucket, causing the row to be
|
|
7
|
+
* nested underneath a new {@link BucketRow} created to hold all rows with that value.
|
|
4
8
|
*/
|
|
9
|
+
export interface BucketSpecConfig {
|
|
10
|
+
/** Name for the bucketing level configured by this spec - equivalent to a dimension name. */
|
|
11
|
+
name: string;
|
|
12
|
+
/**
|
|
13
|
+
* Function returning the bucketed value (if any) into which the given row should be placed -
|
|
14
|
+
* equivalent to a dimension value. Return null/undefined to exclude the row from bucketing.
|
|
15
|
+
*/
|
|
16
|
+
bucketFn: (row: BaseRow) => string;
|
|
17
|
+
/**
|
|
18
|
+
* Function returning bucket row label from the bucket value string returned by bucketFn.
|
|
19
|
+
* Defaults to using the value directly.
|
|
20
|
+
*/
|
|
21
|
+
labelFn?: (bucket: string) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Fields on which the `bucketFn` depends, to ensure rows are re-bucketed if dependent field
|
|
24
|
+
* values change. If not provided or does not cover all fields potentially accessed by
|
|
25
|
+
* `bucketFn`, an incremental "data only" update that should have changed a row's bucket can
|
|
26
|
+
* fail to do so.
|
|
27
|
+
*/
|
|
28
|
+
dependentFields?: string[];
|
|
29
|
+
}
|
|
5
30
|
export declare class BucketSpec {
|
|
6
31
|
name: string;
|
|
7
32
|
bucketFn: (row: BaseRow) => string;
|
|
8
33
|
labelFn: (bucket: string) => string;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* @param bucketFn - function to determine which (if any) bucket the given row should
|
|
12
|
-
* be placed into
|
|
13
|
-
* @param labelFn - function to generate the bucket row label from name returned by bucketFn
|
|
14
|
-
**/
|
|
15
|
-
constructor(name: string, bucketFn: (row: BaseRow) => string, labelFn?: (bucket: string) => string);
|
|
34
|
+
dependentFields: string[];
|
|
35
|
+
constructor(config: BucketSpecConfig);
|
|
16
36
|
}
|
|
@@ -8,7 +8,7 @@ import { StoreRecord } from '../StoreRecord';
|
|
|
8
8
|
import { AggregateRow } from './row/AggregateRow';
|
|
9
9
|
import { BucketRow } from './row/BucketRow';
|
|
10
10
|
import { BaseRow } from './row/BaseRow';
|
|
11
|
-
import { BucketSpec } from './BucketSpec';
|
|
11
|
+
import { BucketSpec, BucketSpecConfig } from './BucketSpec';
|
|
12
12
|
export interface CubeConfig {
|
|
13
13
|
fields: CubeField[] | CubeFieldSpec[];
|
|
14
14
|
/** Default configs applied to all `CubeField`s constructed internally by this Cube. */
|
|
@@ -60,9 +60,9 @@ export type OmitFn = (row: AggregateRow | BucketRow) => boolean;
|
|
|
60
60
|
* aggregations and create an unwanted "Open" grouping.
|
|
61
61
|
*
|
|
62
62
|
* @param rows - the rows being checked for bucketing
|
|
63
|
-
* @returns
|
|
63
|
+
* @returns {@link BucketSpecConfig} for dynamic sub-aggregations, or null to perform no bucketing.
|
|
64
64
|
*/
|
|
65
|
-
export type BucketSpecFn = (rows: BaseRow[]) => BucketSpec;
|
|
65
|
+
export type BucketSpecFn = (rows: BaseRow[]) => BucketSpecConfig | BucketSpec;
|
|
66
66
|
/**
|
|
67
67
|
* A data store that supports grouping, aggregating, and filtering data on multiple dimensions.
|
|
68
68
|
*
|
|
@@ -19,8 +19,6 @@ export interface QueryConfig {
|
|
|
19
19
|
* Fields or field names on which data should be grouped and aggregated. These are the ordered
|
|
20
20
|
* grouping levels in the resulting hierarchy - e.g. ['Country', 'State', 'City'].
|
|
21
21
|
*
|
|
22
|
-
* Any fields provided here must also be included in the `fields` array, if specified.
|
|
23
|
-
*
|
|
24
22
|
* If not provided or empty, the resulting data will not be grouped. Specify 'includeRoot' or
|
|
25
23
|
* 'includeLeaves' in that case, otherwise no data will be returned.
|
|
26
24
|
*/
|
|
@@ -81,7 +79,7 @@ export interface QueryConfig {
|
|
|
81
79
|
*
|
|
82
80
|
* This can be used to break selected aggregations into sub-groups dynamically, without having
|
|
83
81
|
* to define another dimension in the Cube and have it apply to all aggregations. See the
|
|
84
|
-
* {@link BucketSpecFn} type and {@link
|
|
82
|
+
* {@link BucketSpecFn} type and {@link BucketSpecConfig} interface for additional information.
|
|
85
83
|
*
|
|
86
84
|
* Defaults to {@link Cube.bucketSpecFn}.
|
|
87
85
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HoistBase, PlainObject, Some } from '@xh/hoist/core';
|
|
2
|
-
import { Cube, CubeField, Filter, FilterLike, Query, QueryConfig, Store, StoreRecordId } from '@xh/hoist/data';
|
|
2
|
+
import { Cube, CubeField, Filter, FilterLike, Query, QueryConfig, Store, StoreChangeLog, StoreRecordId } from '@xh/hoist/data';
|
|
3
3
|
import { ViewRowData } from '@xh/hoist/data/cube/ViewRowData';
|
|
4
4
|
import { AggregationContext } from './aggregate/AggregationContext';
|
|
5
5
|
import { BaseRow } from './row/BaseRow';
|
|
@@ -51,6 +51,7 @@ export declare class View extends HoistBase {
|
|
|
51
51
|
private _rowDatas;
|
|
52
52
|
private _leafMap;
|
|
53
53
|
private _recordMap;
|
|
54
|
+
private _bucketDependentFields;
|
|
54
55
|
_aggContext: AggregationContext;
|
|
55
56
|
_rowCache: Map<string, BaseRow>;
|
|
56
57
|
/** @internal - applications should use {@link Cube.createView} */
|
|
@@ -79,7 +80,7 @@ export declare class View extends HoistBase {
|
|
|
79
80
|
/** Update the filter on the current Query.*/
|
|
80
81
|
setFilter(filter: FilterLike): void;
|
|
81
82
|
noteCubeLoaded(): void;
|
|
82
|
-
noteCubeUpdated(changeLog:
|
|
83
|
+
noteCubeUpdated(changeLog: StoreChangeLog): void;
|
|
83
84
|
private fullUpdate;
|
|
84
85
|
private dataOnlyUpdate;
|
|
85
86
|
private loadStores;
|
|
@@ -88,7 +89,7 @@ export declare class View extends HoistBase {
|
|
|
88
89
|
private groupAndInsertRecords;
|
|
89
90
|
private bucketRows;
|
|
90
91
|
private getSimpleUpdates;
|
|
91
|
-
private
|
|
92
|
+
private hasDimOrBucketUpdates;
|
|
92
93
|
private cachedRow;
|
|
93
94
|
private filterRecords;
|
|
94
95
|
private createAggregationContext;
|
|
@@ -5,7 +5,7 @@ import { View } from '../View';
|
|
|
5
5
|
/**
|
|
6
6
|
* Row within a dataset produced by a Cube / View representing aggregated data on a dimension that
|
|
7
7
|
* has been further grouped into a dynamic child "bucket" - a subset of the dimension-level
|
|
8
|
-
* {@link AggregateRow} produced as per a specified {@link
|
|
8
|
+
* {@link AggregateRow} produced as per a specified {@link BucketSpecFn}.
|
|
9
9
|
*
|
|
10
10
|
* This is an internal data structure - {@link ViewRowData} is the public row-level data API.
|
|
11
11
|
*/
|
package/data/cube/BucketSpec.ts
CHANGED
|
@@ -8,26 +8,44 @@
|
|
|
8
8
|
import {BaseRow} from './row/BaseRow';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Spec to define a bucketing level within the hierarchy of data returned by a Query, as identified
|
|
12
|
+
* by a set of rows passed to a {@link BucketSpecFn} configured on that Query (or defaulted from
|
|
13
|
+
* the Cube). If this object is returned for a candidate set of rows, each row is evaluated by the
|
|
14
|
+
* spec's `bucketFn` to determine if it should yield a value for the bucket, causing the row to be
|
|
15
|
+
* nested underneath a new {@link BucketRow} created to hold all rows with that value.
|
|
12
16
|
*/
|
|
17
|
+
export interface BucketSpecConfig {
|
|
18
|
+
/** Name for the bucketing level configured by this spec - equivalent to a dimension name. */
|
|
19
|
+
name: string;
|
|
20
|
+
/**
|
|
21
|
+
* Function returning the bucketed value (if any) into which the given row should be placed -
|
|
22
|
+
* equivalent to a dimension value. Return null/undefined to exclude the row from bucketing.
|
|
23
|
+
*/
|
|
24
|
+
bucketFn: (row: BaseRow) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Function returning bucket row label from the bucket value string returned by bucketFn.
|
|
27
|
+
* Defaults to using the value directly.
|
|
28
|
+
*/
|
|
29
|
+
labelFn?: (bucket: string) => string;
|
|
30
|
+
/**
|
|
31
|
+
* Fields on which the `bucketFn` depends, to ensure rows are re-bucketed if dependent field
|
|
32
|
+
* values change. If not provided or does not cover all fields potentially accessed by
|
|
33
|
+
* `bucketFn`, an incremental "data only" update that should have changed a row's bucket can
|
|
34
|
+
* fail to do so.
|
|
35
|
+
*/
|
|
36
|
+
dependentFields?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
13
39
|
export class BucketSpec {
|
|
14
40
|
name: string;
|
|
15
41
|
bucketFn: (row: BaseRow) => string;
|
|
16
42
|
labelFn: (bucket: string) => string;
|
|
43
|
+
dependentFields: string[];
|
|
17
44
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
**/
|
|
24
|
-
constructor(
|
|
25
|
-
name: string,
|
|
26
|
-
bucketFn: (row: BaseRow) => string,
|
|
27
|
-
labelFn?: (bucket: string) => string
|
|
28
|
-
) {
|
|
29
|
-
this.name = name;
|
|
30
|
-
this.bucketFn = bucketFn;
|
|
31
|
-
this.labelFn = labelFn ?? (b => b);
|
|
45
|
+
constructor(config: BucketSpecConfig) {
|
|
46
|
+
this.name = config.name;
|
|
47
|
+
this.bucketFn = config.bucketFn;
|
|
48
|
+
this.labelFn = config.labelFn ?? (b => b);
|
|
49
|
+
this.dependentFields = config.dependentFields ?? [];
|
|
32
50
|
}
|
|
33
51
|
}
|
package/data/cube/Cube.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {StoreRecord} from '../StoreRecord';
|
|
|
17
17
|
import {AggregateRow} from './row/AggregateRow';
|
|
18
18
|
import {BucketRow} from './row/BucketRow';
|
|
19
19
|
import {BaseRow} from './row/BaseRow';
|
|
20
|
-
import {BucketSpec} from './BucketSpec';
|
|
20
|
+
import {BucketSpec, BucketSpecConfig} from './BucketSpec';
|
|
21
21
|
import {defaultsDeep, isEmpty} from 'lodash';
|
|
22
22
|
|
|
23
23
|
export interface CubeConfig {
|
|
@@ -82,9 +82,9 @@ export type OmitFn = (row: AggregateRow | BucketRow) => boolean;
|
|
|
82
82
|
* aggregations and create an unwanted "Open" grouping.
|
|
83
83
|
*
|
|
84
84
|
* @param rows - the rows being checked for bucketing
|
|
85
|
-
* @returns
|
|
85
|
+
* @returns {@link BucketSpecConfig} for dynamic sub-aggregations, or null to perform no bucketing.
|
|
86
86
|
*/
|
|
87
|
-
export type BucketSpecFn = (rows: BaseRow[]) => BucketSpec;
|
|
87
|
+
export type BucketSpecFn = (rows: BaseRow[]) => BucketSpecConfig | BucketSpec;
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* A data store that supports grouping, aggregating, and filtering data on multiple dimensions.
|
package/data/cube/Query.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
StoreRecord
|
|
17
17
|
} from '@xh/hoist/data';
|
|
18
18
|
import {throwIf} from '@xh/hoist/utils/js';
|
|
19
|
-
import {find, isEqual} from 'lodash';
|
|
19
|
+
import {find, isEqual, uniq} from 'lodash';
|
|
20
20
|
import {Cube} from './Cube';
|
|
21
21
|
import {CubeField} from './CubeField';
|
|
22
22
|
|
|
@@ -40,8 +40,6 @@ export interface QueryConfig {
|
|
|
40
40
|
* Fields or field names on which data should be grouped and aggregated. These are the ordered
|
|
41
41
|
* grouping levels in the resulting hierarchy - e.g. ['Country', 'State', 'City'].
|
|
42
42
|
*
|
|
43
|
-
* Any fields provided here must also be included in the `fields` array, if specified.
|
|
44
|
-
*
|
|
45
43
|
* If not provided or empty, the resulting data will not be grouped. Specify 'includeRoot' or
|
|
46
44
|
* 'includeLeaves' in that case, otherwise no data will be returned.
|
|
47
45
|
*/
|
|
@@ -109,7 +107,7 @@ export interface QueryConfig {
|
|
|
109
107
|
*
|
|
110
108
|
* This can be used to break selected aggregations into sub-groups dynamically, without having
|
|
111
109
|
* to define another dimension in the Cube and have it apply to all aggregations. See the
|
|
112
|
-
* {@link BucketSpecFn} type and {@link
|
|
110
|
+
* {@link BucketSpecFn} type and {@link BucketSpecConfig} interface for additional information.
|
|
113
111
|
*
|
|
114
112
|
* Defaults to {@link Cube.bucketSpecFn}.
|
|
115
113
|
*/
|
|
@@ -153,8 +151,8 @@ export class Query {
|
|
|
153
151
|
omitFn = cube.omitFn
|
|
154
152
|
}: QueryConfig) {
|
|
155
153
|
this.cube = cube;
|
|
156
|
-
this.fields = this.parseFields(fields);
|
|
157
154
|
this.dimensions = this.parseDimensions(dimensions);
|
|
155
|
+
this.fields = uniq([...this.parseFields(fields), ...(this.dimensions ?? [])]);
|
|
158
156
|
this.includeRoot = includeRoot;
|
|
159
157
|
this.includeLeaves = includeLeaves;
|
|
160
158
|
this.provideLeaves = provideLeaves;
|
|
@@ -234,7 +232,7 @@ export class Query {
|
|
|
234
232
|
private parseDimensions(raw: CubeField[] | string[]): CubeField[] {
|
|
235
233
|
if (!raw) return null;
|
|
236
234
|
if (raw[0] instanceof CubeField) return raw as CubeField[];
|
|
237
|
-
const {fields} = this;
|
|
235
|
+
const {fields} = this.cube;
|
|
238
236
|
return raw.map(name => {
|
|
239
237
|
const field = find(fields, {name});
|
|
240
238
|
throwIf(
|
package/data/cube/View.ts
CHANGED
|
@@ -14,14 +14,16 @@ import {
|
|
|
14
14
|
Query,
|
|
15
15
|
QueryConfig,
|
|
16
16
|
Store,
|
|
17
|
+
StoreChangeLog,
|
|
17
18
|
StoreRecord,
|
|
18
19
|
StoreRecordId
|
|
19
20
|
} from '@xh/hoist/data';
|
|
21
|
+
import {BucketSpec} from '@xh/hoist/data/cube/BucketSpec';
|
|
20
22
|
import {ViewRowData} from '@xh/hoist/data/cube/ViewRowData';
|
|
21
23
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
22
24
|
import {shallowEqualArrays} from '@xh/hoist/utils/impl';
|
|
23
25
|
import {logWithDebug, throwIf} from '@xh/hoist/utils/js';
|
|
24
|
-
import {castArray, find, forEach, groupBy, isEmpty, isNil, map} from 'lodash';
|
|
26
|
+
import {castArray, find, forEach, groupBy, isEmpty, isNil, map, uniq} from 'lodash';
|
|
25
27
|
import {AggregationContext} from './aggregate/AggregationContext';
|
|
26
28
|
import {AggregateRow} from './row/AggregateRow';
|
|
27
29
|
import {BaseRow} from './row/BaseRow';
|
|
@@ -94,6 +96,7 @@ export class View extends HoistBase {
|
|
|
94
96
|
private _rowDatas: ViewRowData[] = null;
|
|
95
97
|
private _leafMap: Map<StoreRecordId, LeafRow> = null;
|
|
96
98
|
private _recordMap: Map<StoreRecordId, StoreRecord> = null;
|
|
99
|
+
private _bucketDependentFields = new Set<string>();
|
|
97
100
|
_aggContext: AggregationContext = null;
|
|
98
101
|
_rowCache: Map<string, BaseRow> = null;
|
|
99
102
|
|
|
@@ -207,7 +210,7 @@ export class View extends HoistBase {
|
|
|
207
210
|
}
|
|
208
211
|
|
|
209
212
|
@action
|
|
210
|
-
noteCubeUpdated(changeLog:
|
|
213
|
+
noteCubeUpdated(changeLog: StoreChangeLog) {
|
|
211
214
|
const simpleUpdates = this.getSimpleUpdates(changeLog);
|
|
212
215
|
|
|
213
216
|
if (!simpleUpdates) {
|
|
@@ -277,6 +280,8 @@ export class View extends HoistBase {
|
|
|
277
280
|
{dimensions, includeRoot} = query,
|
|
278
281
|
rootId = 'root';
|
|
279
282
|
|
|
283
|
+
this._bucketDependentFields.clear();
|
|
284
|
+
|
|
280
285
|
const records = this._aggContext.filteredRecords;
|
|
281
286
|
const leafMap: Map<StoreRecordId, LeafRow> = new Map();
|
|
282
287
|
let newRows = this.groupAndInsertRecords(records, dimensions, rootId, {}, leafMap);
|
|
@@ -360,13 +365,19 @@ export class View extends HoistBase {
|
|
|
360
365
|
if (!query.bucketSpecFn) return rows;
|
|
361
366
|
if (!query.includeLeaves && rows[0]?.isLeaf) return rows;
|
|
362
367
|
|
|
363
|
-
const
|
|
364
|
-
if (!
|
|
368
|
+
const bucketSpecOrConf = query.bucketSpecFn(rows);
|
|
369
|
+
if (!bucketSpecOrConf) return rows;
|
|
365
370
|
|
|
366
|
-
const
|
|
371
|
+
const bucketSpec =
|
|
372
|
+
bucketSpecOrConf instanceof BucketSpec
|
|
373
|
+
? bucketSpecOrConf
|
|
374
|
+
: new BucketSpec(bucketSpecOrConf);
|
|
375
|
+
const {name: bucketName, bucketFn, dependentFields} = bucketSpec,
|
|
367
376
|
buckets: Record<string, BaseRow[]> = {},
|
|
368
377
|
ret: BaseRow[] = [];
|
|
369
378
|
|
|
379
|
+
dependentFields.forEach(it => this._bucketDependentFields.add(it));
|
|
380
|
+
|
|
370
381
|
// Determine which bucket to put this row into (if any)
|
|
371
382
|
rows.forEach(row => {
|
|
372
383
|
const bucketVal = bucketFn(row);
|
|
@@ -394,14 +405,14 @@ export class View extends HoistBase {
|
|
|
394
405
|
|
|
395
406
|
// return a list of simple data updates we can apply to leaves.
|
|
396
407
|
// false if leaf population changing, or aggregations are complex
|
|
397
|
-
private getSimpleUpdates(t): StoreRecord[] | false {
|
|
408
|
+
private getSimpleUpdates(t: StoreChangeLog): StoreRecord[] | false {
|
|
398
409
|
if (!t) return [];
|
|
399
410
|
if (!this.aggregatorsAreSimple) return false;
|
|
400
411
|
const {_leafMap, query} = this;
|
|
401
412
|
|
|
402
413
|
// 1) Simple case: no filter
|
|
403
414
|
if (!query.filter) {
|
|
404
|
-
return isEmpty(t.add) && isEmpty(t.remove) && !this.
|
|
415
|
+
return isEmpty(t.add) && isEmpty(t.remove) && !this.hasDimOrBucketUpdates(t.update)
|
|
405
416
|
? t.update
|
|
406
417
|
: false;
|
|
407
418
|
}
|
|
@@ -425,19 +436,21 @@ export class View extends HoistBase {
|
|
|
425
436
|
|
|
426
437
|
// 2c) Examine the final set of updates for any changes to dimension field values which would
|
|
427
438
|
// require rebuilding the row hierarchy
|
|
428
|
-
if (this.
|
|
439
|
+
if (this.hasDimOrBucketUpdates(ret)) return false;
|
|
429
440
|
|
|
430
441
|
return ret;
|
|
431
442
|
}
|
|
432
443
|
|
|
433
|
-
private
|
|
434
|
-
const {dimensions} = this.query
|
|
435
|
-
|
|
444
|
+
private hasDimOrBucketUpdates(update: StoreRecord[]): boolean {
|
|
445
|
+
const {dimensions} = this.query,
|
|
446
|
+
bucketDependentFields = Array.from(this._bucketDependentFields);
|
|
447
|
+
|
|
448
|
+
if (isEmpty(dimensions) && isEmpty(bucketDependentFields)) return false;
|
|
436
449
|
|
|
437
|
-
const
|
|
450
|
+
const fieldNames = uniq([...dimensions.map(it => it.name), ...bucketDependentFields]);
|
|
438
451
|
for (const rec of update) {
|
|
439
452
|
const curRec = this._leafMap.get(rec.id);
|
|
440
|
-
if (
|
|
453
|
+
if (fieldNames.some(name => rec.data[name] !== curRec.data[name])) return true;
|
|
441
454
|
}
|
|
442
455
|
|
|
443
456
|
return false;
|
|
@@ -13,7 +13,7 @@ import {View} from '../View';
|
|
|
13
13
|
/**
|
|
14
14
|
* Row within a dataset produced by a Cube / View representing aggregated data on a dimension that
|
|
15
15
|
* has been further grouped into a dynamic child "bucket" - a subset of the dimension-level
|
|
16
|
-
* {@link AggregateRow} produced as per a specified {@link
|
|
16
|
+
* {@link AggregateRow} produced as per a specified {@link BucketSpecFn}.
|
|
17
17
|
*
|
|
18
18
|
* This is an internal data structure - {@link ViewRowData} is the public row-level data API.
|
|
19
19
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "78.0.0-SNAPSHOT.
|
|
3
|
+
"version": "78.0.0-SNAPSHOT.1763737348746",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|