@xh/hoist 74.0.0-SNAPSHOT.1748626008777 → 74.0.0-SNAPSHOT.1748890930922

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 CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## 74.0.0-SNAPSHOT - unreleased
4
4
 
5
+ ### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW - minor change to ViewManagerModel)
6
+
7
+ * Removed `ViewManagerModel.settleTime`. Now set via individual `PersistOptions.settleTime` instead.
8
+
9
+ ### 🎁 New Features
10
+ * Added `ViewManagerModel.preserveUnsavedChanges` flag to opt-out of that behaviour.
11
+ * Added `PersistOptions.settleTime` to configure time to wait for state to settle before persisting.
12
+ * Support for gridcolumn level `onCellClicked` events.
13
+
14
+ ### 🐞 Bug Fixes
15
+ * Improved `ViewManagerModel.settleTime` by delegating to individual `PersistenceProviders`.
16
+ * Fixed bug where grid column state could become unintentionally dirty when columns were hidden.
17
+
5
18
  ## v73.0.1 - 2025-05-19
6
19
 
7
20
  ### 🐞 Bug Fixes
@@ -120,7 +120,9 @@ export interface GridConfig {
120
120
  */
121
121
  onRowDoubleClicked?: (e: RowDoubleClickedEvent) => void;
122
122
  /**
123
- * Callback when a cell is clicked.
123
+ * Callback when any cell on the grid is clicked - inspect the event to determine the column.
124
+ * Note that {@link ColumnSpec.onCellClicked} is a more targeted handler scoped to a single
125
+ * column, which might be more convenient when clicks on only one column are of interest.
124
126
  */
125
127
  onCellClicked?: (e: CellClickedEvent) => void;
126
128
  /**
@@ -4,6 +4,7 @@ import { FunctionComponent, ReactNode } from 'react';
4
4
  import { GridModel } from '../GridModel';
5
5
  import { ColumnCellClassFn, ColumnCellClassRuleFn, ColumnComparator, ColumnEditableFn, ColumnEditorFn, ColumnEditorProps, ColumnExcelFormatFn, ColumnExportValueFn, ColumnGetValueFn, ColumnHeaderClassFn, ColumnHeaderNameFn, ColumnRenderer, ColumnSetValueFn, ColumnSortSpec, ColumnSortValueFn, ColumnTooltipFn } from '../Types';
6
6
  import type { ColDef } from '@xh/hoist/kit/ag-grid';
7
+ import { CellClickedEvent } from '@ag-grid-community/core';
7
8
  export interface ColumnSpec {
8
9
  /**
9
10
  * Name of data store field to display within the column, or object containing properties
@@ -248,6 +249,11 @@ export interface ColumnSpec {
248
249
  * many rows + multiple actions per row. Defaults to false;
249
250
  */
250
251
  actionsShowOnHoverOnly?: boolean;
252
+ /**
253
+ * Callback when a cell within this column clicked.
254
+ * See also {@link GridConfig.onCellClicked}, called when any cell within the grid is clicked.
255
+ */
256
+ onCellClicked?: (e: CellClickedEvent) => void;
251
257
  /**
252
258
  * "escape hatch" object to pass directly to Ag-Grid for desktop implementations. Note these
253
259
  * options may be used / overwritten by the framework itself, and are not all guaranteed to be
@@ -337,6 +343,7 @@ export declare class Column {
337
343
  actionsShowOnHoverOnly?: boolean;
338
344
  fieldSpec: FieldSpec;
339
345
  omit: Thunkable<boolean>;
346
+ onCellClicked?: (e: CellClickedEvent) => void;
340
347
  gridModel: GridModel;
341
348
  agOptions: ColDef;
342
349
  appData: PlainObject;
@@ -43,6 +43,11 @@ export interface ViewManagerConfig {
43
43
  * True (default) to allow users to share their views with other users.
44
44
  */
45
45
  enableSharing?: boolean;
46
+ /**
47
+ * True (default) to save pending state to SessionStorage so that it can be restored across
48
+ * browser refreshes. Unlike auto-save, this does not write to the database.
49
+ */
50
+ preserveUnsavedChanges?: boolean;
46
51
  /**
47
52
  * Function to determine the initial view for a user, when no view has already been persisted.
48
53
  * Will be passed a list of views available to the current user. Implementations where
@@ -52,13 +57,6 @@ export interface ViewManagerConfig {
52
57
  * Must be set when enableDefault is false.
53
58
  */
54
59
  initialViewSpec?: (views: ViewInfo[]) => ViewInfo;
55
- /**
56
- * Delay (in ms) to wait after state has been set on associated components before listening for
57
- * further state changes. The long default wait 1000ms is intended to avoid a false positive
58
- * dirty indicator when linking to complex components such as dashboards or grids that can
59
- * report immediate changes to state due to internal processing or rendering.
60
- */
61
- settleTime?: number;
62
60
  /**
63
61
  * True to allow the user to publish or edit the global views. Apps are expected to
64
62
  * commonly set this based on user roles - e.g. `XH.getUser().hasRole('MANAGE_GRID_VIEWS')`.
@@ -132,8 +130,8 @@ export declare class ViewManagerModel<T = PlainObject> extends HoistModel {
132
130
  readonly enableDefault: boolean;
133
131
  readonly enableGlobal: boolean;
134
132
  readonly enableSharing: boolean;
133
+ readonly preserveUnsavedChanges: boolean;
135
134
  readonly manageGlobal: boolean;
136
- readonly settleTime: number;
137
135
  readonly initialViewSpec: (views: ViewInfo[]) => ViewInfo;
138
136
  /** Current view. Will not include uncommitted changes */
139
137
  view: View<T>;
@@ -161,16 +159,12 @@ export declare class ViewManagerModel<T = PlainObject> extends HoistModel {
161
159
  /** Unsaved changes on the current view.*/
162
160
  private pendingValue;
163
161
  /**
164
- * Array of {@link ViewManagerProvider} instances bound to this model. Providers will
165
- * push themselves onto this array when constructed with a reference to this model. Used to
166
- * proactively push state to the target components when the model's selected `value` changes.
167
- * @internal
162
+ * Array of {@link ViewManagerProvider} instances bound to this model. Used to proactively push
163
+ * state to the target components when the model's selected `value` changes.
168
164
  */
169
- providers: ViewManagerProvider<any>[];
165
+ private providers;
170
166
  /** Data access for persisting views. */
171
167
  private dataAccess;
172
- /** Last time changes were pushed to linked persistence providers */
173
- private lastPushed;
174
168
  get isValueDirty(): boolean;
175
169
  get isViewSavable(): boolean;
176
170
  get isViewAutoSavable(): boolean;
@@ -207,8 +201,18 @@ export declare class ViewManagerModel<T = PlainObject> extends HoistModel {
207
201
  /** Promote a view to global visibility/ownership status. */
208
202
  makeViewGlobalAsync(view: ViewInfo): Promise<View<T>>;
209
203
  deleteViewsAsync(toDelete: ViewInfo[]): Promise<void>;
204
+ /**
205
+ * Called by {@link ViewManagerProvider} to receive state changes from this model.
206
+ * @internal
207
+ */
208
+ registerProvider(provider: ViewManagerProvider<any>): void;
209
+ /**
210
+ * Called by {@link ViewManagerProvider} to stop receiving state changes.
211
+ * @internal
212
+ */
213
+ unregisterProvider(provider: ViewManagerProvider<any>): void;
210
214
  private initAsync;
211
- private pendingValueReaction;
215
+ private unsavedChangesReaction;
212
216
  private autoSaveReaction;
213
217
  private stateReactions;
214
218
  private loadViewAsync;
@@ -11,6 +11,10 @@ export interface PersistOptions {
11
11
  path?: string;
12
12
  /** Debounce interval in ms, or a lodash debounce config. */
13
13
  debounce?: DebounceSpec;
14
+ /**
15
+ * Delay (in ms) to wait after state has been read before listening for further state changes.
16
+ */
17
+ settleTime?: number;
14
18
  /**
15
19
  * Type of PersistenceProvider to create. Specify as one of the built-in string types,
16
20
  * or a subclass of PersistenceProvider.
@@ -26,9 +26,12 @@ export declare abstract class PersistenceProvider<S = any> {
26
26
  readonly path: string;
27
27
  readonly debounce: DebounceSpec;
28
28
  readonly owner: HoistBase;
29
+ readonly settleTime: number;
29
30
  protected target: Persistable<S>;
30
31
  protected defaultState: PersistableState<S>;
31
32
  private disposer;
33
+ private lastReadState;
34
+ private lastReadTime;
32
35
  /**
33
36
  * Construct an instance of this class.
34
37
  *
@@ -255,7 +255,9 @@ export interface GridConfig {
255
255
  onRowDoubleClicked?: (e: RowDoubleClickedEvent) => void;
256
256
 
257
257
  /**
258
- * Callback when a cell is clicked.
258
+ * Callback when any cell on the grid is clicked - inspect the event to determine the column.
259
+ * Note that {@link ColumnSpec.onCellClicked} is a more targeted handler scoped to a single
260
+ * column, which might be more convenient when clicks on only one column are of interest.
259
261
  */
260
262
  onCellClicked?: (e: CellClickedEvent) => void;
261
263
 
@@ -71,6 +71,7 @@ import type {
71
71
  ValueGetterParams,
72
72
  ValueSetterParams
73
73
  } from '@xh/hoist/kit/ag-grid';
74
+ import {CellClickedEvent} from '@ag-grid-community/core';
74
75
 
75
76
  export interface ColumnSpec {
76
77
  /**
@@ -374,6 +375,12 @@ export interface ColumnSpec {
374
375
  */
375
376
  actionsShowOnHoverOnly?: boolean;
376
377
 
378
+ /**
379
+ * Callback when a cell within this column clicked.
380
+ * See also {@link GridConfig.onCellClicked}, called when any cell within the grid is clicked.
381
+ */
382
+ onCellClicked?: (e: CellClickedEvent) => void;
383
+
377
384
  /**
378
385
  * "escape hatch" object to pass directly to Ag-Grid for desktop implementations. Note these
379
386
  * options may be used / overwritten by the framework itself, and are not all guaranteed to be
@@ -479,6 +486,7 @@ export class Column {
479
486
  actionsShowOnHoverOnly?: boolean;
480
487
  fieldSpec: FieldSpec;
481
488
  omit: Thunkable<boolean>;
489
+ onCellClicked?: (e: CellClickedEvent) => void;
482
490
 
483
491
  gridModel: GridModel;
484
492
  agOptions: ColDef;
@@ -551,6 +559,7 @@ export class Column {
551
559
  actionsShowOnHoverOnly,
552
560
  actions,
553
561
  omit,
562
+ onCellClicked,
554
563
  agOptions,
555
564
  appData,
556
565
  ...rest
@@ -657,6 +666,7 @@ export class Column {
657
666
 
658
667
  this.actions = actions;
659
668
  this.actionsShowOnHoverOnly = actionsShowOnHoverOnly ?? false;
669
+ this.onCellClicked = onCellClicked;
660
670
 
661
671
  this.gridModel = gridModel;
662
672
  this.agOptions = agOptions ? clone(agOptions) : {};
@@ -758,7 +768,8 @@ export class Column {
758
768
  if (event.shiftKey && event.key === 'Enter') return true;
759
769
 
760
770
  return false;
761
- }
771
+ },
772
+ onCellClicked: this.onCellClicked
762
773
  };
763
774
 
764
775
  // We will change this setter as needed to install the renderer in the proper location
@@ -5,9 +5,9 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
  import {PersistableState, PersistenceProvider} from '@xh/hoist/core';
8
- import {isObject} from 'lodash';
8
+ import {isEqual, isObject} from 'lodash';
9
9
  import {GridModel} from '../GridModel';
10
- import {GridModelPersistOptions} from '../Types';
10
+ import {ColumnState, GridModelPersistOptions} from '../Types';
11
11
 
12
12
  /**
13
13
  * Initialize persistence for a {@link GridModel} by applying its `persistWith` config.
@@ -33,7 +33,8 @@ export function initPersist(
33
33
  ...persistWith
34
34
  },
35
35
  target: {
36
- getPersistableState: () => new PersistableState(gridModel.persistableColumnState),
36
+ getPersistableState: () =>
37
+ new PersistableColumnState(gridModel.persistableColumnState),
37
38
  setPersistableState: ({value}) => gridModel.setColumnState(value)
38
39
  },
39
40
  owner: gridModel
@@ -75,3 +76,12 @@ export function initPersist(
75
76
  });
76
77
  }
77
78
  }
79
+
80
+ class PersistableColumnState extends PersistableState<ColumnState[]> {
81
+ override equals(other: PersistableState<ColumnState[]>): boolean {
82
+ return isEqual(
83
+ this.value.filter(it => !it.hidden),
84
+ other.value.filter(it => !it.hidden)
85
+ );
86
+ }
87
+ }
@@ -19,7 +19,7 @@ import type {ViewManagerProvider, ReactionSpec} from '@xh/hoist/core';
19
19
  import {genDisplayName} from '@xh/hoist/data';
20
20
  import {fmtDateTime} from '@xh/hoist/format';
21
21
  import {action, bindable, makeObservable, observable, comparer, runInAction} from '@xh/hoist/mobx';
22
- import {olderThan, SECONDS} from '@xh/hoist/utils/datetime';
22
+ import {SECONDS} from '@xh/hoist/utils/datetime';
23
23
  import {executeIfFunction, pluralize, throwIf} from '@xh/hoist/utils/js';
24
24
  import {find, isEqual, isNil, isNull, isObject, isUndefined, lowerCase, uniqBy} from 'lodash';
25
25
  import {ReactNode} from 'react';
@@ -74,6 +74,12 @@ export interface ViewManagerConfig {
74
74
  */
75
75
  enableSharing?: boolean;
76
76
 
77
+ /**
78
+ * True (default) to save pending state to SessionStorage so that it can be restored across
79
+ * browser refreshes. Unlike auto-save, this does not write to the database.
80
+ */
81
+ preserveUnsavedChanges?: boolean;
82
+
77
83
  /**
78
84
  * Function to determine the initial view for a user, when no view has already been persisted.
79
85
  * Will be passed a list of views available to the current user. Implementations where
@@ -84,14 +90,6 @@ export interface ViewManagerConfig {
84
90
  */
85
91
  initialViewSpec?: (views: ViewInfo[]) => ViewInfo;
86
92
 
87
- /**
88
- * Delay (in ms) to wait after state has been set on associated components before listening for
89
- * further state changes. The long default wait 1000ms is intended to avoid a false positive
90
- * dirty indicator when linking to complex components such as dashboards or grids that can
91
- * report immediate changes to state due to internal processing or rendering.
92
- */
93
- settleTime?: number;
94
-
95
93
  /**
96
94
  * True to allow the user to publish or edit the global views. Apps are expected to
97
95
  * commonly set this based on user roles - e.g. `XH.getUser().hasRole('MANAGE_GRID_VIEWS')`.
@@ -176,8 +174,8 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
176
174
  readonly enableDefault: boolean;
177
175
  readonly enableGlobal: boolean;
178
176
  readonly enableSharing: boolean;
177
+ readonly preserveUnsavedChanges: boolean;
179
178
  readonly manageGlobal: boolean;
180
- readonly settleTime: number;
181
179
  readonly initialViewSpec: (views: ViewInfo[]) => ViewInfo;
182
180
 
183
181
  /** Current view. Will not include uncommitted changes */
@@ -216,19 +214,14 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
216
214
  private pendingValue: PendingValue<T> = null;
217
215
 
218
216
  /**
219
- * Array of {@link ViewManagerProvider} instances bound to this model. Providers will
220
- * push themselves onto this array when constructed with a reference to this model. Used to
221
- * proactively push state to the target components when the model's selected `value` changes.
222
- * @internal
217
+ * Array of {@link ViewManagerProvider} instances bound to this model. Used to proactively push
218
+ * state to the target components when the model's selected `value` changes.
223
219
  */
224
- providers: ViewManagerProvider<any>[] = [];
220
+ private providers: ViewManagerProvider<any>[] = [];
225
221
 
226
222
  /** Data access for persisting views. */
227
223
  private dataAccess: DataAccess<T>;
228
224
 
229
- /** Last time changes were pushed to linked persistence providers */
230
- private lastPushed: number = null;
231
-
232
225
  //---------------
233
226
  // Getters
234
227
  //---------------
@@ -296,7 +289,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
296
289
  enableDefault = true,
297
290
  enableGlobal = true,
298
291
  enableSharing = true,
299
- settleTime = 1000,
292
+ preserveUnsavedChanges = true,
300
293
  initialViewSpec = null
301
294
  }: ViewManagerConfig) {
302
295
  super();
@@ -317,7 +310,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
317
310
  this.enableGlobal = enableGlobal;
318
311
  this.enableSharing = enableSharing;
319
312
  this.enableAutoSave = enableAutoSave;
320
- this.settleTime = settleTime;
313
+ this.preserveUnsavedChanges = preserveUnsavedChanges;
321
314
  this.initialViewSpec = initialViewSpec;
322
315
 
323
316
  this.selectTask = TaskObserver.trackLast({
@@ -421,10 +414,7 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
421
414
 
422
415
  @action
423
416
  setValue(value: Partial<T>) {
424
- const {view, pendingValue, lastPushed, settleTime} = this;
425
- if (!pendingValue && settleTime && !olderThan(lastPushed, settleTime)) {
426
- return;
427
- }
417
+ const {view, pendingValue} = this;
428
418
 
429
419
  value = this.cleanState(value);
430
420
  if (!isEqual(value, view.value)) {
@@ -500,6 +490,25 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
500
490
  if (exception) throw exception;
501
491
  }
502
492
 
493
+ //------------------
494
+ // Internal
495
+ //------------------
496
+ /**
497
+ * Called by {@link ViewManagerProvider} to receive state changes from this model.
498
+ * @internal
499
+ */
500
+ registerProvider(provider: ViewManagerProvider<any>) {
501
+ this.providers.push(provider);
502
+ }
503
+
504
+ /**
505
+ * Called by {@link ViewManagerProvider} to stop receiving state changes.
506
+ * @internal
507
+ */
508
+ unregisterProvider(provider: ViewManagerProvider<any>) {
509
+ this.providers = this.providers.filter(it => it !== provider);
510
+ }
511
+
503
512
  //------------------
504
513
  // Implementation
505
514
  //------------------
@@ -515,7 +524,9 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
515
524
  this.views = views;
516
525
  this.userPinned = state.userPinned;
517
526
  this.autoSave = state.autoSave;
518
- this.pendingValue = XH.sessionStorageService.get(pendingValueStorageKey);
527
+ if (this.preserveUnsavedChanges) {
528
+ this.pendingValue = XH.sessionStorageService.get(pendingValueStorageKey);
529
+ }
519
530
  });
520
531
 
521
532
  // 2) Initialize/choose initial view. Null is ok, and will yield default.
@@ -537,13 +548,13 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
537
548
  }
538
549
 
539
550
  this.addReaction(
540
- this.pendingValueReaction(),
551
+ this.preserveUnsavedChanges ? this.unsavedChangesReaction() : null,
541
552
  this.autoSaveReaction(),
542
553
  ...this.stateReactions(initialState)
543
554
  );
544
555
  }
545
556
 
546
- private pendingValueReaction(): ReactionSpec {
557
+ private unsavedChangesReaction(): ReactionSpec {
547
558
  return {
548
559
  track: () => this.pendingValue,
549
560
  run: v => XH.sessionStorageService.set(this.pendingValueStorageKey, v)
@@ -588,7 +599,6 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
588
599
  .thenAction(latest => {
589
600
  this.setAsView(latest, pendingValue?.token == token ? pendingValue : null);
590
601
  this.providers.forEach(it => it.pushStateToTarget());
591
- this.lastPushed = Date.now();
592
602
  })
593
603
  .linkTo(this.selectTask);
594
604
  }
@@ -622,6 +632,9 @@ export class ViewManagerModel<T = PlainObject> extends HoistModel {
622
632
  if (!view.isDefault) {
623
633
  this.views = uniqBy([view.info, ...this.views], 'token');
624
634
  }
635
+
636
+ // Ensure providers have a clean reference of the current view state.
637
+ this.providers.forEach(it => it.read());
625
638
  }
626
639
 
627
640
  private handleException(e, opts: ExceptionHandlerOptions = {}) {
@@ -28,6 +28,11 @@ export interface PersistOptions {
28
28
  /** Debounce interval in ms, or a lodash debounce config. */
29
29
  debounce?: DebounceSpec;
30
30
 
31
+ /**
32
+ * Delay (in ms) to wait after state has been read before listening for further state changes.
33
+ */
34
+ settleTime?: number;
35
+
31
36
  /**
32
37
  * Type of PersistenceProvider to create. Specify as one of the built-in string types,
33
38
  * or a subclass of PersistenceProvider.
@@ -5,6 +5,7 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
 
8
+ import {olderThan} from '@xh/hoist/utils/datetime';
8
9
  import {logDebug, logError, throwIf} from '@xh/hoist/utils/js';
9
10
  import {
10
11
  cloneDeep,
@@ -59,11 +60,14 @@ export abstract class PersistenceProvider<S = any> {
59
60
  readonly path: string;
60
61
  readonly debounce: DebounceSpec;
61
62
  readonly owner: HoistBase;
63
+ readonly settleTime: number;
62
64
 
63
65
  protected target: Persistable<S>;
64
66
  protected defaultState: PersistableState<S>;
65
67
 
66
68
  private disposer: IReactionDisposer;
69
+ private lastReadState: PersistableState<S>;
70
+ private lastReadTime: number;
67
71
 
68
72
  /**
69
73
  * Construct an instance of this class.
@@ -126,7 +130,10 @@ export abstract class PersistenceProvider<S = any> {
126
130
  read(): PersistableState<S> {
127
131
  const state = get(this.readRaw(), this.path);
128
132
  logDebug(['Reading state', state], this.owner);
129
- return !isUndefined(state) ? new PersistableState(state) : null;
133
+ const ret = !isUndefined(state) ? new PersistableState(state) : null;
134
+ this.lastReadState = ret;
135
+ this.lastReadTime = Date.now();
136
+ return ret;
130
137
  }
131
138
 
132
139
  /** Persist JSON-serializable state to this provider's path. */
@@ -161,11 +168,12 @@ export abstract class PersistenceProvider<S = any> {
161
168
  const {owner, persistOptions} = cfg;
162
169
  this.owner = owner;
163
170
 
164
- const {path, debounce = 250} = persistOptions;
171
+ const {path, debounce = 250, settleTime} = persistOptions;
165
172
  throwIf(!path, 'Path not specified in PersistenceProvider.');
166
173
 
167
174
  this.path = path;
168
175
  this.debounce = debounce;
176
+ this.settleTime = settleTime;
169
177
  this.owner.markManaged(this);
170
178
 
171
179
  if (debounce) {
@@ -187,8 +195,14 @@ export abstract class PersistenceProvider<S = any> {
187
195
  this.disposer = reaction(
188
196
  () => this.target.getPersistableState(),
189
197
  state => {
190
- if (state.equals(this.defaultState)) {
198
+ if (this.settleTime && !olderThan(this.lastReadTime, this.settleTime)) {
199
+ return;
200
+ } else if (state.equals(this.defaultState)) {
191
201
  this.clear();
202
+ } else if (this.lastReadState && state.equals(this.lastReadState)) {
203
+ // If the last read state is equal to the current state, use the last read state
204
+ // to avoid appearing "dirty"
205
+ this.write(this.lastReadState.value);
192
206
  } else {
193
207
  this.write(state.value);
194
208
  }
@@ -6,7 +6,6 @@
6
6
  */
7
7
 
8
8
  import {throwIf} from '@xh/hoist/utils/js';
9
- import {pull} from 'lodash';
10
9
  import {PersistenceProvider, PersistenceProviderConfig} from '../PersistenceProvider';
11
10
  import type {ViewManagerModel} from '@xh/hoist/cmp/viewmanager/ViewManagerModel';
12
11
 
@@ -18,7 +17,7 @@ export class ViewManagerProvider<S> extends PersistenceProvider<S> {
18
17
  const {viewManagerModel} = cfg.persistOptions;
19
18
  throwIf(!viewManagerModel, `ViewManagerProvider requires a 'viewManagerModel'.`);
20
19
  this.viewManagerModel = viewManagerModel;
21
- viewManagerModel.providers.push(this);
20
+ viewManagerModel.registerProvider(this);
22
21
  }
23
22
 
24
23
  pushStateToTarget() {
@@ -38,10 +37,7 @@ export class ViewManagerProvider<S> extends PersistenceProvider<S> {
38
37
  }
39
38
 
40
39
  override destroy() {
41
- if (this.viewManagerModel) {
42
- pull(this.viewManagerModel.providers, this);
43
- }
44
-
40
+ this.viewManagerModel?.unregisterProvider(this);
45
41
  super.destroy();
46
42
  }
47
43
  }
@@ -195,6 +195,7 @@ export class DashCanvasModel
195
195
  PersistenceProvider.create({
196
196
  persistOptions: {
197
197
  path: 'dashCanvas',
198
+ settleTime: 1000,
198
199
  ...persistWith
199
200
  },
200
201
  target: this
@@ -215,6 +215,7 @@ export class DashContainerModel
215
215
  PersistenceProvider.create({
216
216
  persistOptions: {
217
217
  path: 'dashContainer',
218
+ settleTime: 1000,
218
219
  ...persistWith
219
220
  },
220
221
  target: this
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "74.0.0-SNAPSHOT.1748626008777",
3
+ "version": "74.0.0-SNAPSHOT.1748890930922",
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",