@adaptabletools/adaptable 23.0.19 → 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.
- package/package.json +1 -1
- package/src/AdaptableOptions/AdaptableOptions.d.ts +1 -1
- package/src/AdaptableOptions/DefaultAdaptableOptions.js +1 -0
- package/src/AdaptableOptions/FilterOptions.d.ts +8 -0
- package/src/AdaptableOptions/LayoutOptions.d.ts +25 -1
- package/src/AdaptableState/InitialState.d.ts +1 -1
- package/src/AdaptableState/LayoutState.d.ts +3 -1
- package/src/Api/Implementation/FormatColumnApiImpl.d.ts +1 -0
- package/src/Api/Implementation/FormatColumnApiImpl.js +15 -0
- package/src/Api/Implementation/FreeTextColumnApiImpl.js +0 -3
- package/src/Api/Implementation/StateApiImpl.d.ts +2 -0
- package/src/Api/Implementation/StateApiImpl.js +15 -0
- package/src/Api/Internal/ColumnFilterInternalApi.d.ts +7 -1
- package/src/Api/Internal/ColumnFilterInternalApi.js +3 -0
- package/src/Api/Internal/FormatColumnInternalApi.d.ts +5 -0
- package/src/Api/Internal/FormatColumnInternalApi.js +22 -0
- package/src/Api/Internal/StateInternalApi.d.ts +6 -1
- package/src/Api/Internal/StateInternalApi.js +44 -0
- package/src/Api/StateApi.d.ts +20 -0
- package/src/Redux/Store/AdaptableReduxMerger.js +24 -6
- package/src/Redux/Store/AdaptableStore.js +1 -1
- package/src/Utilities/Helpers/SettingsPanelHelper.js +2 -2
- package/src/View/Components/ColumnFilter/components/FloatingFilterValues.js +9 -40
- package/src/View/Components/FilterForm/ListBoxFilterForm.js +15 -13
- package/src/agGrid/AdaptableAgGrid.d.ts +13 -0
- package/src/agGrid/AdaptableAgGrid.js +51 -2
- package/src/agGrid/AdaptableFilterHandler.d.ts +5 -1
- package/src/agGrid/AdaptableFilterHandler.js +44 -0
- package/src/components/Combobox/comboboxUtils.d.ts +6 -0
- package/src/components/Combobox/comboboxUtils.js +1 -1
- package/src/components/Combobox/index.js +11 -2
- package/src/env.js +2 -2
- package/src/layout-manager/src/index.d.ts +12 -2
- package/src/layout-manager/src/index.js +46 -8
- package/src/types.d.ts +1 -1
- package/tsconfig.esm.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -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.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { IRowNode } from 'ag-grid-enterprise';
|
|
1
2
|
import { Layout, PivotLayout, TableLayout } from '../AdaptableState/LayoutState';
|
|
2
3
|
import { AdaptableObjectTag } from '../AdaptableState/Common/AdaptableObject';
|
|
4
|
+
import { AdaptableColumnContext } from '../AdaptableState/Common/AdaptableColumnContext';
|
|
3
5
|
import { BaseContext, LayoutExtensionModule, LayoutExtensionObject } from '../../types';
|
|
4
6
|
/**
|
|
5
7
|
* Options for configuring Layouts; manage Column visibility, order, sorting, grouping, width etc.
|
|
6
8
|
*/
|
|
7
|
-
export interface LayoutOptions {
|
|
9
|
+
export interface LayoutOptions<TData = any> {
|
|
8
10
|
/**
|
|
9
11
|
* Set of options for how Layouts are displayed in Settings Panel
|
|
10
12
|
*/
|
|
@@ -21,6 +23,10 @@ export interface LayoutOptions {
|
|
|
21
23
|
* Default properties to apply when creating a new Layout (Table or Pivot)
|
|
22
24
|
*/
|
|
23
25
|
layoutCreationDefaultProperties?: LayoutCreationDefaultProperties | ((context: LayoutCreationDefaultPropertiesContext) => TableLayoutCreationDefaultProperties | PivotLayoutCreationDefaultProperties);
|
|
26
|
+
/**
|
|
27
|
+
* Sets if Row Selection checkbox should be visible for a given row
|
|
28
|
+
*/
|
|
29
|
+
isRowSelectionCheckboxVisible?: (context: LayoutRowSelectionContext<TData>) => boolean;
|
|
24
30
|
}
|
|
25
31
|
/**
|
|
26
32
|
* Options for managing Tags in Layouts (used to enhance Layouts)
|
|
@@ -127,3 +133,21 @@ export interface LayoutCreationDefaultPropertiesContext extends BaseContext {
|
|
|
127
133
|
*/
|
|
128
134
|
layoutType: 'table' | 'pivot';
|
|
129
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Context for `LayoutOptions.isRowSelectionCheckboxVisible` function
|
|
138
|
+
*/
|
|
139
|
+
export interface LayoutRowSelectionContext<TData = any> extends AdaptableColumnContext<TData> {
|
|
140
|
+
/**
|
|
141
|
+
* Current Layout
|
|
142
|
+
*/
|
|
143
|
+
layout: Layout;
|
|
144
|
+
/**
|
|
145
|
+
* The Row Node being evaluated
|
|
146
|
+
*/
|
|
147
|
+
rowNode: IRowNode<TData>;
|
|
148
|
+
/**
|
|
149
|
+
* The data in the Row Node.
|
|
150
|
+
* `undefined` for group, footer, pinned, and other non-data rows — use `rowNode` for those cases.
|
|
151
|
+
*/
|
|
152
|
+
data: TData | undefined;
|
|
153
|
+
}
|
|
@@ -60,7 +60,7 @@ export interface InitialState {
|
|
|
60
60
|
*/
|
|
61
61
|
FormatColumn?: FormatColumnState;
|
|
62
62
|
/**
|
|
63
|
-
* Collection of *FreeText* objects
|
|
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
|
/**
|
|
@@ -310,7 +310,9 @@ export interface LayoutRowSelection {
|
|
|
310
310
|
*/
|
|
311
311
|
Mode: 'singleRow' | 'multiRow';
|
|
312
312
|
/**
|
|
313
|
-
* Whether to display checkboxes in Selection Column
|
|
313
|
+
* Whether to display checkboxes in Selection Column.
|
|
314
|
+
* Overridden by `LayoutOptions.isRowSelectionCheckboxVisible` when that function is provided
|
|
315
|
+
* (except when `RowSelection` is `false`, which disables selection entirely).
|
|
314
316
|
* @defaultValue true
|
|
315
317
|
*/
|
|
316
318
|
Checkboxes?: boolean;
|
|
@@ -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
|
}
|
|
@@ -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)) {
|
package/src/Api/StateApi.d.ts
CHANGED
|
@@ -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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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 (
|
|
82
|
-
|
|
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
|
-
|
|
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', '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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();
|