@adaptabletools/adaptable 20.1.3 → 20.1.5

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.
@@ -921,10 +921,10 @@ const adaptableMiddleware = (adaptable) => (function(middlewareAPI) {
921
921
  /**
922
922
  * Use Case: Column Filters have changed
923
923
  * Action: Apply Column Filtering and fire associated events
924
+ * Note: For LAYOUT_COLUMN_FILTER_EDIT we do not filter (as it seems to be already handled)
924
925
  */
925
926
  case LayoutRedux.LAYOUT_COLUMN_FILTER_ADD:
926
- // see ref "also-this-handles-the-LAYOUT_COLUMN_FILTER_EDIT-case"
927
- // case LayoutRedux.LAYOUT_COLUMN_FILTER_EDIT:
927
+ case LayoutRedux.LAYOUT_COLUMN_FILTER_EDIT:
928
928
  case LayoutRedux.LAYOUT_COLUMN_FILTER_SET:
929
929
  case LayoutRedux.LAYOUT_COLUMN_FILTER_CLEAR:
930
930
  case LayoutRedux.LAYOUT_COLUMN_FILTER_CLEAR_ALL:
@@ -937,11 +937,12 @@ const adaptableMiddleware = (adaptable) => (function(middlewareAPI) {
937
937
  const shouldTriggerColumnFiltering = adaptable.api.filterApi.columnFilterApi.internalApi.shouldNewColumnFilterTriggerColumnFiltering(action);
938
938
  const oldLayoutState = middlewareAPI.getState().Layout;
939
939
  returnAction = next(action);
940
- setTimeout(() => {
941
- if (shouldTriggerColumnFiltering) {
940
+ if (shouldTriggerColumnFiltering &&
941
+ action.type !== LayoutRedux.LAYOUT_COLUMN_FILTER_EDIT) {
942
+ setTimeout(() => {
942
943
  adaptable.applyFiltering();
943
- }
944
- }, 50);
944
+ }, 50);
945
+ }
945
946
  adaptable.api.eventApi.internalApi.fireColumnFilterAppliedEvent();
946
947
  adaptable.api.eventApi.internalApi.fireLayoutChangedEvent(action.type, oldLayoutState, middlewareAPI.getState().Layout);
947
948
  return returnAction;
@@ -35,7 +35,6 @@ export class DataService {
35
35
  if (cellDataChangedInfo.newValue != cellDataChangedInfo.oldValue) {
36
36
  this.emitter.emitSync('CellDataChanged', cellDataChangedInfo);
37
37
  this.adaptable.api.eventApi.internalApi.fireCellChangedEvent(cellDataChangedInfo);
38
- console.log('cell changed called');
39
38
  const dataChangeLogEntry = this.extractDataChangeLogEntry(cellDataChangedInfo);
40
39
  this.cellDataChangeLogSubject$.next(dataChangeLogEntry);
41
40
  }
@@ -29,7 +29,7 @@ export const ColumnValuesSelect = (props) => {
29
29
  minWidth: `var(--ab-cmp-select-column-menu-${column.columnId}__min-width, var(--ab-cmp-select-column-menu__min-width, 160px))`,
30
30
  };
31
31
  }, [column.columnId]);
32
- const component = (React.createElement(Select, { isMulti: true, showHeaderSelectionCheckbox: true, searchable: true, closeMenuOnSelect: false, menuStyle: menuStyle, ...props.selectProps, options: options, value: value, isLoading: props.isLoading, onChange: props.onChange }));
32
+ const component = (React.createElement(Select, { key: "select", isMulti: true, resizable: true, showHeaderSelectionCheckbox: true, searchable: true, closeMenuOnSelect: false, menuStyle: menuStyle, ...props.selectProps, options: options, value: value, isLoading: props.isLoading, onChange: props.onChange }));
33
33
  return (React.createElement("div", { className: join(baseClassName, props.isLoading && `${baseClassName}--loading`, !value.length && `${baseClassName}--empty`), onKeyDownCapture: (e) => {
34
34
  const event = e.nativeEvent || e;
35
35
  event.stopPropagation = () => {
@@ -1353,8 +1353,8 @@ You need to define at least one Layout!`);
1353
1353
  * Action2: Set Selected Cells (on a debounce)
1354
1354
  */
1355
1355
  this.agGridAdapter.getAgGridApi().addEventListener('sortChanged', (this.listenerSortChanged = () => {
1356
- this.api.eventApi.internalApi.fireGridSortedEvent();
1357
1356
  this.debouncedSetSelectedCells();
1357
+ this.api.eventApi.internalApi.fireGridSortedEvent();
1358
1358
  }));
1359
1359
  /**
1360
1360
  * Use Case: Charts have been created or destroyed, Chart ranges selected or Chart options changed
@@ -1557,8 +1557,11 @@ You need to define at least one Layout!`);
1557
1557
  if (isPivot) {
1558
1558
  // let's also support the pivot result columns
1559
1559
  const pivotResultColumns = gridApi.getPivotResultColumns() || [];
1560
+ const autoGroupColumns = gridApi
1561
+ .getAllGridColumns()
1562
+ .filter((column) => this.api.columnApi.isAutoRowGroupColumn(column.getColId()));
1560
1563
  if (pivotResultColumns.length) {
1561
- agGridCols = [...agGridCols, ...pivotResultColumns];
1564
+ agGridCols = [...agGridCols, ...pivotResultColumns, ...autoGroupColumns];
1562
1565
  }
1563
1566
  }
1564
1567
  const columnGroupChildren = gridApi
@@ -3042,7 +3045,7 @@ You need to define at least one Layout!`);
3042
3045
  // and is specific to Adaptable, therefore we need to refresh it manually
3043
3046
  this.refreshHeader();
3044
3047
  const layoutModel = layoutStateToLayoutModel(layout);
3045
- this.layoutManager?.setLayout(layoutModel, {
3048
+ const layoutChanged = this.layoutManager?.setLayout(layoutModel, {
3046
3049
  skipApplyRowGroupsExpandedState: !shouldUpdateExpandState,
3047
3050
  });
3048
3051
  if (layout.AutoSizeColumns) {
@@ -3065,6 +3068,15 @@ You need to define at least one Layout!`);
3065
3068
  plugin.afterSetLayout(this, layout);
3066
3069
  }
3067
3070
  });
3071
+ if (!layoutChanged) {
3072
+ // we won't get an onLayoutChange event
3073
+ // most likely only filters have changed (column or grid filters)
3074
+ // and because filters are not part of the layout manager
3075
+ // there will be no change to the layout
3076
+ // so we need to manually trigger the onLayoutChange event
3077
+ // for other layout-related updates
3078
+ this.onLayoutChange(layout, { skipRefresh: true });
3079
+ }
3068
3080
  perfSetLayout.end();
3069
3081
  }
3070
3082
  getActiveAdaptableAggFuncForCol(columnId) {
@@ -3373,18 +3385,21 @@ You need to define at least one Layout!`);
3373
3385
  };
3374
3386
  return hasPivotTotals(one) || hasPivotTotals(other);
3375
3387
  }
3376
- onLayoutChange(layout) {
3388
+ onLayoutChange(layout, options) {
3377
3389
  this.logger.info('onLayoutChange()');
3378
- const prevOnChangeLayout = this.__prevLayoutForOnChange || this.api.layoutApi.getCurrentLayout();
3379
- // see #on-regroup-expect-group-column-to-be-recomputed-and-setup-properly
3380
- const rowGroupsChanged = this.isRowGroupDifferentInLayout(prevOnChangeLayout, layout);
3381
- const hasPivotTotalsInLayout = this.hasPivotTotalsInLayout(prevOnChangeLayout, layout);
3382
- const pivotColsChanged = JSON.stringify(layout.PivotColumns) !== JSON.stringify(prevOnChangeLayout.PivotColumns);
3383
- if (rowGroupsChanged || pivotColsChanged || hasPivotTotalsInLayout) {
3384
- this.updateColumnModelAndRefreshGrid();
3385
- }
3386
- else {
3387
- this.deriveAdaptableColumnStateFromAgGrid();
3390
+ const skipRefresh = options?.skipRefresh;
3391
+ if (!skipRefresh) {
3392
+ const prevOnChangeLayout = this.__prevLayoutForOnChange || this.api.layoutApi.getCurrentLayout();
3393
+ // see #on-regroup-expect-group-column-to-be-recomputed-and-setup-properly
3394
+ const rowGroupsChanged = this.isRowGroupDifferentInLayout(prevOnChangeLayout, layout);
3395
+ const hasPivotTotalsInLayout = this.hasPivotTotalsInLayout(prevOnChangeLayout, layout);
3396
+ const pivotColsChanged = JSON.stringify(layout.PivotColumns) !== JSON.stringify(prevOnChangeLayout.PivotColumns);
3397
+ if (rowGroupsChanged || pivotColsChanged || hasPivotTotalsInLayout) {
3398
+ this.updateColumnModelAndRefreshGrid();
3399
+ }
3400
+ else {
3401
+ this.deriveAdaptableColumnStateFromAgGrid();
3402
+ }
3388
3403
  }
3389
3404
  this.__prevLayoutForOnChange = layout;
3390
3405
  this.api.layoutApi.createOrUpdateLayout(layout);
@@ -108,7 +108,7 @@ export class AgGridExportAdapter {
108
108
  };
109
109
  }
110
110
  catch (error) {
111
- this.logger.consoleError(`Error exporting ${report.Name} in ${format} format to ${config.destination}`, error);
111
+ this.logger.consoleError(`Error exporting ${report.Name} in ${format} format to ${config.destination}`, error.message);
112
112
  }
113
113
  finally {
114
114
  /**
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import OverlayTrigger from '../OverlayTrigger';
3
- import { useEffect, useState } from 'react';
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import useProperty from '../utils/useProperty';
5
5
  import FieldWrap from '../FieldWrap';
6
6
  import SimpleButton from '../SimpleButton';
@@ -10,9 +10,45 @@ import { useDatepickerContext } from './DatepickerContext';
10
10
  import { DayPicker } from 'react-day-picker';
11
11
  import { AdaptableDateInlineInput } from '../../View/Components/AdaptableInput/AdaptableDateInlineInput';
12
12
  import { isValid, addYears, endOfYear, startOfYear, addDays, addBusinessDays, } from 'date-fns';
13
- const DatepickerOverlay = ({ onHide, children, onKeyDown, onMouseDown, }) => {
13
+ import { Select } from '../Select';
14
+ const onDatePickerDropdownKeyDown = (e) => {
15
+ if (e.key === 'Escape' || e.key === 'Enter') {
16
+ e.stopPropagation();
17
+ }
18
+ };
19
+ const DatePickerComponents = {
20
+ YearsDropdown: (props) => {
21
+ const onChange = useCallback((value) => {
22
+ props.onChange?.({
23
+ target: {
24
+ value,
25
+ },
26
+ });
27
+ }, [props.onChange]);
28
+ return (React.createElement(Select, { onKeyDown: onDatePickerDropdownKeyDown, value: props.value, onChange: onChange, options: props.options }));
29
+ },
30
+ MonthsDropdown: (props) => {
31
+ const onChange = useCallback((value) => {
32
+ props.onChange?.({
33
+ target: {
34
+ value,
35
+ },
36
+ });
37
+ }, [props.onChange]);
38
+ return (React.createElement(Select, { onKeyDown: onDatePickerDropdownKeyDown, value: `${props.value}`, onChange: onChange, options: props.options.map((option) => {
39
+ return {
40
+ label: option.label,
41
+ value: `${option.value}`,
42
+ };
43
+ }) }));
44
+ },
45
+ };
46
+ const DatepickerOverlay = ({ onHide, children, onKeyDown, onMouseDown, overlayDOMRef, }) => {
14
47
  const domRef = React.useRef(null);
15
- return (React.createElement("div", { className: "ab-Datepicker-Overlay", ref: domRef, onKeyDown: onKeyDown, onMouseDown: onMouseDown, onBlur: (e) => {
48
+ return (React.createElement("div", { className: "ab-Datepicker-Overlay", tabIndex: -1, ref: (el) => {
49
+ domRef.current = el;
50
+ overlayDOMRef.current = el;
51
+ }, onKeyDown: onKeyDown, onMouseDown: onMouseDown, onBlur: (e) => {
16
52
  const { relatedTarget } = e;
17
53
  const node = domRef.current;
18
54
  // relatedTarget is the newly focused element as a result of this blur event
@@ -74,13 +110,14 @@ export const Datepicker = React.forwardRef((props, ref) => {
74
110
  clearValue();
75
111
  }, accessLevel: 'Full' })) : null;
76
112
  const calendarButton = (React.createElement(SimpleButton, { disabled: disabled, variant: "text", icon: "calendar", tooltip: "Date", iconSize: 20, px: 0, py: 0, onClick: () => setVisible(true) }));
113
+ const overlayDOMRef = useRef(null);
77
114
  return (React.createElement(Flex, null,
78
- React.createElement(OverlayTrigger, { visible: visible, render: () => (React.createElement(DatepickerOverlay, { onMouseDown: props.onMouseDown, onHide: () => setVisible(false), onKeyDown: (e) => {
115
+ React.createElement(OverlayTrigger, { visible: visible, render: () => (React.createElement(DatepickerOverlay, { overlayDOMRef: overlayDOMRef, onMouseDown: props.onMouseDown, onHide: () => setVisible(false), onKeyDown: (e) => {
79
116
  if (e.key === 'Escape' || e.key === 'Enter') {
80
117
  setVisible(false, e.key);
81
118
  }
82
119
  } },
83
- React.createElement(DayPicker, { fixedWeeks: true, autoFocus: autoFocus ?? true, showWeekNumber: showWeekNumber, showOutsideDays: showOutsideDays, mode: "single", captionLayout: "dropdown", month: isNaN(+month) ? new Date() : month, selected: value, onMonthChange: setMonth, onSelect: updateValue, startMonth: START_MONTH, endMonth: END_MONTH, footer: React.createElement(Flex, { justifyContent: "space-between", mt: 2, flexWrap: 'wrap' }, footerButtons) }))) },
120
+ React.createElement(DayPicker, { fixedWeeks: true, autoFocus: autoFocus ?? true, showWeekNumber: showWeekNumber, showOutsideDays: showOutsideDays, mode: "single", captionLayout: "dropdown", month: isNaN(+month) ? new Date() : month, selected: value, onMonthChange: setMonth, onSelect: updateValue, startMonth: START_MONTH, endMonth: END_MONTH, components: DatePickerComponents, footer: React.createElement(Flex, { justifyContent: "space-between", mt: 2, flexWrap: 'wrap' }, footerButtons) }))) },
84
121
  React.createElement(FieldWrap, { ...boxProps, style: {
85
122
  borderRadius: style?.borderRadius,
86
123
  width: style?.width,
@@ -90,6 +127,14 @@ export const Datepicker = React.forwardRef((props, ref) => {
90
127
  if (!visible) {
91
128
  setVisible(true);
92
129
  }
130
+ }, onBlur: (e) => {
131
+ const { relatedTarget } = e;
132
+ const overlayDOMNode = overlayDOMRef.current;
133
+ if ((overlayDOMNode && relatedTarget == overlayDOMNode) ||
134
+ overlayDOMNode?.contains(relatedTarget)) {
135
+ return;
136
+ }
137
+ setVisible(false);
93
138
  } },
94
139
  React.createElement(AdaptableDateInlineInput, { ref: ref, value: inputValue,
95
140
  // We do not want to show the format when the date-picker is visible
@@ -14,6 +14,7 @@ export type SelectProps<SelectValue extends unknown, IsMulti extends boolean = f
14
14
  menuStyle?: React.CSSProperties;
15
15
  menuMinWidth?: string | number;
16
16
  searchable?: boolean;
17
+ resizable?: boolean;
17
18
  isClearable?: boolean;
18
19
  closeMenuOnSelect?: boolean;
19
20
  hideSelectedOptions?: boolean;
@@ -47,5 +48,6 @@ export type SelectProps<SelectValue extends unknown, IsMulti extends boolean = f
47
48
  size?: 'small' | 'normal';
48
49
  isCreatable?: boolean;
49
50
  menuPortalTarget?: HTMLElement;
51
+ onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
50
52
  };
51
53
  export declare const Select: <SelectValue extends unknown, IsMulti extends boolean = false>(props: SelectProps<SelectValue, IsMulti>) => React.JSX.Element;
@@ -5,8 +5,17 @@ import CreatableSelect from 'react-select/creatable';
5
5
  import { Icon } from '../icons';
6
6
  import { Box } from 'rebass';
7
7
  import { DataSource, InfiniteTable, } from '@infinite-table/infinite-react';
8
- import { useMemo } from 'react';
8
+ import { useCallback, useMemo } from 'react';
9
9
  import join from '../utils/join';
10
+ import { Resizable } from 're-resizable';
11
+ const resizableDirections = {
12
+ right: true,
13
+ bottom: true,
14
+ bottomRight: true,
15
+ };
16
+ const defaultResizableSize = {
17
+ width: '100%',
18
+ };
10
19
  const checkboxStyle = {
11
20
  position: 'relative',
12
21
  top: 1,
@@ -15,7 +24,7 @@ const INFINITE_DOM_PROPS = {
15
24
  style: {
16
25
  height: '100%',
17
26
  minHeight: `var(--ab-cmp-select-menu__min-height, 25rem)`,
18
- maxHeight: '50vh',
27
+ // maxHeight: '50vh',
19
28
  width: '100%',
20
29
  },
21
30
  };
@@ -148,6 +157,7 @@ export const Select = function (props) {
148
157
  } }));
149
158
  };
150
159
  }, []);
160
+ const resizable = props.resizable ?? false;
151
161
  const ValueContainer = React.useMemo(() => {
152
162
  return (props) => {
153
163
  let { children, ...inputProps } = props;
@@ -176,24 +186,33 @@ export const Select = function (props) {
176
186
  } }, children));
177
187
  };
178
188
  }, [renderMultipleValues, props.placeholder]);
189
+ const sizeRef = React.useRef({ ...defaultResizableSize });
179
190
  const MenuComponent = React.useMemo(() => {
180
191
  return (inputProps) => {
181
192
  const { isLoading } = inputProps;
193
+ const theChildren = (React.createElement(React.Fragment, null,
194
+ inputProps.children,
195
+ React.createElement("div", { style: {
196
+ display: isLoading ? 'block' : 'none',
197
+ position: 'absolute',
198
+ inset: 0,
199
+ background: `var(--ab-cmp-select-loading__background)`,
200
+ zIndex: 1,
201
+ } })));
202
+ const onResizeStop = useCallback((_e, _direction, ref) => {
203
+ const newSize = {
204
+ width: ref.style.width,
205
+ height: ref.style.height,
206
+ };
207
+ sizeRef.current = newSize;
208
+ }, []);
182
209
  return (React.createElement(React.Fragment, null,
183
210
  React.createElement(components.Menu, { ...inputProps, innerProps: {
184
211
  'data-name': 'menu-container',
185
212
  ...inputProps.innerProps,
186
- } },
187
- inputProps.children,
188
- React.createElement("div", { style: {
189
- display: isLoading ? 'block' : 'none',
190
- position: 'absolute',
191
- inset: 0,
192
- background: `var(--ab-cmp-select-loading__background)`,
193
- zIndex: 1,
194
- } }))));
213
+ } }, resizable ? (React.createElement(Resizable, { enable: resizableDirections, minWidth: '100%', maxHeight: '60vh', maxWidth: '60vw', defaultSize: sizeRef.current, onResizeStop: onResizeStop }, theChildren)) : (theChildren))));
195
214
  };
196
- }, []);
215
+ }, [resizable]);
197
216
  const SelectComponent = props.isCreatable ? CreatableSelect : ReactSelect;
198
217
  const ClearIndicator = React.useMemo(() => {
199
218
  return (clearIndicatorProps) => {
@@ -365,7 +384,7 @@ export const Select = function (props) {
365
384
  setInputValue(value);
366
385
  props.onInputChange?.(value);
367
386
  }, [props.onInputChange, isMulti]);
368
- return (React.createElement(SelectComponent, { ref: ref, "aria-label": props['aria-label'], inputValue: inputValue, onInputChange: onInputChange, onFocus: onFocus, onBlur: onBlur, onMenuOpen: props.onMenuOpen, isLoading: props.isLoading, options: props.options, className: join(props.className, 'ab-Select'), isDisabled: disabled, menuPlacement: props.menuPlacement ?? 'auto', isSearchable: props.searchable, hideSelectedOptions: false, isMulti: isMulti, value: selectedOption, blurInputOnSelect: false, menuPosition: props.menuPosition ?? 'absolute',
387
+ return (React.createElement(SelectComponent, { ref: ref, "aria-label": props['aria-label'], onKeyDown: props.onKeyDown, inputValue: inputValue, onInputChange: onInputChange, onFocus: onFocus, onBlur: onBlur, onMenuOpen: props.onMenuOpen, isLoading: props.isLoading, options: props.options, className: join(props.className, 'ab-Select'), isDisabled: disabled, menuPlacement: props.menuPlacement ?? 'auto', isSearchable: props.searchable, hideSelectedOptions: false, isMulti: isMulti, value: selectedOption, blurInputOnSelect: false, menuPosition: props.menuPosition ?? 'absolute',
369
388
  // This needed so the menu is not clipped by overflow: hidden
370
389
  menuPortalTarget: props.menuPortalTarget === undefined ? document.body : null, isClearable: props.isClearable, closeMenuOnSelect: props.closeMenuOnSelect, onChange: (option) => {
371
390
  if (isMulti) {
@@ -425,7 +444,7 @@ export const Select = function (props) {
425
444
  zIndex: 999999,
426
445
  boxShadow: 'var(--ab-cmp-select-menu__box-shadow)',
427
446
  minWidth: `var(--ab-cmp-select-menu__min-width)`,
428
- width: `${Math.max(maxLabelLength, 10)}ch`,
447
+ width: resizable ? '100%' : `${Math.max(maxLabelLength, 10)}ch`,
429
448
  '--ab-cmp-select-menu__min-height': `min(${(props.options || []).length + (showHeaderSelectionCheckbox ? 1 : 0)} * var(--ab-grid-row-height), 20rem)`,
430
449
  maxHeight: 'var(--ab-cmp-select-menu__max-height)',
431
450
  maxWidth: 'var(--ab-cmp-select-menu__max-width)',
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: 1747298992714 || Date.now(),
4
- VERSION: "20.1.3" || '--current-version--',
3
+ PUBLISH_TIMESTAMP: 1748008314697 || Date.now(),
4
+ VERSION: "20.1.5" || '--current-version--',
5
5
  };
@@ -443,6 +443,16 @@ export declare const ADAPTABLE_METAMODEL: {
443
443
  kind: string;
444
444
  desc: string;
445
445
  };
446
+ AdaptableFilterState: {
447
+ name: string;
448
+ kind: string;
449
+ desc: string;
450
+ props: {
451
+ name: string;
452
+ kind: string;
453
+ desc: string;
454
+ }[];
455
+ };
446
456
  AdaptableFlashingCell: {
447
457
  name: string;
448
458
  kind: string;
@@ -879,16 +889,6 @@ export declare const ADAPTABLE_METAMODEL: {
879
889
  desc: string;
880
890
  }[];
881
891
  };
882
- AdaptableSearchState: {
883
- name: string;
884
- kind: string;
885
- desc: string;
886
- props: {
887
- name: string;
888
- kind: string;
889
- desc: string;
890
- }[];
891
- };
892
892
  AdaptableSettingsPanel: {
893
893
  name: string;
894
894
  kind: string;