@adaptabletools/adaptable 23.1.0-canary.2 → 23.1.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptabletools/adaptable",
3
- "version": "23.1.0-canary.2",
3
+ "version": "23.1.1",
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",
@@ -174,9 +174,20 @@ export interface DefaultPredicateFilterContext<TData = any> extends BaseContext
174
174
  */
175
175
  export interface InFilterValue<ValueType = any> {
176
176
  /**
177
- * Item's label
177
+ * Item's label.
178
+ *
179
+ * Either a string, or a function that returns the label. The function is
180
+ * re-evaluated whenever filtering changes, so the label can embed live info
181
+ * (e.g. `visibleCount`) without `customInFilterValues` being called again:
182
+ *
183
+ * ```ts
184
+ * label: (item) => `${item.label} (${item.visibleCount})`
185
+ * ```
186
+ *
187
+ * The function receives the item's default {@link InFilterValueInfo} (same shape
188
+ * as `context.defaultValues`), looked up by `value`.
178
189
  */
179
- label: string;
190
+ label: string | InFilterValueLabelFn;
180
191
  /**
181
192
  * The value of Item being shown
182
193
  */
@@ -188,6 +199,11 @@ export interface InFilterValue<ValueType = any> {
188
199
  level?: number;
189
200
  children?: InFilterValue<ValueType>[];
190
201
  }
202
+ /**
203
+ * Computes the label of an IN Filter item from its default info
204
+ * (`count`, `visibleCount`, `visible`, ...). Re-evaluated when filtering changes.
205
+ */
206
+ export type InFilterValueLabelFn = (item: InFilterValueInfo) => string;
191
207
  /**
192
208
  * Result when providing custom values for the IN Column Filter
193
209
  */
@@ -245,6 +261,10 @@ export interface InFilterLabelContext extends BaseContext {
245
261
  * Information about items in the IN Column Filter
246
262
  */
247
263
  export interface InFilterValueInfo extends InFilterValue {
264
+ /**
265
+ * Item's default label (the Column's display value)
266
+ */
267
+ label: string;
248
268
  /**
249
269
  * Whether Item is currently selected
250
270
  */
@@ -0,0 +1,9 @@
1
+ import { InFilterValue, InFilterValueInfo } from '../AdaptableOptions/FilterOptions';
2
+ /**
3
+ * Copies an In Filter item into a plain object with a string `label`.
4
+ *
5
+ * Items coming from `customInFilterValues` may carry a live `label` getter (when
6
+ * the consumer returned a label function) - snapshotting reads the current label
7
+ * and gives UI components fresh references so they re-render the text.
8
+ */
9
+ export declare const snapshotInFilterValue: (item: InFilterValue) => InFilterValueInfo;
@@ -0,0 +1,8 @@
1
+ export const snapshotInFilterValue = (item) => {
2
+ const label = item.label;
3
+ return {
4
+ ...item,
5
+ label: typeof label === 'function' ? label(item) : label,
6
+ children: item.children ? item.children.map(snapshotInFilterValue) : undefined,
7
+ };
8
+ };
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { ColumnValuesSelectProps } from '../../FilterForm/ListBoxFilterForm';
3
- import { InFilterLabelContext } from '../../../../types';
3
+ import { InFilterValue, InFilterLabelContext } from '../../../../types';
4
4
  /** Suggested page size passed to `customInFilterValues` as `context.limit`. */
5
5
  export declare const IN_FILTER_VALUES_PAGE_SIZE = 1000;
6
6
  export interface FloatingFilterValuesProps {
@@ -24,10 +24,7 @@ export declare function useDistinctFilterColumnValues(options: {
24
24
  isDistinctColumnValuesLoading: boolean;
25
25
  isLoadingMore: boolean;
26
26
  quickFilterValues: {
27
- values: {
28
- value: any;
29
- label: string;
30
- }[];
27
+ values: import("../../../../types").InFilterValueInfo[];
31
28
  dataLoadIsComplete: boolean;
32
29
  skipDefaultSearch: boolean;
33
30
  searchValueUsedInFilterValue: boolean;
@@ -35,10 +32,7 @@ export declare function useDistinctFilterColumnValues(options: {
35
32
  inFilterLabel?: (context: InFilterLabelContext) => string;
36
33
  };
37
34
  setQuickFilterValues: React.Dispatch<React.SetStateAction<{
38
- values: {
39
- value: any;
40
- label: string;
41
- }[];
35
+ values: InFilterValue[];
42
36
  dataLoadIsComplete: boolean;
43
37
  skipDefaultSearch: boolean;
44
38
  searchValueUsedInFilterValue: boolean;
@@ -3,6 +3,7 @@ import * as React from 'react';
3
3
  import { useEffect } from 'react';
4
4
  import { useAdaptable } from '../../../AdaptableContext';
5
5
  import { ColumnValuesSelect } from '../../FilterForm/ListBoxFilterForm';
6
+ import { snapshotInFilterValue } from '../../../../Utilities/snapshotInFilterValue';
6
7
  import { AG_GRID_GROUPED_COLUMN } from '../../../../Utilities/Constants/GeneralConstants';
7
8
  const onceFlags = {};
8
9
  const logOnce = (message, loggingFn) => {
@@ -77,6 +78,37 @@ export function useDistinctFilterColumnValues(options) {
77
78
  isLoadingMoreRef.current = next;
78
79
  setIsLoadingMore(next);
79
80
  }, []);
81
+ const [labelsVersion, setLabelsVersion] = React.useState(0);
82
+ useEffect(() => {
83
+ const filterHandler = api.filterApi.columnFilterApi.internalApi.getAdaptableFilterHandler(columnId);
84
+ if (!filterHandler || typeof filterHandler.onDisplayValuesInvalidated !== 'function') {
85
+ return;
86
+ }
87
+ return filterHandler.onDisplayValuesInvalidated(() => {
88
+ const refreshed = filterHandler.getCachedFilterDisplayValues();
89
+ if (refreshed && !refreshed.hasMore) {
90
+ hasMoreRef.current = false;
91
+ accumulatedValuesRef.current = refreshed.values;
92
+ lastRequestedOffsetRef.current = 0;
93
+ setQuickFilterValues((prev) => ({
94
+ ...prev,
95
+ values: refreshed.values,
96
+ dataLoadIsComplete: true,
97
+ skipDefaultSearch: !!refreshed.skipDefaultSearch,
98
+ hasMore: false,
99
+ inFilterLabel: refreshed.inFilterLabel,
100
+ }));
101
+ return;
102
+ }
103
+ setLabelsVersion((version) => version + 1);
104
+ });
105
+ }, [api, columnId]);
106
+ const displayedQuickFilterValues = React.useMemo(() => {
107
+ return {
108
+ ...quickFilterValues,
109
+ values: quickFilterValues.values.map(snapshotInFilterValue),
110
+ };
111
+ }, [quickFilterValues, labelsVersion]);
80
112
  const fetchFilterValues = React.useCallback((offset) => {
81
113
  let searchValueUsedInFilterValue = false;
82
114
  const columnFilterInternalApi = api.filterApi.columnFilterApi.internalApi;
@@ -283,7 +315,7 @@ export function useDistinctFilterColumnValues(options) {
283
315
  return {
284
316
  isDistinctColumnValuesLoading,
285
317
  isLoadingMore,
286
- quickFilterValues,
318
+ quickFilterValues: displayedQuickFilterValues,
287
319
  setQuickFilterValues,
288
320
  triggerValuesLoad,
289
321
  };
@@ -3,6 +3,7 @@ import * as React from 'react';
3
3
  import { runIfNotResolvedIn } from '../../../Utilities/runIfNotResolvedIn';
4
4
  import { useAdaptable } from '../../AdaptableContext';
5
5
  import { useCallback } from 'react';
6
+ import { snapshotInFilterValue } from '../../../Utilities/snapshotInFilterValue';
6
7
  import { MultiCombobox, SingleCombobox, } from '../../../components/Combobox';
7
8
  export const PermittedValuesSelector = function (props) {
8
9
  const adaptable = useAdaptable();
@@ -42,7 +43,9 @@ export const PermittedValuesSelector = function (props) {
42
43
  });
43
44
  setSearchValueWasUsedWhenFiltering(searchValueUsedInFilterValue);
44
45
  setIsLoading(false);
45
- setOptions(Array.isArray(distinctColumnValues) ? distinctColumnValues : distinctColumnValues.values);
46
+ setOptions((Array.isArray(distinctColumnValues)
47
+ ? distinctColumnValues
48
+ : distinctColumnValues.values).map(snapshotInFilterValue));
46
49
  })();
47
50
  }, [props.columnId, props.loadValues, refetchKey]);
48
51
  const isCreatable = (props.allowNewValues && !dataType) || ['text', 'number'].includes(dataType);
@@ -2439,32 +2439,90 @@ export class AdaptableAgGrid {
2439
2439
  };
2440
2440
  const mapFn = (gridCell, level) => {
2441
2441
  if (level || Array.isArray(gridCell.children)) {
2442
- const cell = gridCell;
2443
- const inFilterValue = {
2444
- value: gridCell.rawValue,
2445
- label: gridCell.displayValue,
2446
- level: level ?? 0,
2447
- tooltip: false,
2448
- get visible() {
2449
- return gridCell.visible;
2450
- },
2451
- count: gridCell.count ?? 0,
2452
- leafChildrenCount: cell.leafChildrenCount ?? 0,
2453
- get visibleCount() {
2454
- return gridCell.visibleCount ?? 0;
2455
- },
2456
- isSelected: gridCell.rowNode.displayed,
2457
- children: cell.children
2458
- ? cell.children.map((v) => mapFn(v, level ? level + 1 : 1))
2459
- : undefined,
2460
- };
2461
- return inFilterValue;
2442
+ return toInFilterValueInfo(gridCell, level ?? 0);
2462
2443
  }
2463
2444
  return {
2464
2445
  value: gridCell.rawValue,
2465
2446
  label: gridCell.displayValue,
2466
2447
  };
2467
2448
  };
2449
+ const toInFilterValueInfo = (gridCell, level) => {
2450
+ const itemId = `${gridCell.rowNode?.id}-${gridCell.column?.columnId}`;
2451
+ let children;
2452
+ let childrenComputed = false;
2453
+ return {
2454
+ id: itemId,
2455
+ label: gridCell.displayValue,
2456
+ value: gridCell.rawValue,
2457
+ isSelected: gridCell.rowNode.displayed,
2458
+ count: gridCell.count ?? 0,
2459
+ tooltip: false,
2460
+ level,
2461
+ leafChildrenCount: gridCell.leafChildrenCount ?? 0,
2462
+ get children() {
2463
+ if (!childrenComputed) {
2464
+ childrenComputed = true;
2465
+ children = gridCell.children?.map((child) => mapFn(child, level + 1));
2466
+ }
2467
+ return children;
2468
+ },
2469
+ get visible() {
2470
+ return gridCell.visible;
2471
+ },
2472
+ get visibleCount() {
2473
+ return gridCell.visibleCount ?? 0;
2474
+ },
2475
+ };
2476
+ };
2477
+ let infoByValue;
2478
+ const getInfoForValue = (value) => {
2479
+ if (!infoByValue) {
2480
+ const lookup = (infoByValue = new Map());
2481
+ const visit = (gridCells, level) => {
2482
+ for (const gridCell of gridCells) {
2483
+ if (!lookup.has(gridCell.rawValue)) {
2484
+ lookup.set(gridCell.rawValue, toInFilterValueInfo(gridCell, level));
2485
+ }
2486
+ const children = gridCell.children;
2487
+ if (children) {
2488
+ visit(children, level + 1);
2489
+ }
2490
+ }
2491
+ };
2492
+ visit(getCurrentGridCells(), 0);
2493
+ }
2494
+ return infoByValue.get(value);
2495
+ };
2496
+ const resolveLabels = (values, level) => {
2497
+ return values.map((item) => {
2498
+ const children = item.children ? resolveLabels(item.children, level + 1) : undefined;
2499
+ if (typeof item.label !== 'function') {
2500
+ return children === item.children ? item : { ...item, children };
2501
+ }
2502
+ const labelFn = item.label;
2503
+ let info;
2504
+ return {
2505
+ ...item,
2506
+ children,
2507
+ get label() {
2508
+ if (!info) {
2509
+ info = getInfoForValue(item.value) ?? {
2510
+ value: item.value,
2511
+ label: item.value == null ? '' : String(item.value),
2512
+ tooltip: item.tooltip ?? false,
2513
+ level,
2514
+ isSelected: false,
2515
+ count: 0,
2516
+ visibleCount: 0,
2517
+ visible: false,
2518
+ leafChildrenCount: 0,
2519
+ };
2520
+ }
2521
+ return labelFn(info);
2522
+ },
2523
+ };
2524
+ });
2525
+ };
2468
2526
  const customInFilterValues = this.adaptableOptions.filterOptions.customInFilterValues;
2469
2527
  if (customInFilterValues) {
2470
2528
  const gridApiInternal = this.api.gridApi.internalApi;
@@ -2481,101 +2539,33 @@ export class AdaptableAgGrid {
2481
2539
  return options.currentSearchValue;
2482
2540
  },
2483
2541
  get sortedValues() {
2484
- const result = gridApiInternal
2542
+ return gridApiInternal
2485
2543
  .sortDistinctValues(getCurrentGridCells(), column)
2486
- .map((gridCell) => {
2487
- const itemId = `${gridCell.rowNode?.id}-${gridCell.column?.columnId}`;
2488
- return {
2489
- id: itemId,
2490
- label: gridCell.displayValue,
2491
- value: gridCell.rawValue,
2492
- isSelected: gridCell.rowNode.displayed,
2493
- count: gridCell.count ?? 0,
2494
- tooltip: false,
2495
- level: 0,
2496
- leafChildrenCount: gridCell.leafChildrenCount ?? 0,
2497
- get children() {
2498
- if (!gridCell.children) {
2499
- return undefined;
2500
- }
2501
- return gridCell.children.map((gridCell) => mapFn(gridCell, 1));
2502
- },
2503
- get visible() {
2504
- return gridCell.visible;
2505
- },
2506
- get visibleCount() {
2507
- return gridCell.visibleCount ?? 0;
2508
- },
2509
- };
2510
- });
2511
- return result;
2544
+ .map((gridCell) => toInFilterValueInfo(gridCell, 0));
2512
2545
  },
2513
2546
  get defaultValues() {
2514
- const result = getCurrentGridCells().map((gridCell) => {
2515
- const itemId = `${gridCell.rowNode?.id}-${gridCell.column?.columnId}`;
2516
- return {
2517
- id: itemId,
2518
- label: gridCell.displayValue,
2519
- value: gridCell.rawValue,
2520
- isSelected: gridCell.rowNode.displayed,
2521
- count: gridCell.count ?? 0,
2522
- tooltip: false,
2523
- level: 0,
2524
- leafChildrenCount: gridCell.leafChildrenCount ?? 0,
2525
- get children() {
2526
- if (!gridCell.children) {
2527
- return undefined;
2528
- }
2529
- return gridCell.children.map((gridCell) => mapFn(gridCell, 1));
2530
- },
2531
- get visible() {
2532
- return gridCell.visible;
2533
- },
2534
- get visibleCount() {
2535
- return gridCell.visibleCount ?? 0;
2536
- },
2537
- };
2538
- });
2539
- return result;
2547
+ return getCurrentGridCells().map((gridCell) => toInFilterValueInfo(gridCell, 0));
2540
2548
  },
2541
2549
  get orderedValues() {
2542
2550
  const rowNodes = gridApiInternal.getSortedRowNodesWithCurrentLayoutSortOrder();
2543
- const currentGridCells = self.getDistinctGridCellsForColumn(column, rowNodes);
2544
- const result = currentGridCells.map((gridCell) => {
2545
- const itemId = `${gridCell.rowNode?.id}-${gridCell.column?.columnId}`;
2546
- return {
2547
- id: itemId,
2548
- label: gridCell.displayValue,
2549
- value: gridCell.rawValue,
2550
- isSelected: gridCell.rowNode.displayed,
2551
- count: gridCell.count,
2552
- tooltip: false,
2553
- level: 0,
2554
- get children() {
2555
- if (!gridCell.children) {
2556
- return undefined;
2557
- }
2558
- return gridCell.children.map((gridCell) => mapFn(gridCell, 1));
2559
- },
2560
- leafChildrenCount: gridCell.leafChildrenCount ?? 0,
2561
- get visible() {
2562
- return gridCell.visible;
2563
- },
2564
- get visibleCount() {
2565
- return gridCell.visibleCount;
2566
- },
2567
- };
2568
- });
2569
- return result;
2551
+ return self
2552
+ .getDistinctGridCellsForColumn(column, rowNodes)
2553
+ .map((gridCell) => toInFilterValueInfo(gridCell, 0));
2570
2554
  },
2571
2555
  };
2572
- let customInFilterValuesResult;
2556
+ let customInFilterValuesResult = undefined;
2573
2557
  try {
2574
2558
  customInFilterValuesResult = await customInFilterValues(customInFilterValuesContext);
2575
2559
  }
2576
2560
  catch (error) {
2577
2561
  this.logger.consoleError('Failed to retrieve custom filter values.', column, error);
2578
2562
  }
2563
+ if (customInFilterValuesResult?.values) {
2564
+ return {
2565
+ ...customInFilterValuesResult,
2566
+ values: resolveLabels(customInFilterValuesResult.values, 0),
2567
+ };
2568
+ }
2579
2569
  return customInFilterValuesResult;
2580
2570
  }
2581
2571
  const values = getCurrentGridCells().map((gridCell) => mapFn(gridCell));
@@ -8,7 +8,14 @@ export declare class AdaptableFilterHandler implements FilterHandler<any, any, C
8
8
  private filterDisplayValuesResult;
9
9
  private previousFilterDisplayValuesResult;
10
10
  private rememberedInFilterValues;
11
+ private displayValuesInvalidatedListeners;
11
12
  constructor(adaptableApi: AdaptableApi);
13
+ /**
14
+ * Notifies when the cached display values are invalidated by a filter change
15
+ * (or new rows), so mounted filter components can re-read live labels.
16
+ */
17
+ onDisplayValuesInvalidated(listener: () => void): () => void;
18
+ private notifyDisplayValuesInvalidated;
12
19
  init(params: FilterHandlerParams): void;
13
20
  doesFilterPass(params: DoesFilterPassParams<any, any, ColumnFilter>): boolean;
14
21
  getCachedFilterDisplayValues(): InFilterValueResult | undefined;
@@ -4,9 +4,21 @@ export class AdaptableFilterHandler {
4
4
  filterDisplayValuesResult;
5
5
  previousFilterDisplayValuesResult;
6
6
  rememberedInFilterValues = new Map();
7
+ displayValuesInvalidatedListeners = new Set();
7
8
  constructor(adaptableApi) {
8
9
  this.adaptableApi = adaptableApi;
9
10
  }
11
+ onDisplayValuesInvalidated(listener) {
12
+ this.displayValuesInvalidatedListeners.add(listener);
13
+ return () => {
14
+ this.displayValuesInvalidatedListeners.delete(listener);
15
+ };
16
+ }
17
+ notifyDisplayValuesInvalidated() {
18
+ this.displayValuesInvalidatedListeners.forEach((listener) => {
19
+ listener();
20
+ });
21
+ }
10
22
  init(params) {
11
23
  this.colId = params.column.getColId();
12
24
  }
@@ -117,15 +129,19 @@ export class AdaptableFilterHandler {
117
129
  this.previousFilterDisplayValuesResult = this.filterDisplayValuesResult;
118
130
  }
119
131
  this.filterDisplayValuesResult = undefined;
132
+ this.notifyDisplayValuesInvalidated();
120
133
  }
121
134
  async refreshFilterDisplayValues() {
122
135
  this.resetFilterDisplayValues();
123
- return this.getFromCacheOrFetchFilterDisplayValues({ currentSearchValue: '' });
136
+ const result = await this.getFromCacheOrFetchFilterDisplayValues({ currentSearchValue: '' });
137
+ this.notifyDisplayValuesInvalidated();
138
+ return result;
124
139
  }
125
140
  refresh(_params) { }
126
141
  destroy() {
127
142
  this.filterDisplayValuesResult = undefined;
128
143
  this.previousFilterDisplayValuesResult = undefined;
129
144
  this.rememberedInFilterValues.clear();
145
+ this.displayValuesInvalidatedListeners.clear();
130
146
  }
131
147
  }
package/src/env.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export default {
2
2
  NEXT_PUBLIC_INFINITE_TABLE_LICENSE_KEY: "StartDate=2021-06-29|EndDate=2030-01-01|Owner=Adaptable|Type=distribution|TS=1624971462479|C=137829811,1004007071,2756196225,1839832928,3994409405,636616862" || '',
3
- PUBLISH_TIMESTAMP: 1789575862116 || Date.now(),
4
- VERSION: "23.1.0-canary.2" || '--current-version--',
3
+ PUBLISH_TIMESTAMP: 1791538589243 || Date.now(),
4
+ VERSION: "23.1.1" || '--current-version--',
5
5
  };
package/src/types.d.ts CHANGED
@@ -40,7 +40,7 @@ export type { FormatColumnOptions, CustomDisplayFormatter, CustomDisplayFormatte
40
40
  export type { PlusMinusOptions } from './AdaptableOptions/EditOptions';
41
41
  export type { SmartEditOptions } from './AdaptableOptions/EditOptions';
42
42
  export type { ColumnOptions, ColumnFriendlyNameContext, ColumnTypesContext, BaseColumnHeaderContext, ColumnHeaderContext, AutoGroupColumnHeaderContext, RowGroupColumnHeaderContext, TableColumnHeaderContext, TableColumnGroupHeaderContext, PivotColumnGroupHeaderContext, PivotResultColumnHeaderContext, PivotGrandTotalHeaderContext, PivotColumnTotalHeaderContext, PivotAggregationTotalHeaderContext, } from './AdaptableOptions/ColumnOptions';
43
- export type { AdaptablePersistStateFunction, AdaptableStateFunctionConfig, AdaptableLoadStateFunction, AdaptableSaveStateFunction, AdaptableClearStateFunction, StateOptions, } from './AdaptableOptions/StateOptions';
43
+ export type { AdaptablePersistStateFunction, AdaptableStateFunctionConfig, AdaptableLoadStateFunction, AdaptableSaveStateFunction, AdaptableClearStateFunction, AdaptableApplyStateFunction, StateOptions, } from './AdaptableOptions/StateOptions';
44
44
  export type { TableAggregationColumns, PivotAggregationColumns, WeightedAverageAggregation, } from './AdaptableState/Common/AggregationColumns';
45
45
  export type { AccessLevel, Entitlement } from './AdaptableState/Common/Entitlement';
46
46
  export type { ExtendedLayout, LayoutExtension, LayoutExtensionObject, LayoutExtensionModule, LayoutExtendedConfig, } from './AdaptableState/Common/ExtendedLayout';