@adaptabletools/adaptable 23.0.20 → 23.0.21

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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/AdaptableOptions/FilterOptions.d.ts +8 -0
  3. package/src/AdaptableOptions/LayoutOptions.d.ts +1 -8
  4. package/src/AdaptableState/InitialState.d.ts +1 -1
  5. package/src/Api/Implementation/FormatColumnApiImpl.d.ts +1 -0
  6. package/src/Api/Implementation/FormatColumnApiImpl.js +15 -0
  7. package/src/Api/Implementation/FreeTextColumnApiImpl.js +0 -3
  8. package/src/Api/Implementation/StateApiImpl.d.ts +2 -0
  9. package/src/Api/Implementation/StateApiImpl.js +15 -0
  10. package/src/Api/Internal/ColumnFilterInternalApi.d.ts +7 -1
  11. package/src/Api/Internal/ColumnFilterInternalApi.js +3 -0
  12. package/src/Api/Internal/FormatColumnInternalApi.d.ts +5 -0
  13. package/src/Api/Internal/FormatColumnInternalApi.js +22 -0
  14. package/src/Api/Internal/StateInternalApi.d.ts +6 -1
  15. package/src/Api/Internal/StateInternalApi.js +44 -0
  16. package/src/Api/StateApi.d.ts +20 -0
  17. package/src/Redux/Store/AdaptableReduxMerger.js +24 -6
  18. package/src/Redux/Store/AdaptableStore.js +1 -1
  19. package/src/Utilities/Helpers/SettingsPanelHelper.js +2 -2
  20. package/src/View/Components/ColumnFilter/components/FloatingFilterValues.js +9 -40
  21. package/src/View/Components/FilterForm/ListBoxFilterForm.js +15 -13
  22. package/src/agGrid/AdaptableAgGrid.d.ts +1 -0
  23. package/src/agGrid/AdaptableAgGrid.js +15 -0
  24. package/src/agGrid/AdaptableFilterHandler.d.ts +5 -1
  25. package/src/agGrid/AdaptableFilterHandler.js +44 -0
  26. package/src/components/Combobox/comboboxUtils.d.ts +6 -0
  27. package/src/components/Combobox/comboboxUtils.js +1 -1
  28. package/src/components/Combobox/index.js +11 -2
  29. package/src/env.js +2 -2
  30. package/tsconfig.esm.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptabletools/adaptable",
3
- "version": "23.0.20",
3
+ "version": "23.0.21",
4
4
  "description": "Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements",
5
5
  "keywords": [
6
6
  "web-components",
@@ -276,6 +276,14 @@ export interface CustomInFilterValuesContext<TData = any> extends AdaptableColum
276
276
  * Opaque pagination cursors may also be stashed on this object and read on the next call.
277
277
  */
278
278
  previousFilterResult?: InFilterValueResult;
279
+ /**
280
+ * Values currently selected in the IN (or NOT IN) Column Filter.
281
+ *
282
+ * Labels are resolved from previously loaded In Filter items when available.
283
+ *
284
+ * Useful with lazy pagination or server-side search: selected items may not be in the current page.
285
+ */
286
+ selectedValues: InFilterValue[];
279
287
  /**
280
288
  * Zero-based index of the first value to return.
281
289
  * Reset to 0 when the dropdown opens or `currentSearchValue` changes.
@@ -24,14 +24,7 @@ export interface LayoutOptions<TData = any> {
24
24
  */
25
25
  layoutCreationDefaultProperties?: LayoutCreationDefaultProperties | ((context: LayoutCreationDefaultPropertiesContext) => TableLayoutCreationDefaultProperties | PivotLayoutCreationDefaultProperties);
26
26
  /**
27
- * Function which checks if the Row Selection checkbox should be visible for a given row.
28
- *
29
- * When provided, this overrides `LayoutRowSelection.Checkboxes` and any `gridOptions.rowSelection.checkboxes` value,
30
- * and forces AG Grid `hideDisabledCheckboxes: true` so returning `false` hides (rather than greys out) the checkbox.
31
- *
32
- * Does not apply when the Layout sets `RowSelection: false` (row selection is fully disabled for that Layout).
33
- *
34
- * Use `context.rowNode` for group / footer / pinned / total rows (`data` may be `undefined` for those).
27
+ * Sets if Row Selection checkbox should be visible for a given row
35
28
  */
36
29
  isRowSelectionCheckboxVisible?: (context: LayoutRowSelectionContext<TData>) => boolean;
37
30
  }
@@ -60,7 +60,7 @@ export interface InitialState {
60
60
  */
61
61
  FormatColumn?: FormatColumnState;
62
62
  /**
63
- * Collection of *FreeText* objects so users can make their own notes in bespoke columns that will get stored with their state (and not with the DataSource). Useful if needing a 'Comments' column.
63
+ * Collection of *FreeText* objects that define user-owned columns whose cell values are stored in Adaptable State (not the grid DataSource). Use for editable per-row fields such as ops notes, flags, or codes — distinct from cell-level Notes and Comments.
64
64
  */
65
65
  FreeTextColumn?: FreeTextColumnState;
66
66
  /**
@@ -38,4 +38,5 @@ export declare class FormatColumnApiImpl extends ApiBase implements FormatColumn
38
38
  incrementFormatColumnPrecedence(formatColumn: FormatColumn): void;
39
39
  decrementFormatColumnPrecedence(formatColumn: FormatColumn): void;
40
40
  findFormatColumns(criteria: AdaptableObjectLookupCriteria): FormatColumn[];
41
+ private refreshColumnsForFormatColumns;
41
42
  }
@@ -39,14 +39,17 @@ export class FormatColumnApiImpl extends ApiBase {
39
39
  addFormatColumn(formatColumn) {
40
40
  this.addUidToAdaptableObject(formatColumn);
41
41
  this.dispatchAction(FormatColumnRedux.FormatColumnAdd(formatColumn));
42
+ this.refreshColumnsForFormatColumns(formatColumn);
42
43
  return this.getFormatColumnByUuId(formatColumn.Uuid);
43
44
  }
44
45
  addFormatColumns(formatColumns) {
45
46
  this.dispatchAction(FormatColumnRedux.FormatColumnsAdd(formatColumns));
47
+ this.refreshColumnsForFormatColumns(formatColumns);
46
48
  return formatColumns?.map((formatColumn) => this.getFormatColumnByUuId(formatColumn.Uuid));
47
49
  }
48
50
  editFormatColumn(formatColumn) {
49
51
  this.dispatchAction(FormatColumnRedux.FormatColumnEdit(formatColumn));
52
+ this.refreshColumnsForFormatColumns(formatColumn);
50
53
  return this.getFormatColumnByUuId(formatColumn.Uuid);
51
54
  }
52
55
  addFormatColumnStyle(formatColumn, style) {
@@ -56,26 +59,33 @@ export class FormatColumnApiImpl extends ApiBase {
56
59
  updateFormatColumnStyle(formatColumn, style) {
57
60
  formatColumn.Style = style;
58
61
  this.dispatchAction(FormatColumnRedux.FormatColumnEdit(formatColumn));
62
+ this.refreshColumnsForFormatColumns(formatColumn);
59
63
  }
60
64
  deleteFormatColumn(formatColumn) {
61
65
  this.dispatchAction(FormatColumnRedux.FormatColumnDelete(formatColumn));
66
+ this.refreshColumnsForFormatColumns(formatColumn);
62
67
  }
63
68
  deleteAllFormatColumns() {
64
69
  this.dispatchAction(FormatColumnRedux.FormatColumnDeleteAll());
70
+ this.getGridApi().refreshAllCells(true);
65
71
  }
66
72
  suspendFormatColumn(formatColumn) {
67
73
  this.dispatchAction(FormatColumnRedux.FormatColumnSuspend(formatColumn));
74
+ this.refreshColumnsForFormatColumns(formatColumn);
68
75
  return this.getFormatColumnByUuId(formatColumn.Uuid);
69
76
  }
70
77
  unSuspendFormatColumn(formatColumn) {
71
78
  this.dispatchAction(FormatColumnRedux.FormatColumnUnSuspend(formatColumn));
79
+ this.refreshColumnsForFormatColumns(formatColumn);
72
80
  return this.getFormatColumnByUuId(formatColumn.Uuid);
73
81
  }
74
82
  suspendAllFormatColumn() {
75
83
  this.dispatchAction(FormatColumnRedux.FormatColumnSuspendAll());
84
+ this.getGridApi().refreshAllCells(true);
76
85
  }
77
86
  unSuspendAllFormatColumn() {
78
87
  this.dispatchAction(FormatColumnRedux.FormatColumnUnSuspendAll());
88
+ this.getGridApi().refreshAllCells(true);
79
89
  }
80
90
  getFormatColumnsForColumn(column, config) {
81
91
  const formatColumns = this.getFormatColumns(config).filter((formatColumn) => (config?.includeSuspended || !formatColumn.IsSuspended) && (config?.includeReadOnly || !formatColumn.IsReadOnly));
@@ -114,9 +124,11 @@ export class FormatColumnApiImpl extends ApiBase {
114
124
  }
115
125
  incrementFormatColumnPrecedence(formatColumn) {
116
126
  this.dispatchAction(FormatColumnRedux.FormatColumnMoveUp(formatColumn));
127
+ this.refreshColumnsForFormatColumns(formatColumn);
117
128
  }
118
129
  decrementFormatColumnPrecedence(formatColumn) {
119
130
  this.dispatchAction(FormatColumnRedux.FormatColumnMoveDown(formatColumn));
131
+ this.refreshColumnsForFormatColumns(formatColumn);
120
132
  }
121
133
  findFormatColumns(criteria) {
122
134
  const config = {
@@ -124,4 +136,7 @@ export class FormatColumnApiImpl extends ApiBase {
124
136
  };
125
137
  return this.getAdaptableInternalApi().findAdaptableObjectsByLookupCriteria(criteria, this.getFormatColumns(config));
126
138
  }
139
+ refreshColumnsForFormatColumns(formatColumns) {
140
+ this.internalApi.refreshFormatColumnPresentation(formatColumns);
141
+ }
127
142
  }
@@ -60,9 +60,6 @@ export class FreeTextColumnApiImpl extends ApiBase {
60
60
  existingItem.FreeText = sv.FreeText;
61
61
  }
62
62
  }
63
- else {
64
- freeTextColumn.FreeTextStoredValues.push(sv);
65
- }
66
63
  }
67
64
  else {
68
65
  freeTextColumn.FreeTextStoredValues.push(sv);
@@ -30,6 +30,8 @@ export declare class StateApiImpl extends ApiBase implements StateApi {
30
30
  copyUserStateToClipboard(): void;
31
31
  getInitialState(): InitialState | any;
32
32
  reloadInitialState(newInitialState?: InitialState): Promise<void>;
33
+ applyInitialState(newInitialState?: InitialState): Promise<void>;
34
+ remergePersistedState(): Promise<void>;
33
35
  getAllState(): AdaptableState;
34
36
  getPersistentState(): AdaptablePersistentState;
35
37
  getAllUserState(): BaseState[];
@@ -66,12 +66,27 @@ export class StateApiImpl extends ApiBase {
66
66
  .then(() => {
67
67
  const newState = this.getPersistentState();
68
68
  this.getEventApi().internalApi.fireAdaptableStateReloadedEvent(oldState, newState);
69
+ this.getGridApi().refreshAllCells(true);
70
+ this.getThemeApi().applyCurrentTheme();
69
71
  })
70
72
  .then(() => {
71
73
  return this.getAdaptableStore().saveStateNow(this._adaptable, this.getAdaptableStore().TheStore.getState());
72
74
  });
73
75
  });
74
76
  }
77
+ applyInitialState(newInitialState) {
78
+ if (newInitialState) {
79
+ this.getOptions().initialState = JSON.parse(JSON.stringify(newInitialState));
80
+ }
81
+ const initialState = (newInitialState ?? this.getInitialState());
82
+ return this.internalApi.applyMergedInitialAndUserState(initialState, this.getPersistentState());
83
+ }
84
+ async remergePersistedState() {
85
+ const config = buildAdaptableStateFunctionConfig(this._adaptable);
86
+ const loadState = this.getOptions().stateOptions.loadState;
87
+ const userState = await loadState(config);
88
+ return this.internalApi.applyMergedInitialAndUserState(this.getInitialState(), userState);
89
+ }
75
90
  getAllState() {
76
91
  return this.getAdaptableState();
77
92
  }
@@ -5,7 +5,7 @@ import { AdaptablePredicateDef } from '../../AdaptableState/Common/AdaptablePred
5
5
  import { ColumnFilter } from '../../AdaptableState/Common/ColumnFilter';
6
6
  import { GridCell } from '../../AdaptableState/Selection/GridCell';
7
7
  import { LayoutColumnFilterAction } from '../../Redux/ActionsReducers/LayoutRedux';
8
- import { InFilterValueResult } from '../../types';
8
+ import { InFilterValue, InFilterValueResult } from '../../types';
9
9
  import { ApiBase } from '../Implementation/ApiBase';
10
10
  import { AdaptableFilterHandler } from '../../agGrid/AdaptableFilterHandler';
11
11
  export declare class ColumnFilterInternalApi extends ApiBase {
@@ -58,5 +58,11 @@ export declare class ColumnFilterInternalApi extends ApiBase {
58
58
  }): Promise<InFilterValueResult>;
59
59
  shouldManuallyApplyColumnFilter(columnId: string): boolean;
60
60
  getAdaptableFilterHandler(columnId: string): AdaptableFilterHandler | undefined;
61
+ /**
62
+ * Returns the remembered selected In-filter items for a column - used to resolve
63
+ * labels for selected values missing from the current page (lazy pagination).
64
+ * @param columnId column to get the remembered values for
65
+ */
66
+ getRememberedLazyInFilterValues(columnId: string): InFilterValue[];
61
67
  getAllAdaptableFilterHandlers(): AdaptableFilterHandler[];
62
68
  }
@@ -234,6 +234,9 @@ export class ColumnFilterInternalApi extends ApiBase {
234
234
  getAdaptableFilterHandler(columnId) {
235
235
  return this.getAgGridApi().getColumnFilterHandler(columnId);
236
236
  }
237
+ getRememberedLazyInFilterValues(columnId) {
238
+ return this.getAdaptableFilterHandler(columnId)?.getRememberedLazyInFilterValues() ?? [];
239
+ }
237
240
  getAllAdaptableFilterHandlers() {
238
241
  const allFilterableColumns = this.getColumnApi().getFilterableColumns();
239
242
  const filterHandlers = allFilterableColumns
@@ -2,6 +2,11 @@ import { Column, HeaderValueGetterParams, IRowNode } from 'ag-grid-enterprise';
2
2
  import { AdaptableColumn, AdaptableFormat, AdaptablePredicateDef, ColumnScope, FormatColumn, FormatColumnConfig, FormatColumnTarget, StringFormatterOptions } from '../../types';
3
3
  import { ApiBase } from '../Implementation/ApiBase';
4
4
  export declare class FormatColumnInternalApi extends ApiBase {
5
+ /**
6
+ * Re-evaluate Format Column styles on affected columns after state changes.
7
+ * Column-model update alone can leave visible cells stale until scroll.
8
+ */
9
+ refreshFormatColumnPresentation(formatColumns: FormatColumn | FormatColumn[] | undefined): void;
5
10
  /**
6
11
  * Retrieves all Format Columns in Adaptable State with the `Style` property set
7
12
  * @returns format columns
@@ -27,6 +27,28 @@ function getParentGroup(source) {
27
27
  return undefined;
28
28
  }
29
29
  export class FormatColumnInternalApi extends ApiBase {
30
+ refreshFormatColumnPresentation(formatColumns) {
31
+ if (!formatColumns) {
32
+ return;
33
+ }
34
+ const list = Array.isArray(formatColumns) ? formatColumns : [formatColumns];
35
+ const columnIds = new Set();
36
+ for (const formatColumn of list) {
37
+ if (!formatColumn?.Scope) {
38
+ continue;
39
+ }
40
+ if (this.getColumnScopeApi().scopeIsAll(formatColumn.Scope)) {
41
+ this.getGridApi().refreshAllCells(true);
42
+ return;
43
+ }
44
+ this.getColumnScopeApi()
45
+ .getColumnsInScope(formatColumn.Scope)
46
+ .forEach((column) => columnIds.add(column.columnId));
47
+ }
48
+ if (columnIds.size > 0) {
49
+ this.getGridApi().refreshColumns([...columnIds]);
50
+ }
51
+ }
30
52
  getAllFormatColumnWithStyle(config) {
31
53
  return this.getFormatColumnApi()
32
54
  .getFormatColumns(config)
@@ -1,6 +1,11 @@
1
- import { AdaptablePersistentState, AdaptableState } from '../../types';
1
+ import { AdaptablePersistentState, AdaptableState, InitialState } from '../../types';
2
2
  import { ApiBase } from '../Implementation/ApiBase';
3
3
  export declare class StateInternalApi extends ApiBase {
4
+ /**
5
+ * Startup-equivalent merge: Initial State + user state → store, layout/theme/cells, persist.
6
+ * Does not clear persistence and does not call `loadState` (caller supplies user state).
7
+ */
8
+ applyMergedInitialAndUserState(initialState: InitialState, userState: AdaptablePersistentState | Record<string, unknown> | null | undefined): Promise<void>;
4
9
  getAllStateForDevTools(): AdaptableState;
5
10
  /**
6
11
  * Replaces persisted Adaptable state with the supplied snapshot (e.g. from DevTools).
@@ -1,6 +1,50 @@
1
1
  import { getPersistableState, LoadState, InitState } from '../../Redux/Store/AdaptableStore';
2
+ import { MergeStateFunction } from '../../Redux/Store/AdaptableReduxMerger';
3
+ import { buildAdaptableStateFunctionConfig } from '../../Redux/Store/buildAdaptableStateFunctionConfig';
4
+ import { addUuidsToInitialState } from '../../Utilities/Helpers/AdaptableHelper';
2
5
  import { ApiBase } from '../Implementation/ApiBase';
6
+ function deepClone(value) {
7
+ return JSON.parse(JSON.stringify(value ?? null));
8
+ }
3
9
  export class StateInternalApi extends ApiBase {
10
+ applyMergedInitialAndUserState(initialState, userState) {
11
+ return new Promise((resolve, reject) => {
12
+ this.getAdaptableInternalApi().executeWithProgressIndicator('Applying Initial State...', () => {
13
+ try {
14
+ const oldState = this.getAdaptableApi().stateApi.getPersistentState();
15
+ const initialForMerge = addUuidsToInitialState(deepClone(initialState));
16
+ const userForMerge = deepClone(userState ?? {});
17
+ let merged = MergeStateFunction(initialForMerge, userForMerge);
18
+ const stateFunctionConfig = buildAdaptableStateFunctionConfig(this._adaptable);
19
+ merged = this.getOptions().stateOptions.applyState(merged, stateFunctionConfig);
20
+ const agGridOptions = {
21
+ columnDefs: this._adaptable.agGridAdapter.getGridOption('columnDefs'),
22
+ autoGroupColumnDef: this._adaptable.agGridAdapter.getGridOption('autoGroupColumnDef'),
23
+ rowModelType: this._adaptable.agGridAdapter.getGridOption('rowModelType'),
24
+ treeData: this._adaptable.agGridAdapter.getGridOption('treeData'),
25
+ };
26
+ merged = this._adaptable.normalizeAdaptableState(merged, agGridOptions);
27
+ this.getAdaptableStore().TheStore.dispatch(LoadState(merged));
28
+ this.getAdaptableStore().TheStore.dispatch(InitState());
29
+ this._adaptable.refreshLayout();
30
+ this.getGridApi().refreshAllCells(true);
31
+ this.getThemeApi().applyCurrentTheme();
32
+ const newState = this.getAdaptableApi().stateApi.getPersistentState();
33
+ this.getEventApi().internalApi.fireAdaptableStateReloadedEvent(oldState, newState);
34
+ void this.getAdaptableStore()
35
+ .saveStateNow(this._adaptable)
36
+ .then(() => resolve())
37
+ .catch(reject);
38
+ }
39
+ catch (error) {
40
+ reject(error);
41
+ }
42
+ finally {
43
+ this.getUserInterfaceApi().hideProgressIndicator();
44
+ }
45
+ });
46
+ });
47
+ }
4
48
  getAllStateForDevTools() {
5
49
  let allState = this.getAdaptableApi().stateApi.getAllState();
6
50
  if (allState.Layout && Array.isArray(allState.Layout.Layouts)) {
@@ -42,6 +42,26 @@ export interface StateApi {
42
42
  * @param newInitialState optional new InitialState to load
43
43
  */
44
44
  reloadInitialState(newInitialState?: InitialState): Promise<void>;
45
+ /**
46
+ * Merges Initial State into the **current** user state (Revision / Override / KeepUserDefined),
47
+ * without clearing persistence. Same merge as startup, applied at runtime.
48
+ *
49
+ * When `newInitialState` is provided it also updates `AdaptableOptions.initialState`.
50
+ * Omitting it re-applies the current Initial State onto current user state.
51
+ *
52
+ * @param newInitialState optional Initial State to merge (defaults to current Initial State)
53
+ */
54
+ applyInitialState(newInitialState?: InitialState): Promise<void>;
55
+ /**
56
+ * Re-runs the startup merge using **persisted** user state from `StateOptions.loadState`
57
+ * and the current `AdaptableOptions.initialState`. Does not clear persistence.
58
+ *
59
+ * Use when remote/local storage may have changed (or Initial State in options was updated)
60
+ * and you want to re-sync without a full wipe (`reloadInitialState`).
61
+ *
62
+ * Unsaved in-memory-only changes are discarded in favour of what `loadState` returns.
63
+ */
64
+ remergePersistedState(): Promise<void>;
45
65
  /**
46
66
  * Copies all Adaptable state to clipboard
47
67
  */
@@ -2,6 +2,7 @@ import { isArray } from '../../Utilities/Extensions/ArrayExtensions';
2
2
  import { isObject, merge, mergeWith, } from '../../Utilities/Extensions/ObjectExtensions';
3
3
  import { AdaptableLogger } from '../../agGrid/AdaptableLogger';
4
4
  import AdaptableHelper from '../../Utilities/Helpers/AdaptableHelper';
5
+ const mergerLogger = new AdaptableLogger('StateMerger');
5
6
  function customizer(objValue, srcValue) {
6
7
  if (isArray(objValue)) {
7
8
  if (!Array.isArray(srcValue)) {
@@ -66,8 +67,10 @@ export function MergeStateFunction(oldState, newState) {
66
67
  }
67
68
  for (const initialStateModuleName in initialState) {
68
69
  if (!state[initialStateModuleName]) {
69
- const { Revision, ...moduleState } = initialState[initialStateModuleName];
70
- state[initialStateModuleName] = moduleState;
70
+ state[initialStateModuleName] = initialState[initialStateModuleName];
71
+ if (initialState[initialStateModuleName]?.Revision != null) {
72
+ mergerLogger.info(`Added section '${initialStateModuleName}' from Initial State (Revision preserved)`, initialState[initialStateModuleName].Revision);
73
+ }
71
74
  }
72
75
  }
73
76
  for (const stateModuleName in state) {
@@ -75,14 +78,29 @@ export function MergeStateFunction(oldState, newState) {
75
78
  const stateRevision = state[stateModuleName].Revision ?? 0;
76
79
  const initialStateRevision = initialState[stateModuleName].Revision ?? 0;
77
80
  const stateRevisionKey = typeof stateRevision === 'object' ? stateRevision.Key : stateRevision;
78
- const initialStateRevisionBehaviour = typeof initialStateRevision === 'object' ? initialStateRevision.Behavior : 'Override';
81
+ let initialStateRevisionStrategy = 'Override';
82
+ if (typeof initialStateRevision === 'object') {
83
+ const rawStrategy = initialStateRevision.UpdateStrategy ?? initialStateRevision.Behavior;
84
+ if (rawStrategy === 'KeepUserDefined' || rawStrategy === 'Override') {
85
+ initialStateRevisionStrategy = rawStrategy;
86
+ }
87
+ else if (rawStrategy != null) {
88
+ mergerLogger.warn(`Unknown Revision UpdateStrategy '${rawStrategy}' on section '${stateModuleName}'; falling back to Override`, initialStateRevision);
89
+ }
90
+ if (initialStateRevision.UpdateStrategy == null &&
91
+ initialStateRevision.Behavior != null) {
92
+ mergerLogger.info(`Section '${stateModuleName}' uses legacy Revision.Behavior; prefer UpdateStrategy`, initialStateRevision);
93
+ }
94
+ }
79
95
  const initialStateRevisionKey = typeof initialStateRevision === 'object' ? initialStateRevision.Key : initialStateRevision;
80
96
  if (initialStateRevisionKey > stateRevisionKey) {
81
- if (initialStateRevisionBehaviour === 'Override') {
82
- state[stateModuleName] = initialState[stateModuleName];
97
+ if (initialStateRevisionStrategy === 'KeepUserDefined') {
98
+ mergerLogger.info(`Applying Revision on '${stateModuleName}': ${stateRevisionKey} → ${initialStateRevisionKey} (KeepUserDefined)`);
99
+ state[stateModuleName] = ProcessKeepUserDefinedRevision(deepClone(initialState[stateModuleName]), state[stateModuleName]);
83
100
  }
84
101
  else {
85
- state[stateModuleName] = ProcessKeepUserDefinedRevision(deepClone(initialState[stateModuleName]), state[stateModuleName]);
102
+ mergerLogger.info(`Applying Revision on '${stateModuleName}': ${stateRevisionKey} → ${initialStateRevisionKey} (Override)`);
103
+ state[stateModuleName] = initialState[stateModuleName];
86
104
  }
87
105
  }
88
106
  }
@@ -255,7 +255,7 @@ export class AdaptableStore {
255
255
  this.storageEngine.setStateKey(adaptableStateKey);
256
256
  this.loadStorageInProgress = true;
257
257
  return (this.Load = this.storageEngine
258
- .load(adaptable, initialState)
258
+ .load(adaptable, initialState ?? adaptable.adaptableOptions.initialState)
259
259
  .then((storedState) => {
260
260
  if (storedState && this.loadStateOnStartup) {
261
261
  this.TheStore.dispatch(LoadState(postProcessState(adaptable.adaptableOptions.stateOptions.applyState(storedState, buildAdaptableStateFunctionConfig(adaptable)))));
@@ -5,7 +5,7 @@ export const DEFAULT_SETTINGS_PANEL_NAVIGATION_GROUPS = [
5
5
  },
6
6
  {
7
7
  label: 'Layout',
8
- items: ['Layout', 'CalculatedColumn', 'CustomSort', 'Charting'],
8
+ items: ['Layout', 'CalculatedColumn', 'FreeTextColumn', 'CustomSort', 'Charting'],
9
9
  },
10
10
  {
11
11
  label: 'Formatting',
@@ -17,7 +17,7 @@ export const DEFAULT_SETTINGS_PANEL_NAVIGATION_GROUPS = [
17
17
  },
18
18
  {
19
19
  label: 'Notes & Annotations',
20
- items: ['Alert', 'Note', 'Comment', 'FreeTextColumn', 'SystemStatus'],
20
+ items: ['Alert', 'Note', 'Comment', 'SystemStatus'],
21
21
  },
22
22
  {
23
23
  label: 'Export & Import',
@@ -183,17 +183,17 @@ export function useDistinctFilterColumnValues(options) {
183
183
  if (!quickFilterValues.hasMore || !quickFilterValues.dataLoadIsComplete) {
184
184
  return;
185
185
  }
186
- let scrollEl = null;
187
186
  let debounceTimer;
188
- const onScroll = () => {
187
+ const onScroll = (event) => {
188
+ const target = event.target;
189
+ if (!(target instanceof Element) || !target.matches(FILTER_VALUES_SCROLL_SELECTOR)) {
190
+ return;
191
+ }
189
192
  if (debounceTimer) {
190
193
  clearTimeout(debounceTimer);
191
194
  }
192
195
  debounceTimer = setTimeout(() => {
193
- if (!scrollEl) {
194
- return;
195
- }
196
- const el = scrollEl;
196
+ const el = target;
197
197
  const remaining = el.scrollHeight - el.scrollTop - el.clientHeight;
198
198
  if (remaining > NEAR_END_PX) {
199
199
  return;
@@ -201,45 +201,14 @@ export function useDistinctFilterColumnValues(options) {
201
201
  loadMoreValues();
202
202
  }, SCROLL_DEBOUNCE_MS);
203
203
  };
204
- const tryAttach = () => {
205
- scrollEl = document.querySelector(FILTER_VALUES_SCROLL_SELECTOR);
206
- if (scrollEl) {
207
- scrollEl.addEventListener('scroll', onScroll, { passive: true });
208
- return true;
209
- }
210
- return false;
211
- };
212
- let attached = tryAttach();
213
- const raf = attached
214
- ? undefined
215
- : requestAnimationFrame(() => {
216
- attached = tryAttach();
217
- });
218
- const timeout = attached
219
- ? undefined
220
- : setTimeout(() => {
221
- tryAttach();
222
- }, 50);
204
+ document.addEventListener('scroll', onScroll, { capture: true, passive: true });
223
205
  return () => {
224
- if (raf != null) {
225
- cancelAnimationFrame(raf);
226
- }
227
- if (timeout != null) {
228
- clearTimeout(timeout);
229
- }
230
206
  if (debounceTimer) {
231
207
  clearTimeout(debounceTimer);
232
208
  }
233
- if (scrollEl) {
234
- scrollEl.removeEventListener('scroll', onScroll);
235
- }
209
+ document.removeEventListener('scroll', onScroll, { capture: true });
236
210
  };
237
- }, [
238
- quickFilterValues.hasMore,
239
- quickFilterValues.dataLoadIsComplete,
240
- quickFilterValues.values.length,
241
- loadMoreValues,
242
- ]);
211
+ }, [quickFilterValues.hasMore, quickFilterValues.dataLoadIsComplete, loadMoreValues]);
243
212
  useEffect(() => {
244
213
  if (!hasMoreRef.current ||
245
214
  !quickFilterValues.dataLoadIsComplete ||
@@ -10,6 +10,7 @@ import { useMemo } from 'react';
10
10
  import { GridFilterCombobox } from '../../../components/Combobox';
11
11
  import { isEmbeddedColumnFilterLocation } from '../ColumnFilter/columnFilterLocation';
12
12
  import { useColumnFilterLocation } from '../ColumnFilter/AdaptableColumnFilter';
13
+ import { useAdaptable } from '../../AdaptableContext';
13
14
  import { GridFilterTreeDropdown } from '../../../components/Tree/TreeDropdown';
14
15
  const baseClassName = 'ab-ListBoxFilterForm';
15
16
  function toDateDisplayValue(values) {
@@ -29,9 +30,11 @@ const dateOnChangeParser = (value) => {
29
30
  const EMPTY_ARRAY = [];
30
31
  export const ColumnValuesSelect = (props) => {
31
32
  const location = useColumnFilterLocation();
33
+ const adaptable = useAdaptable();
32
34
  const column = props.column;
33
35
  let selectedColumnValues = props.value || EMPTY_ARRAY;
34
36
  const rawOptionsFromProps = Array.isArray(props.options) ? props.options : EMPTY_ARRAY;
37
+ const lookupItems = adaptable?.api.filterApi.columnFilterApi.internalApi.getRememberedLazyInFilterValues(column.columnId) ?? EMPTY_ARRAY;
35
38
  const optionsFromProps = useMemo(() => {
36
39
  if (rawOptionsFromProps === EMPTY_ARRAY) {
37
40
  return rawOptionsFromProps;
@@ -88,19 +91,18 @@ export const ColumnValuesSelect = (props) => {
88
91
  }
89
92
  const options = column.dataType === 'date' || column.columnId === AG_GRID_GROUPED_COLUMN
90
93
  ? dateOptions
91
- : optionsFromProps.filter((distinctValue) => {
92
- let isActive = selectedColumnValues.indexOf(distinctValue.value) >= 0;
93
- if (!isActive && distinctValue.value && distinctValue.value instanceof Date) {
94
- isActive = selectedColumnValues.some((dateStr) => isEqual(parseDateValue(dateStr), parseDateValue(distinctValue.value)));
94
+ : optionsFromProps;
95
+ if (column.dataType !== 'date' && column.columnId !== AG_GRID_GROUPED_COLUMN) {
96
+ for (const selected of selectedColumnValues) {
97
+ if (options.some((option) => option.value === selected)) {
98
+ value.push(selected);
99
+ continue;
95
100
  }
96
- if (StringExtensions.IsNullOrEmpty(distinctValue.value)) {
97
- return false;
98
- }
99
- if (isActive) {
100
- value.push(distinctValue.value);
101
- }
102
- return true;
103
- });
101
+ const dateOption = options.find((option) => option.value instanceof Date &&
102
+ isEqual(parseDateValue(selected), parseDateValue(option.value)));
103
+ value.push(dateOption ? dateOption.value : selected);
104
+ }
105
+ }
104
106
  const treeDropdownToDisplayValue = React.useMemo(() => {
105
107
  if (column.dataType === 'date') {
106
108
  return toDateDisplayValue;
@@ -117,7 +119,7 @@ export const ColumnValuesSelect = (props) => {
117
119
  : undefined;
118
120
  const component = column.dataType === 'date' || column.columnId === AG_GRID_GROUPED_COLUMN ? (_jsx(GridFilterTreeDropdown, { toDisplayValue: treeDropdownToDisplayValue, resizable: true, onValueChange: column.dataType === 'date'
119
121
  ? (value) => props.onChange(dateOnChangeParser(value))
120
- : props.onChange, items: options, value: selectedColumnValues, "data-name": "Select Values", primaryKey: 'value', isLoading: props.isLoading, onOpenChange: onOpenChange, showClear: isEmbeddedColumnFilterLocation(location) })) : (_jsx(GridFilterCombobox, { disabled: props.disabled, ...selectProps, onOpenChange: onOpenChange, items: options, value: value, isLoading: props.isLoading, onValueChange: props.onChange, showClear: isEmbeddedColumnFilterLocation(location) }, "select"));
122
+ : props.onChange, items: options, value: selectedColumnValues, "data-name": "Select Values", primaryKey: 'value', isLoading: props.isLoading, onOpenChange: onOpenChange, showClear: isEmbeddedColumnFilterLocation(location) })) : (_jsx(GridFilterCombobox, { disabled: props.disabled, ...selectProps, onOpenChange: onOpenChange, items: options, value: value, lookupItems: lookupItems, isLoading: props.isLoading, onValueChange: props.onChange, showClear: isEmbeddedColumnFilterLocation(location) }, "select"));
121
123
  return (_jsx("div", { className: join(baseClassName, 'twa:relative', props.isLoading && `${baseClassName}--loading`, !value.length && `${baseClassName}--empty`), onKeyDownCapture: (e) => {
122
124
  if (e.key === 'Tab') {
123
125
  e.stopPropagation();
@@ -291,6 +291,7 @@ export declare class AdaptableAgGrid implements IAdaptable {
291
291
  autoSizeColumns(columnIds?: string[]): void;
292
292
  autoSizeAllColumns(): void;
293
293
  getDistinctValuesForColumn(column: AdaptableColumn): Promise<GridCell[]>;
294
+ private getSelectedInFilterValues;
294
295
  getDistinctFilterValuesForColumn(options: {
295
296
  column: AdaptableColumn;
296
297
  currentSearchValue: string;
@@ -2372,6 +2372,18 @@ export class AdaptableAgGrid {
2372
2372
  async getDistinctValuesForColumn(column) {
2373
2373
  return this.getDistinctGridCellsForColumn(column);
2374
2374
  }
2375
+ getSelectedInFilterValues(column) {
2376
+ const columnFilterApi = this.api.filterApi.columnFilterApi;
2377
+ const columnFilter = columnFilterApi.getColumnFilterForColumn(column.columnId);
2378
+ const filterHandler = columnFilterApi.internalApi.getAdaptableFilterHandler(column.columnId);
2379
+ const inputs = (columnFilter?.Predicates ?? [])
2380
+ .filter((predicate) => predicate.PredicateId === 'In' || predicate.PredicateId === 'NotIn')
2381
+ .flatMap((predicate) => (Array.isArray(predicate.Inputs) ? predicate.Inputs : []));
2382
+ return inputs.map((value) => {
2383
+ const known = filterHandler?.getRememberedLazyInFilterValue(value);
2384
+ return known ?? { value, label: value == null ? '' : String(value) };
2385
+ });
2386
+ }
2375
2387
  async getDistinctFilterValuesForColumn(options) {
2376
2388
  const { column, previousResult } = options;
2377
2389
  const offset = options.offset ?? 0;
@@ -2426,6 +2438,9 @@ export class AdaptableAgGrid {
2426
2438
  previousFilterResult: previousResult,
2427
2439
  offset,
2428
2440
  limit,
2441
+ get selectedValues() {
2442
+ return self.getSelectedInFilterValues(column);
2443
+ },
2429
2444
  get currentSearchValue() {
2430
2445
  return options.currentSearchValue;
2431
2446
  },
@@ -1,12 +1,13 @@
1
1
  import { DoesFilterPassParams, FilterHandler, FilterHandlerParams } from 'ag-grid-enterprise';
2
2
  import { AdaptableApi } from '../Api/AdaptableApi';
3
- import { InFilterValueResult } from '../AdaptableOptions/FilterOptions';
3
+ import { InFilterValue, InFilterValueResult } from '../AdaptableOptions/FilterOptions';
4
4
  import { ColumnFilter } from '../types';
5
5
  export declare class AdaptableFilterHandler implements FilterHandler<any, any, ColumnFilter> {
6
6
  private adaptableApi;
7
7
  colId: string;
8
8
  private filterDisplayValuesResult;
9
9
  private previousFilterDisplayValuesResult;
10
+ private rememberedInFilterValues;
10
11
  constructor(adaptableApi: AdaptableApi);
11
12
  init(params: FilterHandlerParams): void;
12
13
  doesFilterPass(params: DoesFilterPassParams<any, any, ColumnFilter>): boolean;
@@ -22,6 +23,9 @@ export declare class AdaptableFilterHandler implements FilterHandler<any, any, C
22
23
  offset?: number;
23
24
  limit?: number;
24
25
  }): Promise<InFilterValueResult>;
26
+ private rememberSelectedLazyInFilterValues;
27
+ getRememberedLazyInFilterValues(): InFilterValue[];
28
+ getRememberedLazyInFilterValue(value: any): InFilterValue | undefined;
25
29
  onNewRowsLoaded(): void;
26
30
  onAnyFilterChanged(): void;
27
31
  resetFilterDisplayValues(): void;