@adaptabletools/adaptable 23.0.21 → 23.1.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/package.json +3 -2
  2. package/src/AdaptableInterfaces/IAdaptable.d.ts +1 -0
  3. package/src/AdaptableOptions/ColumnMenuOptions.d.ts +1 -1
  4. package/src/AdaptableOptions/ContextMenuOptions.d.ts +1 -1
  5. package/src/AdaptableOptions/FilterOptions.d.ts +34 -0
  6. package/src/Api/GridApi.d.ts +5 -1
  7. package/src/Api/Implementation/GridApiImpl.d.ts +1 -0
  8. package/src/Api/Implementation/GridApiImpl.js +22 -1
  9. package/src/Redux/Store/AdaptableStore.js +6 -1
  10. package/src/Utilities/Helpers/DisplayFormatHelper.js +16 -4
  11. package/src/Utilities/Helpers/StyledColumns/SparklineStyleHelper.d.ts +2 -0
  12. package/src/Utilities/Helpers/StyledColumns/SparklineStyleHelper.js +13 -10
  13. package/src/Utilities/Hooks/useAdaptableOptions.d.ts +1 -1
  14. package/src/Utilities/Services/AnnotationsService.js +8 -8
  15. package/src/View/Components/ColumnFilter/components/FloatingFilterValues.d.ts +3 -0
  16. package/src/View/Components/ColumnFilter/components/FloatingFilterValues.js +16 -1
  17. package/src/View/Components/FilterForm/ListBoxFilterForm.js +1 -1
  18. package/src/View/Components/PredicateEditor/PredicateEditor.js +11 -1
  19. package/src/View/FormatColumn/Wizard/FormatColumnFormatWizardSection.js +6 -2
  20. package/src/View/StyledColumn/Wizard/StyledColumnSparklineSettingsSection.js +18 -8
  21. package/src/View/Wizard/OnePageAdaptableWizard.d.ts +2 -2
  22. package/src/View/Wizard/OnePageWizards.d.ts +1 -1
  23. package/src/agGrid/AdaptableAgGrid.d.ts +30 -6
  24. package/src/agGrid/AdaptableAgGrid.js +60 -78
  25. package/src/agGrid/AgGridAdapter.d.ts +32 -1
  26. package/src/agGrid/AgGridAdapter.js +88 -0
  27. package/src/agGrid/AgGridColumnAdapter.d.ts +44 -0
  28. package/src/agGrid/AgGridColumnAdapter.js +153 -37
  29. package/src/agGrid/AgGridMenuAdapter.d.ts +8 -2
  30. package/src/agGrid/AgGridMenuAdapter.js +17 -4
  31. package/src/agGrid/AgGridModulesAdapter.js +6 -1
  32. package/src/agGrid/AgGridThemeAdapter.d.ts +17 -0
  33. package/src/agGrid/AgGridThemeAdapter.js +37 -6
  34. package/src/components/Combobox/index.d.ts +11 -0
  35. package/src/components/Combobox/index.js +5 -3
  36. package/src/components/Radio/index.d.ts +2 -2
  37. package/src/components/Tree/TreeDropdown/index.d.ts +11 -0
  38. package/src/components/Tree/TreeDropdown/index.js +6 -3
  39. package/src/env.js +2 -2
  40. package/src/layout-manager/src/index.d.ts +9 -0
  41. package/src/layout-manager/src/index.js +38 -17
  42. package/src/types.d.ts +1 -1
  43. package/tsconfig.esm.tsbuildinfo +1 -1
@@ -5,7 +5,7 @@ import { AdaptableApi, DestroyConfig } from '../Api/AdaptableApi';
5
5
  import { AdaptableVariant, IAdaptable, NormalizeAdaptableStateOptions } from '../AdaptableInterfaces/IAdaptable';
6
6
  import { EmitterCallback } from '../Utilities/Emitter';
7
7
  import { AdaptablePlugin } from '../AdaptableOptions/AdaptablePlugin';
8
- import { AgGridAdapter } from './AgGridAdapter';
8
+ import { AgGridAdapter, RowNodeEvent } from './AgGridAdapter';
9
9
  import { IDataService } from '../Utilities/Services/Interface/IDataService';
10
10
  import { AdaptableState } from '../AdaptableState/AdaptableState';
11
11
  import { IAdaptableStore } from '../Redux/Store/Interface/IAdaptableStore';
@@ -54,7 +54,6 @@ interface AdaptableInitInternalConfig<TData = any> {
54
54
  }
55
55
  export declare class AdaptableAgGrid implements IAdaptable {
56
56
  logger: AdaptableLogger;
57
- private _id;
58
57
  lifecycleState: AdaptableLifecycleState;
59
58
  variant: AdaptableVariant;
60
59
  private emitter;
@@ -94,7 +93,7 @@ export declare class AdaptableAgGrid implements IAdaptable {
94
93
  ThemeService: IThemeService;
95
94
  private LicenseService;
96
95
  private ChartingService;
97
- private rowListeners;
96
+ private isListeningToAgGridRowNodeEvents;
98
97
  private debouncedSetSelectedRows;
99
98
  private debouncedSetSelectedCells;
100
99
  private agGridListenerKeydown;
@@ -131,9 +130,6 @@ export declare class AdaptableAgGrid implements IAdaptable {
131
130
  constructor(config?: {
132
131
  getAgGridColumnApiModuleReference?: () => Module;
133
132
  });
134
- static forEachAdaptable(fn: (adaptable: AdaptableAgGrid) => void): void;
135
- private static collectInstance;
136
- private static dismissInstance;
137
133
  get isAgGridInitialising(): boolean;
138
134
  get isReady(): boolean;
139
135
  get isAvailable(): boolean;
@@ -187,6 +183,22 @@ export declare class AdaptableAgGrid implements IAdaptable {
187
183
  * Either initializes the AG Grid instance or delegates it to the framework wrappers (React/Anglar)
188
184
  */
189
185
  private initializeAgGrid;
186
+ /**
187
+ * Derives the host element of the grid (the element the framework wrapper / `createGrid()` renders
188
+ * the grid into) when `ContainerOptions.agGridContainer` is not provided.
189
+ *
190
+ * AG Grid 36+ wraps the grid in three grid-owned `div.ag-styled-root` elements which mirror the
191
+ * theme classes (`ag-theme-params-N ...`, or the host's `ag-theme-<name>` in legacy mode);
192
+ * `AgGridAdapter.getAgGridRootElement()` is the innermost one. These wrappers must be skipped:
193
+ * they are re-classed by AG Grid on every theme change, so anything AdapTable adds/removes on them
194
+ * (legacy theme switching) would be lost.
195
+ *
196
+ * Resolution order:
197
+ * 1. the closest non-`.ag-styled-root` ancestor of the grid root (the host element)
198
+ * 2. legacy theme mode only: if the host element has no `ag-theme-*` class, the closest ancestor
199
+ * which has one (AG Grid itself accepts the theme class anywhere up the tree)
200
+ */
201
+ private deriveAgGridContainerElement;
190
202
  useRowNodeLookUp(): boolean;
191
203
  private buildInitContainerContext;
192
204
  getAgGridContainerElement(): HTMLElement | null;
@@ -198,6 +210,13 @@ export declare class AdaptableAgGrid implements IAdaptable {
198
210
  private initAdaptableStore;
199
211
  private mapAdaptableStateToAgGridState;
200
212
  private addGridEventListeners;
213
+ /**
214
+ * see #local_event_service_monkey_patch (AgGridAdapter.ts)
215
+ *
216
+ * Entry point for the AG Grid RowNode `dataChanged` / `cellChanged` events, which AG Grid does not expose publicly.
217
+ * These drive Adaptable's data change detection (CellChanged events, Alerts, Flashing Cells, etc.)
218
+ */
219
+ onAgGridRowNodeEvent(event: RowNodeEvent): void;
201
220
  private shouldAutoSizeLayout;
202
221
  private autoSizeLayoutIfNeeded;
203
222
  forPlugins(callback: (plugin: AdaptablePlugin) => any): void;
@@ -232,6 +251,11 @@ export declare class AdaptableAgGrid implements IAdaptable {
232
251
  private __forceUpdateColumnModel;
233
252
  updateColumnModelAndRefreshGrid(): void;
234
253
  _updateColumnModelAndRefreshGridNow(): void;
254
+ /**
255
+ * Re-applies the current sort, eg. after a Custom Sort was added/edited/removed at runtime.
256
+ * The column comparators are already up to date (see #stable_col_def_callbacks), AG Grid just has to sort again.
257
+ */
258
+ refreshSortIfColumnsSorted(): void;
235
259
  redrawBody(): void;
236
260
  refreshHeader(): void;
237
261
  redrawRows(rowNodes?: IRowNode[]): void;
@@ -1,5 +1,5 @@
1
1
  import { debounce } from '../Utilities/Helpers/TimingHelper';
2
- import { createGrid, LocalEventService, } from 'ag-grid-enterprise';
2
+ import { createGrid, } from 'ag-grid-enterprise';
3
3
  import { AdaptableLogger } from './AdaptableLogger';
4
4
  import { PrimaryKeyDocsLink } from '../Utilities/Constants/DocumentationLinkConstants';
5
5
  import StringExtensions from '../Utilities/Extensions/StringExtensions';
@@ -110,48 +110,9 @@ import { AgGridFilterAdapterFactory } from './AgGridFilterAdapter';
110
110
  import { AdaptableFilterHandler } from './AdaptableFilterHandler';
111
111
  import { AgGridFloatingFilterAdapterFactory } from './AgGridFloatingFilterAdapter';
112
112
  import { listenForDevTools } from '../setupDevTools';
113
- const LocalEventService_Prototype = LocalEventService.prototype;
114
- const LocalEventService_dispatchEvent = LocalEventService_Prototype.dispatchEvent;
115
- LocalEventService_Prototype.dispatchEvent = function (event) {
116
- const agGridApi = event.api;
117
- if (agGridApi?.isDestroyed()) {
118
- return;
119
- }
120
- LocalEventService_dispatchEvent.apply(this, arguments);
121
- if (event.type === 'cellChanged' || event.type === 'dataChanged') {
122
- const eventRowNode = event.node;
123
- if (!eventRowNode) {
124
- AdaptableLogger.consoleErrorBase(`No RowNode found in passed event, this should never happen!`, event);
125
- return;
126
- }
127
- const extractGridApiFromRowNode = (rowNode) => {
128
- const rowNodeApi = rowNode?.beans?.gridApi;
129
- if (!rowNodeApi) {
130
- AdaptableLogger.consoleErrorBase(`No GridAPI found in passed RowNode, this should never happen!`, rowNode);
131
- }
132
- return rowNodeApi;
133
- };
134
- if (eventRowNode) {
135
- AdaptableAgGrid.forEachAdaptable((adaptable) => {
136
- if (extractGridApiFromRowNode(eventRowNode) !== adaptable.agGridAdapter?.getAgGridApi(true)) {
137
- return;
138
- }
139
- if (adaptable.isDestroyed) {
140
- return;
141
- }
142
- const fn = adaptable.rowListeners ? adaptable.rowListeners[event.type] : null;
143
- if (fn) {
144
- fn(event);
145
- }
146
- });
147
- }
148
- }
149
- };
150
- const adaptableInstances = {};
151
113
  const publishTimestamp = Number(ADAPTABLE_PUBLISH_TIMESTAMP);
152
114
  export class AdaptableAgGrid {
153
115
  logger;
154
- _id;
155
116
  lifecycleState;
156
117
  variant;
157
118
  emitter;
@@ -188,7 +149,7 @@ export class AdaptableAgGrid {
188
149
  ThemeService;
189
150
  LicenseService;
190
151
  ChartingService;
191
- rowListeners;
152
+ isListeningToAgGridRowNodeEvents = false;
192
153
  debouncedSetSelectedRows;
193
154
  debouncedSetSelectedCells;
194
155
  agGridListenerKeydown;
@@ -226,18 +187,6 @@ export class AdaptableAgGrid {
226
187
  this.agGridModulesAdapter = new AgGridModulesAdapter(this);
227
188
  this.DataService = new DataService(this);
228
189
  }
229
- static forEachAdaptable(fn) {
230
- Object.keys(adaptableInstances).forEach((key) => {
231
- fn(adaptableInstances[key]);
232
- });
233
- }
234
- static collectInstance(adaptable, adaptableId) {
235
- adaptable._id = adaptableId;
236
- adaptableInstances[adaptable._id] = adaptable;
237
- }
238
- static dismissInstance(adaptable) {
239
- delete adaptableInstances[adaptable._id];
240
- }
241
190
  get isAgGridInitialising() {
242
191
  return this.lifecycleState === 'initAgGrid';
243
192
  }
@@ -330,7 +279,6 @@ export class AdaptableAgGrid {
330
279
  const adaptableId = this._rawAdaptableOptions.adaptableId;
331
280
  const initMarker = getMarker(adaptableId).track.Init.label.Init.start();
332
281
  const perfInitAdaptableAgGrid = this.logger.beginPerf(`Adaptable._initAdaptableAgGrid()`);
333
- AdaptableAgGrid.collectInstance(this, this._rawAdaptableOptions.adaptableId);
334
282
  this.variant = config.variant;
335
283
  this.initWithLazyData =
336
284
  config.gridOptions.rowData == undefined || config.gridOptions.rowData.length === 0;
@@ -669,6 +617,7 @@ export class AdaptableAgGrid {
669
617
  agGridApi.onFilterChanged();
670
618
  filteringApplied = true;
671
619
  }
620
+ agGridApi.refreshCells();
672
621
  this._emit('AdapTableFiltersApplied');
673
622
  return filteringApplied;
674
623
  }
@@ -1055,6 +1004,14 @@ export class AdaptableAgGrid {
1055
1004
  return result;
1056
1005
  };
1057
1006
  });
1007
+ this.agGridOptionsService.setGridOptionsProperty(gridOptions, 'getColumnMenuItems', (original_getColumnMenuItems) => {
1008
+ return (params) => {
1009
+ if (params.source === 'columnMenu') {
1010
+ return this.agGridMenuAdapter.buildColumnMenu(params, original_getColumnMenuItems);
1011
+ }
1012
+ return this.agGridMenuAdapter.buildColumnsToolPanelMenu(params);
1013
+ };
1014
+ });
1058
1015
  this.agGridOptionsService.setGridOptionsProperty(gridOptions, 'getMainMenuItems', (original_getMainMenuItems) => {
1059
1016
  return (params) => {
1060
1017
  return this.agGridMenuAdapter.buildColumnMenu(params, original_getMainMenuItems);
@@ -1300,8 +1257,7 @@ export class AdaptableAgGrid {
1300
1257
  this.logger.info('Initializing with lazy data (no initial rowData).');
1301
1258
  }
1302
1259
  if (!this.getAgGridContainerElement()) {
1303
- const gridRoot = this.agGridAdapter.getAgGridRootElement();
1304
- const gridContainer = gridRoot?.closest('[class*="ag-theme"]');
1260
+ const gridContainer = this.deriveAgGridContainerElement(agGridApi.getGridOption('theme') === 'legacy');
1305
1261
  if (!gridContainer) {
1306
1262
  this.logger.consoleError('Failed to derive AG Grid container from the framework wrapper. Please provide a valid container element via ContainerOptions.agGridContainer.');
1307
1263
  }
@@ -1321,6 +1277,27 @@ export class AdaptableAgGrid {
1321
1277
  const agGridApi = createGrid(agGridContainer, gridOptions, gridParams);
1322
1278
  return agGridApi;
1323
1279
  }
1280
+ deriveAgGridContainerElement(isLegacyTheme) {
1281
+ const gridRoot = this.agGridAdapter.getAgGridRootElement();
1282
+ if (!gridRoot) {
1283
+ return undefined;
1284
+ }
1285
+ let hostElement = gridRoot;
1286
+ while (hostElement && hostElement.classList?.contains('ag-styled-root')) {
1287
+ hostElement = hostElement.parentElement;
1288
+ }
1289
+ if (!hostElement) {
1290
+ return undefined;
1291
+ }
1292
+ const hasAgThemeClass = (element) => Array.from(element.classList ?? []).some((cls) => cls.indexOf('ag-theme-') === 0);
1293
+ if (isLegacyTheme && !hasAgThemeClass(hostElement)) {
1294
+ const themedAncestor = hostElement.parentElement?.closest('[class*="ag-theme-"]');
1295
+ if (themedAncestor && hasAgThemeClass(themedAncestor)) {
1296
+ return themedAncestor;
1297
+ }
1298
+ }
1299
+ return hostElement;
1300
+ }
1324
1301
  useRowNodeLookUp() {
1325
1302
  return this.agGridAdapter.getAgGridApi().getGridOption('getRowId') != undefined;
1326
1303
  }
@@ -1519,25 +1496,23 @@ export class AdaptableAgGrid {
1519
1496
  };
1520
1497
  this.agGridAdapter.getAgGridApi().addGlobalListener(this.listenerGlobalChartingChanges);
1521
1498
  }
1522
- this.rowListeners = {
1523
- dataChanged: (event) => {
1524
- this.onRowDataChanged({
1525
- rowNode: event.node,
1526
- oldData: event.oldData,
1527
- newData: event.newData,
1528
- });
1529
- },
1530
- cellChanged: (event) => {
1531
- if (event.column) {
1532
- this.onCellDataChanged({
1533
- rowNode: event.node,
1534
- oldValue: event.oldValue,
1535
- newValue: event.newValue,
1536
- colId: event.column.colId,
1537
- });
1538
- }
1539
- },
1540
- };
1499
+ this.isListeningToAgGridRowNodeEvents = true;
1500
+ }
1501
+ onAgGridRowNodeEvent(event) {
1502
+ if (!this.isListeningToAgGridRowNodeEvents) {
1503
+ return;
1504
+ }
1505
+ if (event.type === 'dataChanged') {
1506
+ const { node, oldData, newData } = event;
1507
+ this.onRowDataChanged({ rowNode: node, oldData, newData });
1508
+ return;
1509
+ }
1510
+ if (event.type === 'cellChanged') {
1511
+ const { node, column, oldValue, newValue } = event;
1512
+ if (column) {
1513
+ this.onCellDataChanged({ rowNode: node, oldValue, newValue, colId: column.getColId() });
1514
+ }
1515
+ }
1541
1516
  }
1542
1517
  shouldAutoSizeLayout() {
1543
1518
  const { layoutApi } = this.api;
@@ -1926,11 +1901,18 @@ export class AdaptableAgGrid {
1926
1901
  }
1927
1902
  }
1928
1903
  _updateColumnModelAndRefreshGridNow() {
1929
- this.logger.info('Updating column model and refreshing grid.');
1904
+ const startTime = performance.now();
1930
1905
  this.deriveAdaptableColumnStateFromAgGrid();
1931
1906
  this.agGridColumnAdapter.setupColumns();
1932
1907
  this.redrawBody();
1933
1908
  this.refreshHeader();
1909
+ this.logger.info(`Updated column model and refreshed grid in ${Math.round(performance.now() - startTime)}ms.`);
1910
+ }
1911
+ refreshSortIfColumnsSorted() {
1912
+ const columnSorts = this.api.layoutApi.getCurrentLayout()?.ColumnSorts;
1913
+ if (columnSorts?.length) {
1914
+ this.layoutManager.refreshSort();
1915
+ }
1934
1916
  }
1935
1917
  redrawBody() {
1936
1918
  this.redrawRows();
@@ -3415,6 +3397,7 @@ export class AdaptableAgGrid {
3415
3397
  'doesExternalFilterPass',
3416
3398
  'excelStyles',
3417
3399
  'getContextMenuItems',
3400
+ 'getColumnMenuItems',
3418
3401
  'getMainMenuItems',
3419
3402
  'getRowClass',
3420
3403
  'getRowId',
@@ -3458,13 +3441,12 @@ export class AdaptableAgGrid {
3458
3441
  this.agGridThemeAdapter = null;
3459
3442
  this.agGridModulesAdapter?.destroy();
3460
3443
  this.agGridModulesAdapter = null;
3461
- this.rowListeners = null;
3444
+ this.isListeningToAgGridRowNodeEvents = false;
3462
3445
  this.emitter.destroy();
3463
3446
  this.emitter = null;
3464
3447
  this.adaptableOptions = null;
3465
3448
  this.columnMinMaxValuesCache = null;
3466
3449
  this.lifecycleState = 'preDestroyed';
3467
- AdaptableAgGrid.dismissInstance(this);
3468
3450
  if (config?.unmount) {
3469
3451
  const abContainerElement = this.getAdaptableContainerElement();
3470
3452
  if (abContainerElement != null) {
@@ -1,9 +1,34 @@
1
- import { ColDef, ColGroupDef, Column, GridApi, GridOptions, IRowNode, ManagedGridOptionKey, ManagedGridOptions, Module } from 'ag-grid-enterprise';
1
+ import { AgEvent, ColDef, ColGroupDef, Column, GridApi, GridOptions, IRowNode, ManagedGridOptionKey, ManagedGridOptions, Module } from 'ag-grid-enterprise';
2
2
  import type { AgChartInstance, AgSparklineOptions } from 'ag-charts-types';
3
3
  import { AdaptableAgGrid } from './AdaptableAgGrid';
4
4
  import { AdaptableColumn, AdaptableColumnGroup } from '../AdaptableState/Common/AdaptableColumn';
5
5
  import { SelectedCellInfo } from '../AdaptableState/Selection/SelectedCellInfo';
6
6
  import { SelectedRowInfo } from '../AdaptableState/Selection/SelectedRowInfo';
7
+ /**
8
+ * #local_event_service_monkey_patch
9
+ *
10
+ * AG Grid does not expose the RowNode `cellChanged` and `dataChanged` events (they are dispatched only on the
11
+ * RowNode's own, lazily created, `LocalEventService`), so we override `LocalEventService.prototype.dispatchEvent`
12
+ * and hook our own data change detection into it.
13
+ *
14
+ * Since AG Grid v36, `LocalEventService` lives in the `ag-stack` package (not in `ag-grid-community`).
15
+ * Unlike a duplicated `ag-grid-community`, a duplicated `ag-stack` copy in a consumer bundle
16
+ * (different install location, CJS vs ESM, version skew) is SILENT - AG Grid keeps working,
17
+ * but a prototype patched via `import { LocalEventService } from 'ag-stack'` would never be hit
18
+ * by the grid, because the grid instantiates the class from ITS copy of the module.
19
+ *
20
+ * Because of this we don't patch the statically imported class; instead we patch the prototype
21
+ * of the `LocalEventService` instance actually used by the running grid (`beans.eventSvc.globalSvc`)
22
+ * - one patch per distinct prototype, regardless of how many copies of `ag-stack` are loaded.
23
+ *
24
+ * The patched prototype is shared by ALL grids (and all their RowNodes), so the patch identifies the
25
+ * originating grid via `rowNode.beans` and routes the event to that grid's AgGridAdapter
26
+ * (see DANGER_AG_GRID_ADAPTERS_MAP & AgGridAdapter.handleRowNodeEvent).
27
+ */
28
+ export type RowNodeEventType = 'dataChanged' | 'cellChanged';
29
+ export interface RowNodeEvent extends AgEvent<RowNodeEventType> {
30
+ node: IRowNode;
31
+ }
7
32
  export type AgGridThemeMode = 'legacy' | 'themingApi';
8
33
  export declare class AgGridAdapter {
9
34
  private _adaptableInstance;
@@ -26,6 +51,11 @@ export declare class AgGridAdapter {
26
51
  private getActivePivotColumnFilters;
27
52
  private get agGridOptionsService();
28
53
  setAgGridId(agGridId: string): void;
54
+ /**
55
+ * see #local_event_service_monkey_patch
56
+ * Called (for the grid managed by this adapter) whenever AG Grid dispatches a RowNode `cellChanged` / `dataChanged` event
57
+ */
58
+ handleRowNodeEvent(event: RowNodeEvent): void;
29
59
  setAgGridApi(gridApi: GridApi): void;
30
60
  getAgGridApi(skipLogging?: boolean): GridApi | undefined;
31
61
  monkeyPatchingGridOptionsUpdates(): void;
@@ -34,6 +64,7 @@ export declare class AgGridAdapter {
34
64
  private DANGER_getPrivateAgGridBeans;
35
65
  DANGER_getCreateSparkline(): ((options: AgSparklineOptions) => AgChartInstance<AgSparklineOptions>) | undefined;
36
66
  DANGER_getLiveGridOptions(): GridOptions<any>;
67
+ DANGER_invalidateSortCache(): void;
37
68
  getAgGridRootElement(): HTMLElement;
38
69
  /**
39
70
  * When AG Grid is rendered the first time, the AG GridApi is not yet set in the Adaptable context (as it's set only AFTER the grid is fully initialised)
@@ -1,4 +1,5 @@
1
1
  import { ColumnApiModule, } from 'ag-grid-enterprise';
2
+ import { AdaptableLogger } from './AdaptableLogger';
2
3
  import { ACTION_COLUMN_TYPE, CALCULATED_COLUMN_TYPE, FDC3_COLUMN_TYPE, FREE_TEXT_COLUMN_TYPE, } from '../AdaptableState/Common/AdaptableColumn';
3
4
  import { ACCESS_LEVEL_HIDDEN, ADAPTABLE_FDC3_ACTION_COLUMN_FRIENDLY_NAME, } from '../Utilities/Constants/GeneralConstants';
4
5
  import { createUuid } from '../AdaptableState/Uuid';
@@ -10,7 +11,84 @@ import { isPivotColumnTotal } from '../layout-manager/src/isPivotColumnTotal';
10
11
  import { isPivotAggTotalColumn } from '../layout-manager/src/isPivotAggTotalColumn';
11
12
  import { ONLY_AGG_FN_NAME, WEIGHTED_AVERAGE_AGG_FN_NAME, } from '../AdaptableState/Common/AggregationColumns';
12
13
  const DANGER_AG_GRID_BEANS_MAP = {};
14
+ const DANGER_AG_GRID_ADAPTERS_MAP = {};
13
15
  const getColumnApiModule = () => ColumnApiModule;
16
+ const PATCHED_LOCAL_EVENT_SERVICE_PROTOTYPES = new WeakSet();
17
+ const patchLocalEventServiceDispatchEvent = (beans) => {
18
+ const localEventServiceInstance = beans?.eventSvc?.globalSvc;
19
+ const LocalEventService_Prototype = localEventServiceInstance
20
+ ? Object.getPrototypeOf(localEventServiceInstance)
21
+ : null;
22
+ if (!LocalEventService_Prototype ||
23
+ typeof LocalEventService_Prototype.dispatchEvent !== 'function') {
24
+ AdaptableLogger.consoleErrorBase(`CRITICAL: could not get hold of AG Grid LocalEventService, data change detection will NOT work!`, beans);
25
+ return;
26
+ }
27
+ if (PATCHED_LOCAL_EVENT_SERVICE_PROTOTYPES.has(LocalEventService_Prototype)) {
28
+ return;
29
+ }
30
+ PATCHED_LOCAL_EVENT_SERVICE_PROTOTYPES.add(LocalEventService_Prototype);
31
+ const LocalEventService_dispatchEvent = LocalEventService_Prototype.dispatchEvent;
32
+ LocalEventService_Prototype.dispatchEvent = function (event) {
33
+ LocalEventService_dispatchEvent.apply(this, arguments);
34
+ if (event.type !== 'cellChanged' && event.type !== 'dataChanged') {
35
+ return;
36
+ }
37
+ const rowNodeEvent = event;
38
+ const rowNode = rowNodeEvent.node;
39
+ if (!rowNode) {
40
+ AdaptableLogger.consoleErrorBase(`No RowNode found in passed event, this should never happen!`, event);
41
+ return;
42
+ }
43
+ const gridId = rowNode.beans?.context?.getId();
44
+ if (!gridId) {
45
+ AdaptableLogger.consoleErrorBase(`No gridId found in passed RowNode, this should never happen!`, rowNode);
46
+ return;
47
+ }
48
+ DANGER_AG_GRID_ADAPTERS_MAP[gridId]?.handleRowNodeEvent(rowNodeEvent);
49
+ };
50
+ };
51
+ const patchVisibleColsPartition = (beans) => {
52
+ const visibleCols = beans?.visibleCols;
53
+ if (!visibleCols || typeof visibleCols.partitionVisibleCols !== 'function') {
54
+ console.error(`CRITICAL: could not get hold of AG Grid VisibleColsService.partitionVisibleCols, the selection column may NOT be displayed when it is the last column!`);
55
+ return;
56
+ }
57
+ const original_partitionVisibleCols = visibleCols.partitionVisibleCols;
58
+ visibleCols.partitionVisibleCols = function () {
59
+ const result = original_partitionVisibleCols.apply(this, arguments);
60
+ const leftCols = result?.leftCols ?? [];
61
+ const rightCols = result?.rightCols ?? [];
62
+ const centerCols = result?.centerCols ?? [];
63
+ if (leftCols.length + rightCols.length + centerCols.length === 0) {
64
+ return result;
65
+ }
66
+ const colModel = beans.colModel;
67
+ const selectionCol = colModel?.colsList?.find((col) => col?.colKind === 'selection');
68
+ if (!selectionCol || !selectionCol.visible) {
69
+ return result;
70
+ }
71
+ if (leftCols.includes(selectionCol) ||
72
+ rightCols.includes(selectionCol) ||
73
+ centerCols.includes(selectionCol)) {
74
+ return result;
75
+ }
76
+ const showAutoGroupAndValuesOnly = colModel.pivotMode && !colModel.showingPivotResult;
77
+ if (showAutoGroupAndValuesOnly && !(beans.selectionColSvc?.isEnabled?.() ?? false)) {
78
+ return result;
79
+ }
80
+ if (selectionCol.pinned === 'right') {
81
+ rightCols.push(selectionCol);
82
+ }
83
+ else if (selectionCol.pinned) {
84
+ leftCols.push(selectionCol);
85
+ }
86
+ else {
87
+ centerCols.push(selectionCol);
88
+ }
89
+ return result;
90
+ };
91
+ };
14
92
  export class AgGridAdapter {
15
93
  _adaptableInstance;
16
94
  DANGER_USE_GETTER_gridApi;
@@ -38,11 +116,14 @@ export class AgGridAdapter {
38
116
  console.error('CRITICAL: No gridId found in beans, this should never happen!');
39
117
  }
40
118
  DANGER_AG_GRID_BEANS_MAP[gridId] = beans;
119
+ patchLocalEventServiceDispatchEvent(beans);
120
+ patchVisibleColsPartition(beans);
41
121
  };
42
122
  }
43
123
  }
44
124
  destroy() {
45
125
  delete DANGER_AG_GRID_BEANS_MAP[this._agGridId];
126
+ delete DANGER_AG_GRID_ADAPTERS_MAP[this._agGridId];
46
127
  this.initialGridOptions = null;
47
128
  this.DANGER_gridApi_from_args = null;
48
129
  this.DANGER_USE_GETTER_gridApi = null;
@@ -86,6 +167,10 @@ export class AgGridAdapter {
86
167
  }
87
168
  setAgGridId(agGridId) {
88
169
  this._agGridId = agGridId;
170
+ DANGER_AG_GRID_ADAPTERS_MAP[agGridId] = this;
171
+ }
172
+ handleRowNodeEvent(event) {
173
+ this._adaptableInstance?.onAgGridRowNodeEvent(event);
89
174
  }
90
175
  setAgGridApi(gridApi) {
91
176
  this.DANGER_USE_GETTER_gridApi = gridApi;
@@ -223,6 +308,9 @@ export class AgGridAdapter {
223
308
  DANGER_getLiveGridOptions() {
224
309
  return this.DANGER_getPrivateAgGridBeans()?.gridOptions;
225
310
  }
311
+ DANGER_invalidateSortCache() {
312
+ this.DANGER_getPrivateAgGridBeans()?.sortSvc?.invalidate();
313
+ }
226
314
  getAgGridRootElement() {
227
315
  return this.DANGER_getPrivateAgGridBeans()?.eGridDiv;
228
316
  }
@@ -7,12 +7,53 @@ export declare function getEditorForColumnDataType(columnDataType: AdaptableColu
7
7
  export declare class AgGridColumnAdapter {
8
8
  private adaptableInstance;
9
9
  private colDefPropertyCache;
10
+ private workingColDefs;
11
+ private sortCacheInvalidationNeeded;
12
+ private stableCallbacks;
13
+ private stableCallbackImplementations;
14
+ private stableCallbackOwners;
15
+ private adaptableColumnFilterDef;
16
+ private adaptableFloatingFilterComponent;
10
17
  constructor(adaptableInstance: AdaptableAgGrid);
11
18
  private getVariant;
12
19
  destroy(): void;
13
20
  private get adaptableApi();
14
21
  private get adaptableOptions();
15
22
  private get agGridApi();
23
+ /**
24
+ * The colDef that `setupColumnXxx` methods should read from & write to:
25
+ * the working copy while the column is being set up (see #col_def_commit), the live colDef otherwise.
26
+ */
27
+ private getColDefForSetup;
28
+ /**
29
+ * see #col_def_commit
30
+ * Creates the working copy of the colDef, which is committed via `commitColDefChanges`
31
+ */
32
+ private beginColDefChanges;
33
+ /**
34
+ * see #col_def_commit
35
+ * Hands the working copy over to AG Grid, which re-initialises everything it caches from the colDef
36
+ * and refreshes the rendered cells of the column - but only if the colDef actually changed.
37
+ */
38
+ private commitColDefChanges;
39
+ /**
40
+ * see #stable_col_def_callbacks
41
+ * Returns the (stable) function AG Grid should see for this column & property,
42
+ * delegating to the given (latest) implementation.
43
+ */
44
+ private toStableCallback;
45
+ /**
46
+ * #pivot_result_col_def_copies_source
47
+ *
48
+ * AG Grid generates a pivot result colDef by copying the WHOLE colDef of its value (source) column
49
+ * (`PivotColDefService.createColDef` -> `Object.assign(colDef, valueColumn.colDef)`), and regenerates it
50
+ * whenever the source colDef is committed (see #col_def_commit). The copy therefore contains the
51
+ * callbacks Adaptable set up for the SOURCE column, which must not be mistaken for values provided by the user.
52
+ *
53
+ * When such a callback is found where a user value is expected, the user value of the source column
54
+ * is used instead - which is exactly what the pivot result column would have inherited from the user.
55
+ */
56
+ private resolveUserProvidedValue;
16
57
  setColDefProperty<T extends keyof ColDef>(col: Column, propertyName: T, propertyGetter: (userPropertyValue: ColDef[T]) => ColDef[T] | undefined): void;
17
58
  getUserColDefProperty<T extends keyof ColDef>(columnId: string, propertyName: T): ColDef[T] | undefined;
18
59
  shouldSkipColumn(colId: string): boolean;
@@ -22,6 +63,7 @@ export declare class AgGridColumnAdapter {
22
63
  * @param specificColumnIds - optional list of column Ids to setup. If not provided, all columns are setup
23
64
  */
24
65
  setupColumns(specificColumnIds?: string[]): void;
66
+ private setupColumn;
25
67
  private setupColumnValueGetter;
26
68
  private setupColumnCellClass;
27
69
  private setupColumnHeaderClass;
@@ -45,6 +87,8 @@ export declare class AgGridColumnAdapter {
45
87
  private setupColumnCellDataType;
46
88
  setupColumnHeader({ col }: ColumnSetupInfo): void;
47
89
  private setupColumnFilter;
90
+ private getAdaptableColumnFilterDef;
91
+ private getAdaptableFloatingFilterComponent;
48
92
  private setupColumnFloatingFilter;
49
93
  private setupColumnValueFormatter;
50
94
  private setupColumnEditable;