@xh/hoist 55.2.0 → 55.3.0
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 +39 -23
- package/admin/differ/DifferModel.ts +4 -4
- package/cmp/ag-grid/AgGrid.scss +5 -8
- package/cmp/ag-grid/AgGridModel.ts +2 -3
- package/cmp/chart/Chart.ts +1 -1
- package/cmp/chart/ChartModel.ts +2 -2
- package/cmp/filter/FilterChooserModel.ts +1 -2
- package/cmp/grid/GridModel.ts +1 -2
- package/cmp/grid/filter/GridFilterFieldSpec.ts +10 -12
- package/cmp/grid/filter/GridFilterModel.ts +15 -8
- package/cmp/grouping/GroupingChooserModel.ts +2 -2
- package/cmp/tab/TabModel.ts +1 -1
- package/core/HoistBase.ts +1 -2
- package/core/HoistBaseDecorators.ts +2 -2
- package/core/HoistService.ts +7 -7
- package/core/exception/ExceptionHandler.ts +3 -5
- package/core/load/LoadSpec.ts +2 -1
- package/core/model/HoistModel.ts +7 -7
- package/core/persist/PersistenceProvider.ts +2 -3
- package/data/cube/View.ts +5 -5
- package/desktop/cmp/dash/container/DashContainerModel.ts +1 -2
- package/desktop/cmp/grid/impl/filter/ColumnHeaderFilter.ts +21 -18
- package/desktop/cmp/grid/impl/filter/values/ValuesTab.scss +20 -1
- package/desktop/cmp/grid/impl/filter/values/ValuesTabModel.ts +23 -14
- package/desktop/cmp/leftrightchooser/LeftRightChooser.ts +2 -3
- package/desktop/cmp/panel/PanelModel.ts +1 -0
- package/desktop/cmp/panel/impl/Splitter.ts +2 -1
- package/desktop/cmp/panel/impl/dragger/DraggerModel.ts +2 -2
- package/desktop/cmp/treemap/TreeMapModel.ts +2 -2
- package/package.json +1 -1
- package/svc/PrefService.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,55 +1,74 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v55.3.0 - 2023-03-03
|
|
4
|
+
|
|
5
|
+
### 🐞 Bug Fixes
|
|
6
|
+
* Grid column filters scroll their internal grid horizontally to avoid clipping longer values.
|
|
7
|
+
* Minor improvements to the same grid filter dialog's alignment and labelling.
|
|
8
|
+
|
|
9
|
+
### ⚙️ Technical
|
|
10
|
+
* Use native `structuredClone` instead of lodash `deepClone` throughout toolkit.
|
|
11
|
+
|
|
12
|
+
## v55.2.1 - 2023-02-24
|
|
13
|
+
|
|
14
|
+
### 🐞 Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Fixed issue where a resizable `Panel` splitter could be rendered incorrectly while dragging.
|
|
17
|
+
|
|
3
18
|
## v55.2.0 - 2023-02-10
|
|
4
19
|
|
|
5
20
|
### 🎁 New Features
|
|
21
|
+
|
|
6
22
|
* `DashCanvas` enhancements:
|
|
7
23
|
* Views now support minimum and maximum dimensions.
|
|
8
|
-
* Views now expose an `allowDuplicate` flag for controlling the `Duplicate` menu item
|
|
24
|
+
* Views now expose an `allowDuplicate` flag for controlling the `Duplicate` menu item
|
|
25
|
+
visibility.
|
|
9
26
|
|
|
10
27
|
### 🐞 Bug Fixes
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
|
|
29
|
+
* Fixed a bug with Cube views having dimensions containing non-string or `null` values. Rows grouped
|
|
30
|
+
by these dimensions would report values for the dimension which were incorrectly stringified (e.g.
|
|
31
|
+
`'null'` vs. `null` or `'5'` vs. `5`). This has been fixed. Note that the stringified value is
|
|
32
|
+
still reported for the rows' `cubeLabel` value, and will be used for the purposes of grouping.
|
|
15
33
|
|
|
16
34
|
### ⚙️ Typescript API Adjustments
|
|
17
35
|
|
|
18
36
|
* Improved signatures of `RestStore` APIs.
|
|
19
37
|
|
|
20
|
-
|
|
21
38
|
## v55.1.0 - 2023-02-09
|
|
22
39
|
|
|
23
|
-
Version 55 is the first major update of the toolkit after our transition to
|
|
24
|
-
to a host of runtime fixes and features, it also contains a good number of important
|
|
25
|
-
typing adjustments, which are listed below.
|
|
40
|
+
Version 55 is the first major update of the toolkit after our transition to Typescript. In addition
|
|
41
|
+
to a host of runtime fixes and features, it also contains a good number of important Typescript
|
|
42
|
+
typing adjustments, which are listed below. It also includes a helpful
|
|
26
43
|
[Typescript upgrade guide](https://github.com/xh/hoist-react/blob/develop/docs/upgrade-to-typescript.md).
|
|
27
44
|
|
|
28
45
|
### 🎁 New Features
|
|
29
46
|
|
|
30
47
|
* Grid exports can now be tracked in the admin activity tab by setting `exportOptions.track` to
|
|
31
48
|
true (defaults to false).
|
|
32
|
-
* Miscellaneous performance improvements to the cube package
|
|
33
|
-
* The implementation of the `Cube.omitFn` feature has been enhanced.
|
|
34
|
-
called on *all* non-leaf nodes, not just single child nodes.
|
|
49
|
+
* Miscellaneous performance improvements to the cube package.
|
|
50
|
+
* The implementation of the `Cube.omitFn` feature has been enhanced. This function will now be
|
|
51
|
+
called on *all* non-leaf nodes, not just single child nodes. This allows for more flexible
|
|
35
52
|
editing of the shape of the resulting hierarchical data emitted by cube views.
|
|
36
53
|
|
|
37
54
|
### 🐞 Bug Fixes
|
|
55
|
+
|
|
38
56
|
* Fixed: grid cell editors would drop a single character edit.
|
|
39
57
|
* Fixed: grid date input editor's popup did not position correctly in a grid with pinned columns.
|
|
40
58
|
* Fixed issue with `DashContainer` flashing its "empty" text briefly before loading.
|
|
41
59
|
* Several Hoist TypeScript types, interfaces, and signatures have been improved or corrected (typing
|
|
42
60
|
changes only).
|
|
43
61
|
* Fix bug where a `className` provided to a `Panel` with `modalSupport` would be dropped when in a
|
|
44
|
-
modal state. Note this necessitated an additional layer in the `Panel` DOM hierarchy.
|
|
62
|
+
modal state. Note this necessitated an additional layer in the `Panel` DOM hierarchy. Highly
|
|
45
63
|
specific CSS selectors may be affected.
|
|
46
64
|
* Fix bug where `TileFrame` would not pass through the keys of its children.
|
|
47
65
|
|
|
48
66
|
### 💥 Breaking Changes
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
|
|
68
|
+
* The semantics of `Cube.omitFn` have changed such that it will now be called on all aggregate
|
|
69
|
+
nodes, not just nodes with a single child. Applications may need to adjust any implementation of
|
|
70
|
+
this function accordingly.
|
|
71
|
+
* `hoistCmp.containerFactory` and `hoistCmp.withContainerFactory` are removed in favor of
|
|
53
72
|
the basic `hoistCmp.factory` and `hoistCmp.withFactory` respectively. See typescript
|
|
54
73
|
API adjustments below.
|
|
55
74
|
|
|
@@ -57,22 +76,19 @@ typing adjustments, which are listed below. In also includes a helpful
|
|
|
57
76
|
|
|
58
77
|
The following Typescript API were adjusted in v55.
|
|
59
78
|
|
|
60
|
-
* Removed the distinction between `StandardElementFactory` and `ContainerElementFactory`.
|
|
79
|
+
* Removed the distinction between `StandardElementFactory` and `ContainerElementFactory`. This
|
|
61
80
|
distinction was deemed to be unnecessary, and overcomplicated the understanding of Hoist.
|
|
62
|
-
Applications should simply continue to use `ElementFactory` instead.
|
|
63
|
-
`hoistCmp.withContainerFactory` are also removed in favor of the basic `hoistCmp.factory` and
|
|
81
|
+
Applications should simply continue to use `ElementFactory` instead. `hoistCmp.containerFactory`
|
|
82
|
+
and `hoistCmp.withContainerFactory` are also removed in favor of the basic `hoistCmp.factory` and
|
|
64
83
|
`hoistCmp.withFactory` respectively.
|
|
65
|
-
|
|
66
84
|
* `HoistProps.modelConfig` now references the type declaration of `HoistModel.config`. See
|
|
67
85
|
`PanelModel` and `TabContainerModel` for examples.
|
|
68
|
-
|
|
69
86
|
* The new `SelectOption` type has been made multi-platform and moved to `@xh/hoist/core`.
|
|
70
87
|
|
|
71
88
|
**Note** that we do not intend to make such extensive Typescript changes going forward post-v55.0.
|
|
72
89
|
These changes were deemed critical and worth adjusting in our first typescript update, and before
|
|
73
90
|
typescript has been widely adopted in production Hoist apps.
|
|
74
91
|
|
|
75
|
-
|
|
76
92
|
### ⚙️ Technical
|
|
77
93
|
|
|
78
94
|
* Hoist's `Icon` enumeration has been re-organized slightly to better separate icons that describe
|
|
@@ -13,7 +13,7 @@ import {Icon} from '@xh/hoist/icon';
|
|
|
13
13
|
import {bindable, makeObservable, observable, action} from '@xh/hoist/mobx';
|
|
14
14
|
import {pluralize} from '@xh/hoist/utils/js';
|
|
15
15
|
import {hbox} from '@xh/hoist/cmp/layout';
|
|
16
|
-
import {
|
|
16
|
+
import {isEqual, isString, isNil, omit, remove, trimEnd} from 'lodash';
|
|
17
17
|
import {hspacer} from '../../cmp/layout';
|
|
18
18
|
|
|
19
19
|
import {DifferDetailModel} from './DifferDetailModel';
|
|
@@ -151,7 +151,7 @@ export class DifferModel extends HoistModel {
|
|
|
151
151
|
const resp = await Promise.all([
|
|
152
152
|
XH.fetchJson({url: `${url}/${entityName}s`, loadSpec}),
|
|
153
153
|
this.clipboardContent ?
|
|
154
|
-
Promise.resolve(
|
|
154
|
+
Promise.resolve(structuredClone(this.clipboardContent)) :
|
|
155
155
|
XH.fetchJson({url: `${remoteBaseUrl}${url}/${entityName}s`, loadSpec})
|
|
156
156
|
]);
|
|
157
157
|
this.processResponse(resp);
|
|
@@ -250,8 +250,8 @@ export class DifferModel extends HoistModel {
|
|
|
250
250
|
|
|
251
251
|
rawRecordsAreEqual(local, remote) {
|
|
252
252
|
// cloning to avoid disturbing the source data.
|
|
253
|
-
local =
|
|
254
|
-
remote =
|
|
253
|
+
local = structuredClone(local);
|
|
254
|
+
remote = structuredClone(remote);
|
|
255
255
|
|
|
256
256
|
// For JSON records, parse JSON to do an accurate value compare,
|
|
257
257
|
if (local?.valueType === 'json' && remote?.valueType === 'json') {
|
package/cmp/ag-grid/AgGrid.scss
CHANGED
|
@@ -306,14 +306,6 @@
|
|
|
306
306
|
.ag-cell {
|
|
307
307
|
padding-left: var(--xh-grid-tiny-cell-lr-pad-px);
|
|
308
308
|
padding-right: var(--xh-grid-tiny-cell-lr-pad-px);
|
|
309
|
-
|
|
310
|
-
.xh-check-box {
|
|
311
|
-
padding-top: 1px;
|
|
312
|
-
|
|
313
|
-
.bp4-control-indicator {
|
|
314
|
-
font-size: calc(var(--xh-grid-tiny-font-size-px) + 2px);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
309
|
}
|
|
318
310
|
|
|
319
311
|
.ag-header-cell,
|
|
@@ -377,6 +369,11 @@
|
|
|
377
369
|
color: var(--xh-grid-empty-text-color);
|
|
378
370
|
}
|
|
379
371
|
}
|
|
372
|
+
|
|
373
|
+
// Set Blueprint controls - specifically checkboxes - to match grid size.
|
|
374
|
+
.bp4-control .bp4-control-indicator {
|
|
375
|
+
font-size: 1em;
|
|
376
|
+
}
|
|
380
377
|
}
|
|
381
378
|
|
|
382
379
|
//------------------------
|
|
@@ -9,7 +9,6 @@ import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/
|
|
|
9
9
|
import {throwIf} from '@xh/hoist/utils/js';
|
|
10
10
|
import {
|
|
11
11
|
castArray,
|
|
12
|
-
cloneDeep,
|
|
13
12
|
concat,
|
|
14
13
|
find,
|
|
15
14
|
has,
|
|
@@ -306,7 +305,7 @@ export class AgGridModel extends HoistModel {
|
|
|
306
305
|
setSortState(sortState: AgGridColumnSortState[]) {
|
|
307
306
|
this.throwIfNotReady();
|
|
308
307
|
|
|
309
|
-
const sortedColumnState =
|
|
308
|
+
const sortedColumnState = structuredClone(sortState),
|
|
310
309
|
[primaryColumnState, secondaryColumnState] = partition(sortedColumnState, it => !isArray(it.colId)),
|
|
311
310
|
{agColumnApi: colApi, agApi} = this,
|
|
312
311
|
isPivot = colApi.isPivotMode(),
|
|
@@ -390,7 +389,7 @@ export class AgGridModel extends HoistModel {
|
|
|
390
389
|
const {agColumnApi} = this,
|
|
391
390
|
validColIds = [
|
|
392
391
|
AgGridModel.AUTO_GROUP_COL_ID,
|
|
393
|
-
...agColumnApi.
|
|
392
|
+
...agColumnApi.getColumns().map(it => it.colId)
|
|
394
393
|
];
|
|
395
394
|
|
|
396
395
|
let {isPivot, columns} = colState;
|
package/cmp/chart/Chart.ts
CHANGED
|
@@ -355,7 +355,7 @@ class ChartLocalModel extends HoistModel {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
getThemeConfig() {
|
|
358
|
-
return XH.darkTheme ?
|
|
358
|
+
return XH.darkTheme ? structuredClone(DarkTheme) : structuredClone(LightTheme);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
getModelConfig() {
|
package/cmp/chart/ChartModel.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {HoistModel, PlainObject, Some} from '@xh/hoist/core';
|
|
8
8
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
9
|
-
import {castArray,
|
|
9
|
+
import {castArray, merge} from 'lodash';
|
|
10
10
|
|
|
11
11
|
interface ChartConfig {
|
|
12
12
|
|
|
@@ -84,7 +84,7 @@ export class ChartModel extends HoistModel {
|
|
|
84
84
|
*/
|
|
85
85
|
@action
|
|
86
86
|
updateHighchartsConfig(update: any) {
|
|
87
|
-
this.highchartsConfig = merge(
|
|
87
|
+
this.highchartsConfig = merge(structuredClone(this.highchartsConfig), update);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/** @param series - one or more data series to be charted. */
|
|
@@ -29,7 +29,6 @@ import {throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
|
29
29
|
import {createObservableRef} from '@xh/hoist/utils/react';
|
|
30
30
|
import {ReactNode} from 'react';
|
|
31
31
|
import {
|
|
32
|
-
cloneDeep,
|
|
33
32
|
compact,
|
|
34
33
|
flatMap,
|
|
35
34
|
flatten,
|
|
@@ -374,7 +373,7 @@ export class FilterChooserModel extends HoistModel {
|
|
|
374
373
|
// it's internal `value`. Force synchronise its `value` to our bound `selectValue`
|
|
375
374
|
// to get it back inline. Note we're intentionally not using `setSelectValue()`,
|
|
376
375
|
// which returns early if the actual filter value hasn't changed.
|
|
377
|
-
this.selectValue =
|
|
376
|
+
this.selectValue = structuredClone(this.selectValue);
|
|
378
377
|
});
|
|
379
378
|
}
|
|
380
379
|
|
package/cmp/grid/GridModel.ts
CHANGED
|
@@ -60,7 +60,6 @@ import equal from 'fast-deep-equal';
|
|
|
60
60
|
import {
|
|
61
61
|
castArray,
|
|
62
62
|
clone,
|
|
63
|
-
cloneDeep,
|
|
64
63
|
compact,
|
|
65
64
|
defaults,
|
|
66
65
|
defaultsDeep,
|
|
@@ -1061,7 +1060,7 @@ export class GridModel extends HoistModel {
|
|
|
1061
1060
|
applyColumnStateChanges(colStateChanges: Partial<ColumnState>[]) {
|
|
1062
1061
|
if (isEmpty(colStateChanges)) return;
|
|
1063
1062
|
|
|
1064
|
-
let columnState =
|
|
1063
|
+
let columnState = structuredClone(this.columnState);
|
|
1065
1064
|
|
|
1066
1065
|
throwIf(colStateChanges.some(({colId}) => !find(columnState, {colId})),
|
|
1067
1066
|
'Invalid columns detected in column changes!');
|
|
@@ -7,27 +7,27 @@
|
|
|
7
7
|
import {ColumnRenderer} from '@xh/hoist/cmp/grid';
|
|
8
8
|
import {HoistInputProps} from '@xh/hoist/cmp/input';
|
|
9
9
|
import {PlainObject} from '@xh/hoist/core';
|
|
10
|
+
import {FieldFilterOperator, parseFilter, View} from '@xh/hoist/data';
|
|
10
11
|
import {
|
|
11
12
|
BaseFilterFieldSpec,
|
|
12
13
|
BaseFilterFieldSpecConfig
|
|
13
14
|
} from '@xh/hoist/data/filter/BaseFilterFieldSpec';
|
|
14
|
-
import {FieldFilterOperator, parseFilter, View} from '@xh/hoist/data';
|
|
15
15
|
import {castArray, compact, flatten, isDate, isEmpty, uniqBy} from 'lodash';
|
|
16
16
|
import {GridFilterModel} from './GridFilterModel';
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
export interface GridFilterFieldSpecConfig extends BaseFilterFieldSpecConfig {
|
|
20
|
-
/** GridFilterModel instance
|
|
19
|
+
/** GridFilterModel instance owning this fieldSpec. */
|
|
21
20
|
filterModel?: GridFilterModel;
|
|
21
|
+
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Function returning a formatted string for each value in this values filter display.
|
|
24
24
|
* If not provided, the Column's renderer will be used.
|
|
25
25
|
*/
|
|
26
26
|
renderer?: ColumnRenderer;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* Props to pass through to the HoistInput components used on the custom filter tab.
|
|
30
|
-
* that the HoistInput component used is decided by fieldType.
|
|
29
|
+
* Props to pass through to the HoistInput components used on the custom filter tab.
|
|
30
|
+
* Note that the HoistInput component used is decided by fieldType.
|
|
31
31
|
*/
|
|
32
32
|
inputProps?: HoistInputProps;
|
|
33
33
|
|
|
@@ -36,8 +36,8 @@ export interface GridFilterFieldSpecConfig extends BaseFilterFieldSpecConfig {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Apps should NOT instantiate this class directly.
|
|
40
|
-
* to the GridModel's `filterModel`
|
|
39
|
+
* Apps should NOT instantiate this class directly.
|
|
40
|
+
* Instead, provide a config for this object to the GridModel's `filterModel` config.
|
|
41
41
|
*/
|
|
42
42
|
export class GridFilterFieldSpec extends BaseFilterFieldSpec {
|
|
43
43
|
|
|
@@ -53,7 +53,7 @@ export class GridFilterFieldSpec extends BaseFilterFieldSpec {
|
|
|
53
53
|
inputProps,
|
|
54
54
|
defaultOp,
|
|
55
55
|
...rest
|
|
56
|
-
}:GridFilterFieldSpecConfig) {
|
|
56
|
+
}: GridFilterFieldSpecConfig) {
|
|
57
57
|
super(rest);
|
|
58
58
|
|
|
59
59
|
this.filterModel = filterModel;
|
|
@@ -106,9 +106,7 @@ export class GridFilterFieldSpec extends BaseFilterFieldSpec {
|
|
|
106
106
|
this.valueCount = allValues.length;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
* Recursively modify a Filter|CompoundFilter to remove all FieldFilters that reference this column
|
|
111
|
-
*/
|
|
109
|
+
// Recursively modify a Filter|CompoundFilter to remove all FieldFilters referencing this column
|
|
112
110
|
cleanFilter(filter) {
|
|
113
111
|
if (!filter) return filter;
|
|
114
112
|
|
|
@@ -5,15 +5,22 @@
|
|
|
5
5
|
* Copyright © 2022 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {GridFilterModelConfig} from '@xh/hoist/cmp/grid';
|
|
8
|
+
import {GridFilterFieldSpec, GridFilterModelConfig} from '@xh/hoist/cmp/grid';
|
|
9
9
|
import {HoistModel, managed} from '@xh/hoist/core';
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import {
|
|
11
|
+
CompoundFilter,
|
|
12
|
+
FieldFilter,
|
|
13
|
+
Filter,
|
|
14
|
+
FilterLike,
|
|
15
|
+
flattenFilter,
|
|
16
|
+
Store,
|
|
17
|
+
View,
|
|
18
|
+
withFilterByField,
|
|
19
|
+
withFilterByTypes
|
|
20
|
+
} from '@xh/hoist/data';
|
|
21
|
+
import {action, bindable, makeObservable, observable} from '@xh/hoist/mobx';
|
|
13
22
|
import {wait} from '@xh/hoist/promise';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
import {GridFilterFieldSpec} from './GridFilterFieldSpec';
|
|
23
|
+
import {castArray, compact, every, find, isNil, isString, uniq} from 'lodash';
|
|
17
24
|
import {GridModel} from '../GridModel';
|
|
18
25
|
|
|
19
26
|
|
|
@@ -174,4 +181,4 @@ export class GridFilterModel extends HoistModel {
|
|
|
174
181
|
const results = compact(filter.filters.map(it => this.getOuterCompoundFilter(it, field)));
|
|
175
182
|
return results.length === 1 ? results[0] : null;
|
|
176
183
|
}
|
|
177
|
-
}
|
|
184
|
+
}
|
|
@@ -10,7 +10,7 @@ import {action, computed, observable, makeObservable} from '@xh/hoist/mobx';
|
|
|
10
10
|
import {genDisplayName} from '@xh/hoist/data';
|
|
11
11
|
import {throwIf} from '@xh/hoist/utils/js';
|
|
12
12
|
import {createObservableRef} from '@xh/hoist/utils/react';
|
|
13
|
-
import {
|
|
13
|
+
import {difference, isFunction, isArray, isEmpty, isEqual, isString, keys, sortBy} from 'lodash';
|
|
14
14
|
|
|
15
15
|
export interface GroupingChooserConfig {
|
|
16
16
|
/**
|
|
@@ -128,7 +128,7 @@ export class GroupingChooserModel extends HoistModel {
|
|
|
128
128
|
this.persistValue = persistWith.persistValue ?? true;
|
|
129
129
|
this.persistFavorites = persistWith.persistFavorites ?? true;
|
|
130
130
|
|
|
131
|
-
const state =
|
|
131
|
+
const state = structuredClone(this.provider.read());
|
|
132
132
|
if (this.persistValue && state?.value && this.validateValue(state?.value)) {
|
|
133
133
|
value = state.value;
|
|
134
134
|
}
|
package/cmp/tab/TabModel.ts
CHANGED
|
@@ -90,7 +90,7 @@ export class TabModel extends HoistModel {
|
|
|
90
90
|
@bindable.ref icon: ReactElement;
|
|
91
91
|
@bindable.ref tooltip: ReactNode;
|
|
92
92
|
@observable disabled: boolean;
|
|
93
|
-
excludeFromSwitcher: boolean;
|
|
93
|
+
@bindable excludeFromSwitcher: boolean;
|
|
94
94
|
showRemoveAction: boolean;
|
|
95
95
|
content: Content;
|
|
96
96
|
|
package/core/HoistBase.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {XH, PersistenceProvider, PersistOptions, DebounceSpec} from './';
|
|
|
8
8
|
import {throwIf, getOrCreate} from '@xh/hoist/utils/js';
|
|
9
9
|
import {
|
|
10
10
|
debounce as lodashDebounce,
|
|
11
|
-
cloneDeep,
|
|
12
11
|
isFunction,
|
|
13
12
|
isNil,
|
|
14
13
|
isNumber,
|
|
@@ -220,7 +219,7 @@ export abstract class HoistBase {
|
|
|
220
219
|
provider = this.markManaged(PersistenceProvider.create(persistWith)),
|
|
221
220
|
providerState = provider.read();
|
|
222
221
|
if (!isUndefined(providerState)) {
|
|
223
|
-
runInAction(() => this[property] =
|
|
222
|
+
runInAction(() => this[property] = structuredClone(providerState));
|
|
224
223
|
}
|
|
225
224
|
this.addReaction({
|
|
226
225
|
track: () => this[property],
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {PersistenceProvider, PersistOptions, HoistBaseClass} from './';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {isUndefined} from 'lodash';
|
|
10
10
|
import {wait} from '../promise';
|
|
11
11
|
import {throwIf} from '../utils/js';
|
|
12
12
|
|
|
@@ -75,7 +75,7 @@ function createPersistDescriptor(target: HoistBaseClass, property: string, descr
|
|
|
75
75
|
try {
|
|
76
76
|
const persistWith = {path: property, ...this.persistWith, ...options},
|
|
77
77
|
provider = this.markManaged(PersistenceProvider.create(persistWith));
|
|
78
|
-
providerState =
|
|
78
|
+
providerState = structuredClone(provider.read());
|
|
79
79
|
wait().then(() => {
|
|
80
80
|
this.addReaction({
|
|
81
81
|
track: () => this[property],
|
package/core/HoistService.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2022 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {HoistBase, managed, LoadSupport, LoadSpec, Loadable} from './';
|
|
7
|
+
import {HoistBase, managed, LoadSupport, LoadSpec, Loadable, PlainObject} from './';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Core superclass for Services in Hoist. Services are special classes used in both Hoist and
|
|
@@ -64,12 +64,12 @@ export class HoistService extends HoistBase implements Loadable {
|
|
|
64
64
|
@managed
|
|
65
65
|
loadSupport: LoadSupport;
|
|
66
66
|
|
|
67
|
-
get loadModel()
|
|
68
|
-
get lastLoadRequested()
|
|
69
|
-
get lastLoadCompleted()
|
|
70
|
-
get lastLoadException()
|
|
71
|
-
async refreshAsync(meta?:
|
|
72
|
-
async autoRefreshAsync(meta?:
|
|
67
|
+
get loadModel() {return this.loadSupport?.loadModel}
|
|
68
|
+
get lastLoadRequested() {return this.loadSupport?.lastLoadRequested}
|
|
69
|
+
get lastLoadCompleted() {return this.loadSupport?.lastLoadCompleted}
|
|
70
|
+
get lastLoadException() {return this.loadSupport?.lastLoadException}
|
|
71
|
+
async refreshAsync(meta?: PlainObject) {return this.loadSupport?.refreshAsync(meta)}
|
|
72
|
+
async autoRefreshAsync(meta?: PlainObject) {return this.loadSupport?.autoRefreshAsync(meta)}
|
|
73
73
|
async doLoadAsync(loadSpec: LoadSpec) {}
|
|
74
74
|
async loadAsync(loadSpec?: LoadSpec|Partial<LoadSpec>) {
|
|
75
75
|
return this.loadSupport?.loadAsync(loadSpec);
|
|
@@ -37,10 +37,9 @@ export interface ExceptionHandlerOptions {
|
|
|
37
37
|
showAlert?: boolean;
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* If `showAlert`, which type of alert to display.
|
|
41
|
-
* Valid options are 'dialog'|'toast'. Defaults to ExceptionHandler.ALERT_TYPE.
|
|
40
|
+
* If `showAlert`, which type of alert to display. Defaults to ExceptionHandler.ALERT_TYPE.
|
|
42
41
|
*/
|
|
43
|
-
alertType?:
|
|
42
|
+
alertType?: 'dialog'|'toast';
|
|
44
43
|
|
|
45
44
|
/**
|
|
46
45
|
* Force user to fully refresh the app in order to dismiss - default false, excepting
|
|
@@ -80,9 +79,8 @@ export class ExceptionHandler {
|
|
|
80
79
|
|
|
81
80
|
/**
|
|
82
81
|
* Default type of alert to use to display exceptions with `showAlert`.
|
|
83
|
-
* Valid options are 'dialog'|'toast'.
|
|
84
82
|
*/
|
|
85
|
-
static ALERT_TYPE:
|
|
83
|
+
static ALERT_TYPE: 'dialog'|'toast' = 'dialog';
|
|
86
84
|
|
|
87
85
|
/**
|
|
88
86
|
* Default props provided to toast, when alert type is 'toast'
|
package/core/load/LoadSpec.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {LoadSupport} from './';
|
|
9
|
+
import {PlainObject} from '../types/Types';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Object describing a load/refresh request in Hoist.
|
|
@@ -46,7 +47,7 @@ export class LoadSpec {
|
|
|
46
47
|
owner: LoadSupport;
|
|
47
48
|
|
|
48
49
|
/** Application specific information about the load request */
|
|
49
|
-
meta:
|
|
50
|
+
meta: PlainObject;
|
|
50
51
|
|
|
51
52
|
/** True if a more recent request to load this object's owner has *started*. */
|
|
52
53
|
get isStale(): boolean {
|
package/core/model/HoistModel.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {forOwn, has, isFunction} from 'lodash';
|
|
8
8
|
import {warnIf} from '@xh/hoist/utils/js';
|
|
9
|
-
import {DefaultHoistProps, HoistBase, managed} from '../';
|
|
9
|
+
import {DefaultHoistProps, HoistBase, managed, PlainObject} from '../';
|
|
10
10
|
import {ModelSelector} from './';
|
|
11
11
|
import {LoadSupport, LoadSpec, Loadable} from '../load';
|
|
12
12
|
import {observable, action, makeObservable} from '@xh/hoist/mobx';
|
|
@@ -94,12 +94,12 @@ export abstract class HoistModel extends HoistBase implements Loadable {
|
|
|
94
94
|
@managed
|
|
95
95
|
loadSupport: LoadSupport;
|
|
96
96
|
|
|
97
|
-
get loadModel()
|
|
98
|
-
get lastLoadRequested()
|
|
99
|
-
get lastLoadCompleted()
|
|
100
|
-
get lastLoadException()
|
|
101
|
-
async refreshAsync(meta?:
|
|
102
|
-
async autoRefreshAsync(meta?:
|
|
97
|
+
get loadModel() {return this.loadSupport?.loadModel}
|
|
98
|
+
get lastLoadRequested() {return this.loadSupport?.lastLoadRequested}
|
|
99
|
+
get lastLoadCompleted() {return this.loadSupport?.lastLoadCompleted}
|
|
100
|
+
get lastLoadException() {return this.loadSupport?.lastLoadException}
|
|
101
|
+
async refreshAsync(meta?: PlainObject) {return this.loadSupport?.refreshAsync(meta)}
|
|
102
|
+
async autoRefreshAsync(meta?: PlainObject) {return this.loadSupport?.autoRefreshAsync(meta)}
|
|
103
103
|
async doLoadAsync(loadSpec: LoadSpec) {}
|
|
104
104
|
async loadAsync(loadSpec?: LoadSpec|Partial<LoadSpec>) {
|
|
105
105
|
return this.loadSupport?.loadAsync(loadSpec);
|
|
@@ -9,7 +9,6 @@ import {DebounceSpec, XH} from '../';
|
|
|
9
9
|
import {LocalStorageProvider, PrefProvider, DashViewProvider, CustomProvider, PersistOptions} from './';
|
|
10
10
|
import {
|
|
11
11
|
isUndefined,
|
|
12
|
-
cloneDeep,
|
|
13
12
|
get,
|
|
14
13
|
set,
|
|
15
14
|
unset,
|
|
@@ -96,7 +95,7 @@ export class PersistenceProvider {
|
|
|
96
95
|
* Clear any state saved by this object at a path
|
|
97
96
|
*/
|
|
98
97
|
clear(path: string = this.path) {
|
|
99
|
-
const obj =
|
|
98
|
+
const obj = structuredClone(this.readRaw());
|
|
100
99
|
unset(obj, this.path);
|
|
101
100
|
this.writeRaw(obj);
|
|
102
101
|
}
|
|
@@ -112,7 +111,7 @@ export class PersistenceProvider {
|
|
|
112
111
|
// Implementation
|
|
113
112
|
//----------------
|
|
114
113
|
protected writeInternal(data: object) {
|
|
115
|
-
const obj =
|
|
114
|
+
const obj = structuredClone(this.readRaw());
|
|
116
115
|
set(obj, this.path, data);
|
|
117
116
|
this.writeRaw(obj);
|
|
118
117
|
}
|
package/data/cube/View.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Cube,
|
|
11
11
|
CubeField,
|
|
12
12
|
Filter,
|
|
13
|
+
FilterLike,
|
|
13
14
|
Query,
|
|
14
15
|
QueryConfig,
|
|
15
16
|
Store,
|
|
@@ -17,15 +18,14 @@ import {
|
|
|
17
18
|
StoreRecordId
|
|
18
19
|
} from '@xh/hoist/data';
|
|
19
20
|
import {action, makeObservable, observable} from '@xh/hoist/mobx';
|
|
20
|
-
import {throwIf, logWithDebug} from '@xh/hoist/utils/js';
|
|
21
21
|
import {shallowEqualArrays} from '@xh/hoist/utils/impl';
|
|
22
|
-
import {
|
|
22
|
+
import {logWithDebug, throwIf} from '@xh/hoist/utils/js';
|
|
23
|
+
import {castArray, find, forEach, groupBy, isEmpty, isNil, map} from 'lodash';
|
|
23
24
|
import {AggregationContext} from './aggregate/AggregationContext';
|
|
24
|
-
|
|
25
25
|
import {AggregateRow} from './row/AggregateRow';
|
|
26
|
+
import {BaseRow} from './row/BaseRow';
|
|
26
27
|
import {BucketRow} from './row/BucketRow';
|
|
27
28
|
import {LeafRow} from './row/LeafRow';
|
|
28
|
-
import {BaseRow} from './row/BaseRow';
|
|
29
29
|
|
|
30
30
|
export interface ViewConfig {
|
|
31
31
|
/** Query to be used to construct this view. */
|
|
@@ -177,7 +177,7 @@ export class View extends HoistBase {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
/** Update the filter on the current Query.*/
|
|
180
|
-
setFilter(filter:
|
|
180
|
+
setFilter(filter: FilterLike) {
|
|
181
181
|
this.updateQuery({filter});
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -22,7 +22,6 @@ import {debounced, ensureUniqueBy, throwIf} from '@xh/hoist/utils/js';
|
|
|
22
22
|
import {createObservableRef} from '@xh/hoist/utils/react';
|
|
23
23
|
import {isOmitted} from '@xh/hoist/utils/impl';
|
|
24
24
|
import {
|
|
25
|
-
cloneDeep,
|
|
26
25
|
defaultsDeep,
|
|
27
26
|
find,
|
|
28
27
|
isFinite,
|
|
@@ -540,7 +539,7 @@ export class DashContainerModel extends DashModel<DashContainerViewSpec, DashVie
|
|
|
540
539
|
private createGoldenLayout(containerEl: HTMLElement, state: any): GoldenLayout {
|
|
541
540
|
const {viewSpecs} = this,
|
|
542
541
|
ret = new GoldenLayout({
|
|
543
|
-
content: convertStateToGL(
|
|
542
|
+
content: convertStateToGL(structuredClone(state), this),
|
|
544
543
|
settings: {
|
|
545
544
|
// Remove icons by default
|
|
546
545
|
showPopoutIcon: false,
|
|
@@ -70,24 +70,27 @@ const content = hoistCmp.factory({
|
|
|
70
70
|
const bbar = hoistCmp.factory<ColumnHeaderFilterModel>({
|
|
71
71
|
render({model}) {
|
|
72
72
|
const {commitOnChange} = model;
|
|
73
|
-
return toolbar(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
return toolbar({
|
|
74
|
+
compact: true,
|
|
75
|
+
items: [
|
|
76
|
+
filler(),
|
|
77
|
+
button({
|
|
78
|
+
icon: Icon.delete(),
|
|
79
|
+
text: 'Clear Filter',
|
|
80
|
+
intent: 'danger',
|
|
81
|
+
disabled: !model.hasFilter,
|
|
82
|
+
onClick: () => model.clear()
|
|
83
|
+
}),
|
|
84
|
+
button({
|
|
85
|
+
omit: commitOnChange,
|
|
86
|
+
icon: Icon.check(),
|
|
87
|
+
text: 'Apply Filter',
|
|
88
|
+
intent: 'success',
|
|
89
|
+
disabled: !model.hasFilter && !model.hasPendingFilter,
|
|
90
|
+
onClick: () => model.commit()
|
|
91
|
+
})
|
|
92
|
+
]
|
|
93
|
+
});
|
|
91
94
|
}
|
|
92
95
|
});
|
|
93
96
|
|
|
@@ -24,4 +24,23 @@
|
|
|
24
24
|
margin-top: var(--xh-pad-px);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
// Fix up alignment on first checkbox column.
|
|
29
|
+
.ag-header-cell.xh-column-header-align-center {
|
|
30
|
+
padding: 0 !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ag-pinned-left-header,
|
|
34
|
+
.ag-cell.ag-cell-last-left-pinned {
|
|
35
|
+
.bp4-control.bp4-inline {
|
|
36
|
+
margin-right: -8px;
|
|
37
|
+
|
|
38
|
+
input {
|
|
39
|
+
margin: 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Less obvious pinning for the checkbox column.
|
|
44
|
+
border-right: none !important;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -105,6 +105,12 @@ export class ValuesTabModel extends HoistModel {
|
|
|
105
105
|
without(this.pendingValues, ...values);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
toggleAllRecsChecked() {
|
|
109
|
+
const setAllToChecked = !this.allVisibleRecsChecked,
|
|
110
|
+
values = this.gridModel.store.records.map(it => it.get('value'));
|
|
111
|
+
this.setRecsChecked(setAllToChecked, values);
|
|
112
|
+
}
|
|
113
|
+
|
|
108
114
|
//-------------------
|
|
109
115
|
// Implementation
|
|
110
116
|
//-------------------
|
|
@@ -175,13 +181,13 @@ export class ValuesTabModel extends HoistModel {
|
|
|
175
181
|
|
|
176
182
|
private createGridModel() {
|
|
177
183
|
const {BLANK_PLACEHOLDER} = GridFilterModel,
|
|
178
|
-
{
|
|
179
|
-
{fieldType} =
|
|
180
|
-
renderer =
|
|
184
|
+
{headerFilterModel, fieldSpec} = this,
|
|
185
|
+
{fieldType} = headerFilterModel,
|
|
186
|
+
renderer = fieldSpec.renderer ?? (fieldType !== 'tags' ? this.headerFilterModel.column.renderer : null);
|
|
181
187
|
|
|
182
188
|
return new GridModel({
|
|
183
189
|
store: {
|
|
184
|
-
idSpec: (raw) =>
|
|
190
|
+
idSpec: (raw) => fieldSpec.getUniqueValue(raw.value).toString(),
|
|
185
191
|
fields: [
|
|
186
192
|
{name: 'value', type: 'auto'},
|
|
187
193
|
{name: 'isChecked', type: 'bool'}
|
|
@@ -190,7 +196,10 @@ export class ValuesTabModel extends HoistModel {
|
|
|
190
196
|
selModel: 'disabled',
|
|
191
197
|
emptyText: 'No records found...',
|
|
192
198
|
contextMenu: null,
|
|
193
|
-
|
|
199
|
+
// Autosize enabled to ensure that long values don't get clipped and user can scroll
|
|
200
|
+
// right if necessary to view full string. For longer strings that differ only in their
|
|
201
|
+
// endings this is important - e.g. instrument or contract names ending in a date.
|
|
202
|
+
autosizeOptions: {mode: 'managed'},
|
|
194
203
|
sizingMode: 'compact',
|
|
195
204
|
stripeRows: false,
|
|
196
205
|
sortBy: 'value',
|
|
@@ -202,16 +211,18 @@ export class ValuesTabModel extends HoistModel {
|
|
|
202
211
|
{
|
|
203
212
|
field: 'isChecked',
|
|
204
213
|
headerName: ({gridModel}) => {
|
|
205
|
-
const {store} = gridModel,
|
|
206
|
-
values = store.records.map(it => it.get('value'));
|
|
207
214
|
return checkbox({
|
|
208
|
-
disabled: store.empty,
|
|
215
|
+
disabled: gridModel.store.empty,
|
|
209
216
|
displayUnsetState: true,
|
|
210
217
|
value: this.allVisibleRecsChecked,
|
|
211
|
-
onChange: () => this.
|
|
218
|
+
onChange: () => this.toggleAllRecsChecked()
|
|
212
219
|
});
|
|
213
220
|
},
|
|
214
|
-
width:
|
|
221
|
+
width: 28,
|
|
222
|
+
autosizable: false,
|
|
223
|
+
pinned: true,
|
|
224
|
+
align: 'center',
|
|
225
|
+
headerAlign: 'center',
|
|
215
226
|
rendererIsComplex: true,
|
|
216
227
|
renderer: (v, {record}) => {
|
|
217
228
|
return checkbox({
|
|
@@ -223,10 +234,8 @@ export class ValuesTabModel extends HoistModel {
|
|
|
223
234
|
},
|
|
224
235
|
{
|
|
225
236
|
field: 'value',
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
align,
|
|
229
|
-
headerAlign,
|
|
237
|
+
displayName: '(Select All)',
|
|
238
|
+
align: 'left',
|
|
230
239
|
comparator: (v1, v2, sortDir, abs, {defaultComparator}) => {
|
|
231
240
|
const mul = sortDir === 'desc' ? -1 : 1;
|
|
232
241
|
if (v1 === BLANK_PLACEHOLDER) return 1 * mul;
|
|
@@ -8,7 +8,6 @@ import {grid, GridProps} from '@xh/hoist/cmp/grid';
|
|
|
8
8
|
import {hframe, vbox} from '@xh/hoist/cmp/layout';
|
|
9
9
|
import {BoxProps, hoistCmp, HoistProps, uses} from '@xh/hoist/core';
|
|
10
10
|
import '@xh/hoist/desktop/register';
|
|
11
|
-
import {cloneDeep} from 'lodash';
|
|
12
11
|
import {chooserToolbar} from './impl/ChooserToolbar';
|
|
13
12
|
import {description} from './impl/Description';
|
|
14
13
|
import './LeftRightChooser.scss';
|
|
@@ -37,8 +36,8 @@ export const [LeftRightChooser, leftRightChooser] = hoistCmp.withFactory<LeftRig
|
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
},
|
|
40
|
-
leftGridOptions =
|
|
41
|
-
rightGridOptions =
|
|
39
|
+
leftGridOptions = structuredClone(gridOptions),
|
|
40
|
+
rightGridOptions = structuredClone(gridOptions);
|
|
42
41
|
|
|
43
42
|
if (!leftGroupingExpanded) leftGridOptions.agOptions.groupDefaultExpanded = 0;
|
|
44
43
|
if (!rightGroupingExpanded) rightGridOptions.agOptions.groupDefaultExpanded = 0;
|
|
@@ -155,8 +155,8 @@ export class DraggerModel extends HoistModel {
|
|
|
155
155
|
|
|
156
156
|
private getDraggableSplitter() {
|
|
157
157
|
// clone .xh-resizable-splitter to get its styling
|
|
158
|
-
const splitter = this.
|
|
159
|
-
ret = splitter.cloneNode();
|
|
158
|
+
const splitter = this.panelModel.splitterRef.current,
|
|
159
|
+
ret = splitter.cloneNode() as HTMLDivElement;
|
|
160
160
|
|
|
161
161
|
ret.style.position = 'absolute';
|
|
162
162
|
ret.style.display = 'none'; // display = none needed to prevent flash
|
|
@@ -13,7 +13,7 @@ import {numberRenderer} from '@xh/hoist/format';
|
|
|
13
13
|
import {action, bindable, computed, makeObservable, observable} from '@xh/hoist/mobx';
|
|
14
14
|
import {throwIf, withDefault} from '@xh/hoist/utils/js';
|
|
15
15
|
import {ReactNode} from 'react';
|
|
16
|
-
import {
|
|
16
|
+
import {get, isEmpty, isFinite, max, set, sortBy, sumBy, unset} from 'lodash';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Core Model for a TreeMap.
|
|
@@ -442,7 +442,7 @@ export class TreeMapModel extends HoistModel {
|
|
|
442
442
|
|
|
443
443
|
toggleNodeExpanded(treePath) {
|
|
444
444
|
const {gridModel} = this,
|
|
445
|
-
expandState =
|
|
445
|
+
expandState = structuredClone(gridModel.expandState);
|
|
446
446
|
|
|
447
447
|
if (get(expandState, treePath)) {
|
|
448
448
|
unset(expandState, treePath);
|
package/package.json
CHANGED
package/svc/PrefService.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {HoistService, XH} from '@xh/hoist/core';
|
|
8
8
|
import {SECONDS} from '@xh/hoist/utils/datetime';
|
|
9
9
|
import {deepFreeze, throwIf} from '@xh/hoist/utils/js';
|
|
10
|
-
import {
|
|
10
|
+
import {debounce, forEach, isEmpty, isEqual, isNil, pickBy} from 'lodash';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Service to read and set user-specific preference values.
|
|
@@ -92,7 +92,7 @@ export class PrefService extends HoistService {
|
|
|
92
92
|
if (isEqual(oldValue, value)) return;
|
|
93
93
|
|
|
94
94
|
// Change local value to sanitized copy and fire.
|
|
95
|
-
value = deepFreeze(
|
|
95
|
+
value = deepFreeze(structuredClone(value));
|
|
96
96
|
this._data[key].value = value;
|
|
97
97
|
|
|
98
98
|
// Schedule serialization to storage
|
|
@@ -198,7 +198,7 @@ export class PrefService extends HoistService {
|
|
|
198
198
|
for (let key in data) {
|
|
199
199
|
if (data[key].local) {
|
|
200
200
|
data[key].value = !isNil(localPrefs[key]) ?
|
|
201
|
-
deepFreeze(
|
|
201
|
+
deepFreeze(structuredClone(localPrefs[key])) :
|
|
202
202
|
data[key].defaultValue;
|
|
203
203
|
}
|
|
204
204
|
}
|