@adaptabletools/adaptable 23.0.20 → 23.0.22

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 (40) hide show
  1. package/package.json +1 -1
  2. package/src/AdaptableOptions/FilterOptions.d.ts +26 -0
  3. package/src/AdaptableOptions/LayoutOptions.d.ts +1 -8
  4. package/src/AdaptableState/InitialState.d.ts +1 -1
  5. package/src/Api/GridApi.d.ts +5 -1
  6. package/src/Api/Implementation/FormatColumnApiImpl.d.ts +1 -0
  7. package/src/Api/Implementation/FormatColumnApiImpl.js +15 -0
  8. package/src/Api/Implementation/FreeTextColumnApiImpl.js +0 -3
  9. package/src/Api/Implementation/GridApiImpl.d.ts +1 -0
  10. package/src/Api/Implementation/GridApiImpl.js +22 -1
  11. package/src/Api/Implementation/StateApiImpl.d.ts +2 -0
  12. package/src/Api/Implementation/StateApiImpl.js +15 -0
  13. package/src/Api/Internal/ColumnFilterInternalApi.d.ts +7 -1
  14. package/src/Api/Internal/ColumnFilterInternalApi.js +3 -0
  15. package/src/Api/Internal/FormatColumnInternalApi.d.ts +5 -0
  16. package/src/Api/Internal/FormatColumnInternalApi.js +22 -0
  17. package/src/Api/Internal/StateInternalApi.d.ts +6 -1
  18. package/src/Api/Internal/StateInternalApi.js +44 -0
  19. package/src/Api/StateApi.d.ts +20 -0
  20. package/src/Redux/Store/AdaptableReduxMerger.js +24 -6
  21. package/src/Redux/Store/AdaptableStore.js +1 -1
  22. package/src/Utilities/Helpers/DisplayFormatHelper.js +16 -4
  23. package/src/Utilities/Helpers/SettingsPanelHelper.js +2 -2
  24. package/src/View/Components/ColumnFilter/components/FloatingFilterValues.d.ts +9 -0
  25. package/src/View/Components/ColumnFilter/components/FloatingFilterValues.js +21 -41
  26. package/src/View/Components/FilterForm/ListBoxFilterForm.js +16 -14
  27. package/src/View/Components/PredicateEditor/PredicateEditor.js +7 -1
  28. package/src/View/FormatColumn/Wizard/FormatColumnFormatWizardSection.js +6 -2
  29. package/src/agGrid/AdaptableAgGrid.d.ts +1 -0
  30. package/src/agGrid/AdaptableAgGrid.js +15 -0
  31. package/src/agGrid/AdaptableFilterHandler.d.ts +5 -1
  32. package/src/agGrid/AdaptableFilterHandler.js +44 -0
  33. package/src/components/Combobox/comboboxUtils.d.ts +6 -0
  34. package/src/components/Combobox/comboboxUtils.js +1 -1
  35. package/src/components/Combobox/index.d.ts +11 -0
  36. package/src/components/Combobox/index.js +16 -5
  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/tsconfig.esm.tsbuildinfo +1 -1
@@ -118,6 +118,7 @@ export function useDistinctFilterColumnValues(options) {
118
118
  skipDefaultSearch: !!result.skipDefaultSearch,
119
119
  searchValueUsedInFilterValue: prev.searchValueUsedInFilterValue || searchValueUsedInFilterValue,
120
120
  hasMore: !!result.hasMore,
121
+ renderInputValues: result.renderInputValues,
121
122
  dataLoadIsComplete: true,
122
123
  }));
123
124
  })
@@ -171,6 +172,7 @@ export function useDistinctFilterColumnValues(options) {
171
172
  skipDefaultSearch: !!distinctFilterDisplayValues.skipDefaultSearch,
172
173
  searchValueUsedInFilterValue,
173
174
  hasMore: !!distinctFilterDisplayValues.hasMore,
175
+ renderInputValues: distinctFilterDisplayValues.renderInputValues,
174
176
  });
175
177
  lastRequestedOffsetRef.current = 0;
176
178
  setIsDistinctColumnValuesLoading(false);
@@ -183,17 +185,17 @@ export function useDistinctFilterColumnValues(options) {
183
185
  if (!quickFilterValues.hasMore || !quickFilterValues.dataLoadIsComplete) {
184
186
  return;
185
187
  }
186
- let scrollEl = null;
187
188
  let debounceTimer;
188
- const onScroll = () => {
189
+ const onScroll = (event) => {
190
+ const target = event.target;
191
+ if (!(target instanceof Element) || !target.matches(FILTER_VALUES_SCROLL_SELECTOR)) {
192
+ return;
193
+ }
189
194
  if (debounceTimer) {
190
195
  clearTimeout(debounceTimer);
191
196
  }
192
197
  debounceTimer = setTimeout(() => {
193
- if (!scrollEl) {
194
- return;
195
- }
196
- const el = scrollEl;
198
+ const el = target;
197
199
  const remaining = el.scrollHeight - el.scrollTop - el.clientHeight;
198
200
  if (remaining > NEAR_END_PX) {
199
201
  return;
@@ -201,45 +203,14 @@ export function useDistinctFilterColumnValues(options) {
201
203
  loadMoreValues();
202
204
  }, SCROLL_DEBOUNCE_MS);
203
205
  };
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);
206
+ document.addEventListener('scroll', onScroll, { capture: true, passive: true });
223
207
  return () => {
224
- if (raf != null) {
225
- cancelAnimationFrame(raf);
226
- }
227
- if (timeout != null) {
228
- clearTimeout(timeout);
229
- }
230
208
  if (debounceTimer) {
231
209
  clearTimeout(debounceTimer);
232
210
  }
233
- if (scrollEl) {
234
- scrollEl.removeEventListener('scroll', onScroll);
235
- }
211
+ document.removeEventListener('scroll', onScroll, { capture: true });
236
212
  };
237
- }, [
238
- quickFilterValues.hasMore,
239
- quickFilterValues.dataLoadIsComplete,
240
- quickFilterValues.values.length,
241
- loadMoreValues,
242
- ]);
213
+ }, [quickFilterValues.hasMore, quickFilterValues.dataLoadIsComplete, loadMoreValues]);
243
214
  useEffect(() => {
244
215
  if (!hasMoreRef.current ||
245
216
  !quickFilterValues.dataLoadIsComplete ||
@@ -366,7 +337,10 @@ export const FloatingFilterValues = (props) => {
366
337
  triggerValuesLoad();
367
338
  }
368
339
  }, []);
369
- const { skipDefaultSearch } = quickFilterValues;
340
+ const { skipDefaultSearch, renderInputValues } = quickFilterValues;
341
+ const renderComboboxInputValues = React.useMemo(() => renderInputValues
342
+ ? (items) => renderInputValues({ selectedValues: items, selectedCount: items.length })
343
+ : undefined, [renderInputValues]);
370
344
  if (props.columnId === AG_GRID_GROUPED_COLUMN) {
371
345
  if (Array.isArray(props.value) && !props.value.every((value) => Array.isArray(value))) {
372
346
  logOnce(`All the inputs for the IN predicate for the column "${props.columnId}" need to be arrays. Example:
@@ -389,11 +363,17 @@ export const FloatingFilterValues = (props) => {
389
363
  ...props.selectProps,
390
364
  'data-name': 'Select Values',
391
365
  showSelectedCount: true,
366
+ ...(renderComboboxInputValues
367
+ ? { renderInputValues: renderComboboxInputValues }
368
+ : {}),
392
369
  filter: skipDefaultSearch ? null : undefined,
393
370
  onInputValueChange: onInputChange,
394
371
  }
395
372
  : {
396
373
  ...props.selectProps,
374
+ ...(renderComboboxInputValues
375
+ ? { renderInputValues: renderComboboxInputValues }
376
+ : {}),
397
377
  filter: skipDefaultSearch ? null : undefined,
398
378
  onInputValueChange: onInputChange,
399
379
  }, disabled: props.disabled, onMenuOpen: onMenuOpen, isLoading: isDistinctColumnValuesLoading, column: currentColumn, dataType: currentColumn.dataType, options: quickFilterValues.values, value: props.value, onChange: props.onChange }));
@@ -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;
@@ -115,9 +117,9 @@ export const ColumnValuesSelect = (props) => {
115
117
  }
116
118
  }
117
119
  : undefined;
118
- const component = column.dataType === 'date' || column.columnId === AG_GRID_GROUPED_COLUMN ? (_jsx(GridFilterTreeDropdown, { toDisplayValue: treeDropdownToDisplayValue, resizable: true, onValueChange: column.dataType === 'date'
120
+ const component = column.dataType === 'date' || column.columnId === AG_GRID_GROUPED_COLUMN ? (_jsx(GridFilterTreeDropdown, { toDisplayValue: treeDropdownToDisplayValue, renderInputValues: selectProps.renderInputValues, 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();
@@ -64,7 +64,10 @@ export const PredicateEditor = (props) => {
64
64
  triggerValuesLoad();
65
65
  }
66
66
  }, []);
67
- const { skipDefaultSearch } = quickFilterValues;
67
+ const { skipDefaultSearch, renderInputValues } = quickFilterValues;
68
+ const renderComboboxInputValues = React.useMemo(() => renderInputValues
69
+ ? (items) => renderInputValues({ selectedValues: items, selectedCount: items.length })
70
+ : undefined, [renderInputValues]);
68
71
  return (_jsxs(Box, { className: baseClassName, children: [_jsxs(Flex, { alignItems: "center", className: "twa:justify-stretch", children: [icon && _jsx(Tag, { className: "twa:mr-2", children: icon }), _jsx(SingleSelect, { items: predicateDefsOptions, "data-name": "select-predicate", placeholder: "Select Rule", value: props.predicate?.PredicateId ?? null, onValueChange: (value) => {
69
72
  const option = predicateDefsOptions.find((x) => x.value === value);
70
73
  props.onChange({
@@ -75,5 +78,8 @@ export const PredicateEditor = (props) => {
75
78
  } })] }), _jsx(Flex, { children: currentPredicateDef?.inputs?.map((predicateDefInput, index) => (_jsxs(_Fragment, { children: [index > 0 && _jsx(HelpBlock, { className: "twa:m-2", children: "AND" }), _jsx(Flex, { className: "twa:flex-1", flexDirection: "column", children: _jsx(AdaptableInput, { "data-name": `predicate-input-${index}`, className: "twa:mt-2", type: predicateDefInput.type, autoFocus: index === 0, value: props.predicate.Inputs?.[index] ?? '', onChange: (e) => handlePredicateInputChange(e, index) }) }, index)] }))) }), adaptable.api.predicateApi.internalApi.IsInOrNotInPredicateDef(currentPredicateDef) && (_jsx(Box, { className: "twa:mt-2", children: _jsx(ColumnValuesSelect, { isLoading: isDistinctColumnValuesLoading, column: column, options: quickFilterValues.values, onMenuOpen: onMenuOpen, selectProps: {
76
79
  onInputValueChange: onInputChange,
77
80
  filter: skipDefaultSearch ? null : undefined,
81
+ ...(renderComboboxInputValues
82
+ ? { renderInputValues: renderComboboxInputValues }
83
+ : {}),
78
84
  }, dataType: column.dataType, value: props.predicate.Inputs, onChange: handlePredicateValuesChange }) }))] }));
79
85
  };
@@ -71,7 +71,7 @@ const appendNumberFormatOptions = (items, options) => {
71
71
  });
72
72
  }
73
73
  appendDisplayFormatItem(items, 'Content', options.Content);
74
- if (options.Multiplier != null && options.Multiplier !== 1) {
74
+ if (options.Multiplier != null && options.Multiplier !== 0 && options.Multiplier !== 1) {
75
75
  items.push({ label: 'Multiplier', value: String(options.Multiplier) });
76
76
  }
77
77
  appendDisplayFormatItem(items, 'Parentheses', options.Parentheses);
@@ -342,7 +342,11 @@ const renderNumberFormat = (data, onChange, setFormatOption, scopedCustomFormatt
342
342
  ? clamp(Number(e.currentTarget.value), 0, 20)
343
343
  : undefined) }) }), _jsx(FormRow, { label: "Integer Digits", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "integer-digits", type: "number", min: "0", value: resolved.Options.IntegerDigits, onChange: (e) => setFormatOption('IntegerDigits', StringExtensions.IsNumeric(e.currentTarget.value)
344
344
  ? clamp(Number(e.currentTarget.value), 0, 20)
345
- : undefined) }) }), _jsx(FormRow, { label: "Fraction Separator", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "fraction-separator", value: resolved.Options.FractionSeparator ?? '', onChange: (e) => setFormatOption('FractionSeparator', e.currentTarget.value) }) }), _jsx(FormRow, { label: "Integer Separator", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "integer-separator", value: resolved.Options.IntegerSeparator ?? '', onChange: (e) => setFormatOption('IntegerSeparator', e.currentTarget.value) }) })] }), _jsxs(FormLayout, { sizes: NUMBER_FORMAT_VALUE_FORM_SIZES, columns: [NUMBER_FORMAT_LABEL_COLUMN, NUMBER_FORMAT_VALUE_CHILDREN_COLUMN], className: "twa:mr-5 twa:shrink-0", children: [_jsx(FormRow, { label: "Multiplier", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "multiplier", type: "number", value: resolved.Options.Multiplier, onChange: (e) => setFormatOption('Multiplier', Number(e.currentTarget.value)) }) }), _jsx(FormRow, { label: "Prefix", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "prefix", value: resolved.Options.Prefix ?? '', onChange: (e) => setFormatOption('Prefix', e.currentTarget.value) }) }), _jsx(FormRow, { label: "Suffix", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "suffix", value: resolved.Options.Suffix ?? '', onChange: (e) => setFormatOption('Suffix', e.currentTarget.value) }) }), _jsx(FormRow, { label: "Zero Display", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "zero-display", value: resolved.Options.ZeroDisplay === undefined ||
345
+ : undefined) }) }), _jsx(FormRow, { label: "Fraction Separator", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "fraction-separator", value: resolved.Options.FractionSeparator ?? '', onChange: (e) => setFormatOption('FractionSeparator', e.currentTarget.value) }) }), _jsx(FormRow, { label: "Integer Separator", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "integer-separator", value: resolved.Options.IntegerSeparator ?? '', onChange: (e) => setFormatOption('IntegerSeparator', e.currentTarget.value) }) })] }), _jsxs(FormLayout, { sizes: NUMBER_FORMAT_VALUE_FORM_SIZES, columns: [NUMBER_FORMAT_LABEL_COLUMN, NUMBER_FORMAT_VALUE_CHILDREN_COLUMN], className: "twa:mr-5 twa:shrink-0", children: [_jsx(FormRow, { label: "Multiplier", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "multiplier", type: "number", value: typeof resolved.Options.Multiplier === 'number'
346
+ ? resolved.Options.Multiplier
347
+ : '', onChange: (e) => setFormatOption('Multiplier', StringExtensions.IsNumeric(e.currentTarget.value)
348
+ ? Number(e.currentTarget.value)
349
+ : undefined) }) }), _jsx(FormRow, { label: "Prefix", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "prefix", value: resolved.Options.Prefix ?? '', onChange: (e) => setFormatOption('Prefix', e.currentTarget.value) }) }), _jsx(FormRow, { label: "Suffix", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "suffix", value: resolved.Options.Suffix ?? '', onChange: (e) => setFormatOption('Suffix', e.currentTarget.value) }) }), _jsx(FormRow, { label: "Zero Display", children: _jsx(Input, { className: NUMBER_FORMAT_INPUT_CLASS, "data-name": "zero-display", value: resolved.Options.ZeroDisplay === undefined ||
346
350
  resolved.Options.ZeroDisplay === '0'
347
351
  ? '0'
348
352
  : resolved.Options.ZeroDisplay, onChange: (e) => {
@@ -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;
@@ -3,6 +3,7 @@ export class AdaptableFilterHandler {
3
3
  colId;
4
4
  filterDisplayValuesResult;
5
5
  previousFilterDisplayValuesResult;
6
+ rememberedInFilterValues = new Map();
6
7
  constructor(adaptableApi) {
7
8
  this.adaptableApi = adaptableApi;
8
9
  }
@@ -44,6 +45,7 @@ export class AdaptableFilterHandler {
44
45
  return this.fetchFilterDisplayValues(options);
45
46
  }
46
47
  async fetchFilterDisplayValues(options) {
48
+ this.rememberSelectedLazyInFilterValues(undefined);
47
49
  return this.adaptableApi.gridApi.internalApi
48
50
  .getDistinctFilterDisplayValuesForColumn({
49
51
  columnId: this.colId,
@@ -55,10 +57,51 @@ export class AdaptableFilterHandler {
55
57
  limit: options.limit,
56
58
  })
57
59
  .then((result) => {
60
+ this.rememberSelectedLazyInFilterValues(result);
58
61
  this.filterDisplayValuesResult = result;
59
62
  return result;
60
63
  });
61
64
  }
65
+ rememberSelectedLazyInFilterValues(result) {
66
+ if (!this.adaptableApi.optionsApi.getFilterOptions().customInFilterValues) {
67
+ return;
68
+ }
69
+ const paginatedResult = result ?? this.getLastCachedFilterDisplayValues();
70
+ if (paginatedResult?.hasMore === undefined) {
71
+ return;
72
+ }
73
+ const columnFilter = this.adaptableApi.filterApi.columnFilterApi.getColumnFilterForColumn(this.colId);
74
+ const selectedInputs = new Set((columnFilter?.Predicates ?? [])
75
+ .filter((predicate) => predicate.PredicateId === 'In' || predicate.PredicateId === 'NotIn')
76
+ .flatMap((predicate) => (Array.isArray(predicate.Inputs) ? predicate.Inputs : [])));
77
+ for (const value of this.rememberedInFilterValues.keys()) {
78
+ if (!selectedInputs.has(value)) {
79
+ this.rememberedInFilterValues.delete(value);
80
+ }
81
+ }
82
+ if (!selectedInputs.size) {
83
+ return;
84
+ }
85
+ const visit = (values) => {
86
+ if (!values) {
87
+ return;
88
+ }
89
+ for (const item of values) {
90
+ if (selectedInputs.has(item.value)) {
91
+ this.rememberedInFilterValues.set(item.value, item);
92
+ }
93
+ visit(item.children);
94
+ }
95
+ };
96
+ visit(this.getLastCachedFilterDisplayValues()?.values);
97
+ visit(result?.values);
98
+ }
99
+ getRememberedLazyInFilterValues() {
100
+ return Array.from(this.rememberedInFilterValues.values());
101
+ }
102
+ getRememberedLazyInFilterValue(value) {
103
+ return this.rememberedInFilterValues.get(value);
104
+ }
62
105
  onNewRowsLoaded() {
63
106
  this.resetFilterDisplayValues();
64
107
  }
@@ -83,5 +126,6 @@ export class AdaptableFilterHandler {
83
126
  destroy() {
84
127
  this.filterDisplayValuesResult = undefined;
85
128
  this.previousFilterDisplayValuesResult = undefined;
129
+ this.rememberedInFilterValues.clear();
86
130
  }
87
131
  }
@@ -57,6 +57,12 @@ export type ComboboxCommonProps<T extends ComboboxItemType> = {
57
57
  * is cleared on close and every open starts from the full list.
58
58
  */
59
59
  persistFilterOnReopen?: boolean;
60
+ /**
61
+ * Extra items used only to resolve selected values that are not in `items`
62
+ * (e.g. selected In Filter values hidden by the current search page).
63
+ * Not shown in the dropdown list.
64
+ */
65
+ lookupItems?: T[];
60
66
  /**
61
67
  * Custom filter function, or `null` to disable built-in filtering
62
68
  * (e.g. when options are already filtered server-side).
@@ -17,7 +17,7 @@ export function valueToItem(stringValue, lookup) {
17
17
  export function valueToItems(stringValues, lookup) {
18
18
  if (stringValues == null)
19
19
  return undefined;
20
- return stringValues.map((v) => lookup.get(v)).filter(Boolean);
20
+ return stringValues.map((v) => lookup.get(v) ?? { value: v, label: String(v ?? '') });
21
21
  }
22
22
  export function toStringValue(item) {
23
23
  return item?.value ?? null;
@@ -33,7 +33,18 @@ export type MultiComboboxProps<T extends ComboboxItemType> = Omit<AdaptableCombo
33
33
  } & ComboboxDataProps<T>;
34
34
  export declare const MultiCombobox: <T extends ComboboxItemType>(props: MultiComboboxProps<T>) => React.JSX.Element;
35
35
  export type GridFilterComboboxProps<T extends ComboboxItemType> = Omit<MultiComboboxProps<T>, 'showSelectAllCheckbox' | 'searchable' | 'renderInput' | 'renderInputValues'> & {
36
+ /**
37
+ * When true, prefix the selected values in the collapsed input with the
38
+ * selected count (e.g. `(3)`). Ignored when `renderInputValues` is provided.
39
+ */
36
40
  showSelectedCount?: boolean;
41
+ /**
42
+ * Custom content for the selected values in the collapsed input (rendered inside the
43
+ * standard wrapper). When provided, the built-in selected count is not rendered -
44
+ * include one in the returned content if needed.
45
+ * When omitted, selected labels are joined with a comma.
46
+ */
47
+ renderInputValues?: (items: T[]) => React.ReactNode;
37
48
  };
38
49
  export declare const GRID_FILTER_COMBBOX_ADJUSTMENTS_CLASSNAME: string;
39
50
  export declare const GridFilterCombobox: <T extends ComboboxItemType>(props: GridFilterComboboxProps<T>) => React.JSX.Element;
@@ -196,9 +196,18 @@ const useGetAllItems = (props) => {
196
196
  ?
197
197
  [].concat(props.items)
198
198
  : props.items;
199
+ const lookupItems = props.lookupItems;
199
200
  const itemByValue = useMemo(() => {
200
- return new Map(allItems.map((item) => [item.value, item]));
201
- }, [allItems]);
201
+ const map = new Map(allItems.map((item) => [item.value, item]));
202
+ if (lookupItems) {
203
+ for (const item of lookupItems) {
204
+ if (!map.has(item.value)) {
205
+ map.set(item.value, item);
206
+ }
207
+ }
208
+ }
209
+ return map;
210
+ }, [allItems, lookupItems]);
202
211
  const creatableInput = useCreatable(props, allItems, itemByValue);
203
212
  const { inputValue, onInputValueChange } = useMenulistFilterInput(props, {
204
213
  inputValue: creatableInput.inputValue,
@@ -558,7 +567,7 @@ export const MultiCombobox = (props) => {
558
567
  };
559
568
  export const GRID_FILTER_COMBBOX_ADJUSTMENTS_CLASSNAME = cn('twa:[.ab-FloatingFilter_&]:border-none twa:[.ab-FloatingFilter_&]:inset-y-0.5 twa:[.ab-FloatingFilter_&]:inset-x-0 twa:[.ab-FloatingFilter_&]:absolute twa:[.ab-FloatingFilter_&]:shadow-none twa:[.ab-FloatingFilter_&]:min-h-auto!', 'twa:[.ab-FloatingFilter_&]:[line-height:1]', 'twa:[.ab-FloatingFilter_[data-slot=combobox-trigger]]:py-0', 'twa:[.ab-FloatingFilter_&]:[container-type:size]');
560
569
  export const GridFilterCombobox = (props) => {
561
- const { showSelectedCount = false, placeholder = 'Select...', ...comboboxProps } = props;
570
+ const { showSelectedCount = false, placeholder = 'Select...', renderInputValues: renderInputValuesProp, ...comboboxProps } = props;
562
571
  const sharedProps = {
563
572
  placeholder,
564
573
  showSelectAllCheckbox: true,
@@ -570,11 +579,13 @@ export const GridFilterCombobox = (props) => {
570
579
  showClear: false,
571
580
  showTrigger: false,
572
581
  renderInputValues: (items) => {
573
- let children = items.map((item) => getItemTextLabel(item)).join(', ');
582
+ let children = renderInputValuesProp
583
+ ? renderInputValuesProp(items)
584
+ : items.map((item) => getItemTextLabel(item)).join(', ');
574
585
  if (items.length === 0) {
575
586
  children = (_jsx("span", { "data-name": "placeholder", className: "twa:text-muted-foreground", children: placeholder }));
576
587
  }
577
- return (_jsx(_Fragment, { children: _jsxs("div", { className: "twa:text-ellipsis twa:overflow-hidden twa:whitespace-nowrap twa:flex-1000", "data-slot": "combobox-selected-values", children: [showSelectedCount && items.length > 0 && (_jsxs("span", { "data-name": "multiple-values-count", className: "twa:mr-0.5", children: ["(", items.length, ")"] })), children] }) }));
588
+ return (_jsx(_Fragment, { children: _jsxs("div", { className: "twa:text-ellipsis twa:overflow-hidden twa:whitespace-nowrap twa:flex-1000", "data-slot": "combobox-selected-values", children: [showSelectedCount && !renderInputValuesProp && items.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("span", { "data-name": "multiple-values-count", children: ["(", items.length, ")"] }), ' '] })), children] }) }));
578
589
  },
579
590
  };
580
591
  const className = cn(GRID_FILTER_COMBBOX_ADJUSTMENTS_CLASSNAME, comboboxProps.className);
@@ -48,7 +48,18 @@ export type GridFilterTreeDropdownProps<T extends TreeListItem<any>> = Omit<NewT
48
48
  /**
49
49
  * When true, prefix the selected-values summary with `(N)` where N is the
50
50
  * number of selected leaves. Mirrors `GridFilterCombobox.showSelectedCount`.
51
+ * Ignored when `renderInputValues` is provided.
51
52
  */
52
53
  showSelectedCount?: boolean;
54
+ /**
55
+ * Custom content for the selected values in the collapsed input (rendered inside the
56
+ * standard wrapper). Mirrors `GridFilterCombobox.renderInputValues`: called with one
57
+ * item per selected leaf (`value` is the full leaf path, `label` its display label).
58
+ * When provided, the built-in selected count is not rendered.
59
+ */
60
+ renderInputValues?: (items: {
61
+ value: any;
62
+ label: string;
63
+ }[]) => React.ReactNode;
53
64
  };
54
65
  export declare function GridFilterTreeDropdown<T extends TreeListItem<any>>(props: GridFilterTreeDropdownProps<T>): React.JSX.Element;
@@ -399,17 +399,20 @@ export function TreeDropdown(props) {
399
399
  return (_jsx(MultiCombobox, { ...comboboxProps }));
400
400
  }
401
401
  export function GridFilterTreeDropdown(props) {
402
- const { showSelectedCount = false, placeholder = 'Select...', className, ...rest } = props;
402
+ const { showSelectedCount = false, placeholder = 'Select...', renderInputValues: renderInputValuesProp, className, ...rest } = props;
403
403
  const renderSelectedValues = useCallback(({ selectedLeafPaths, getLabelForPath }) => {
404
404
  let children;
405
405
  if (selectedLeafPaths.length === 0) {
406
406
  children = (_jsx("span", { "data-name": "placeholder", className: "twa:text-muted-foreground", children: placeholder }));
407
407
  }
408
+ else if (renderInputValuesProp) {
409
+ children = renderInputValuesProp(selectedLeafPaths.map((path) => ({ value: path, label: getLabelForPath(path) })));
410
+ }
408
411
  else {
409
412
  children = selectedLeafPaths.map((path) => getLabelForPath(path)).join(', ');
410
413
  }
411
- return (_jsxs("div", { className: "twa:text-ellipsis twa:overflow-hidden twa:whitespace-nowrap twa:flex-1000", "data-slot": "combobox-selected-values", children: [showSelectedCount && selectedLeafPaths.length > 0 && (_jsxs("span", { "data-name": "multiple-values-count", className: "twa:mr-0.5", children: ["(", selectedLeafPaths.length, ")"] })), children] }));
412
- }, [placeholder, showSelectedCount]);
414
+ return (_jsxs("div", { className: "twa:text-ellipsis twa:overflow-hidden twa:whitespace-nowrap twa:flex-1000", "data-slot": "combobox-selected-values", children: [showSelectedCount && !renderInputValuesProp && selectedLeafPaths.length > 0 && (_jsxs("span", { "data-name": "multiple-values-count", className: "twa:mr-0.5", children: ["(", selectedLeafPaths.length, ")"] })), children] }));
415
+ }, [placeholder, showSelectedCount, renderInputValuesProp]);
413
416
  const mergedClassName = cn(GRID_FILTER_COMBBOX_ADJUSTMENTS_CLASSNAME, className);
414
417
  return (_jsx(TreeDropdown, { ...rest, placeholder: placeholder, className: mergedClassName, showClear: rest.showClear ?? false, resizable: rest.resizable ?? true, renderSelectedValues: renderSelectedValues }));
415
418
  }
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: 1786537581701 || Date.now(),
4
- VERSION: "23.0.20" || '--current-version--',
3
+ PUBLISH_TIMESTAMP: 1789246590646 || Date.now(),
4
+ VERSION: "23.0.22" || '--current-version--',
5
5
  };