@adaptabletools/adaptable 21.0.11 → 21.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.
- package/base.css +1811 -2336
- package/base.css.map +1 -1
- package/index.css +1768 -1413
- package/index.css.map +1 -1
- package/package.json +3 -3
- package/src/AdaptableInterfaces/IAdaptable.d.ts +2 -1
- package/src/AdaptableOptions/FilterOptions.d.ts +7 -0
- package/src/AdaptableOptions/PredicateOptions.d.ts +4 -3
- package/src/AdaptableState/Common/AdaptableColumn.d.ts +1 -1
- package/src/AdaptableState/Common/AdaptablePredicate.d.ts +12 -0
- package/src/AdaptableState/Common/AdaptablePredicate.js +132 -18
- package/src/AdaptableState/Selection/GridCell.d.ts +10 -0
- package/src/Api/Implementation/ExportApiImpl.js +2 -8
- package/src/Api/Implementation/PredicateApiImpl.d.ts +3 -1
- package/src/Api/Implementation/PredicateApiImpl.js +25 -2
- package/src/Api/Internal/AdaptableInternalApi.d.ts +2 -1
- package/src/Api/Internal/AdaptableInternalApi.js +6 -0
- package/src/Api/Internal/PredicateInternalApi.d.ts +3 -1
- package/src/Api/Internal/PredicateInternalApi.js +14 -0
- package/src/Api/PredicateApi.d.ts +1 -1
- package/src/Redux/Store/AdaptableStore.js +111 -3
- package/src/Utilities/Helpers/Helper.js +26 -2
- package/src/Utilities/Hooks/index.d.ts +4 -0
- package/src/Utilities/Hooks/index.js +4 -0
- package/src/Utilities/Hooks/useAdaptableColumn.d.ts +2 -0
- package/src/Utilities/Hooks/useAdaptableColumn.js +6 -0
- package/src/Utilities/Hooks/useAdaptableOptions.d.ts +2 -0
- package/src/Utilities/Hooks/useAdaptableOptions.js +5 -0
- package/src/Utilities/Hooks/useAdaptableState.d.ts +3 -0
- package/src/Utilities/Hooks/useAdaptableState.js +39 -0
- package/src/Utilities/adaptableQlUtils.js +3 -0
- package/src/View/AdaptableComputedCSSVarsContext.d.ts +12 -0
- package/src/View/AdaptableComputedCSSVarsContext.js +25 -0
- package/src/View/Components/AdaptableInput/AdaptableDateInlineInput.d.ts +1 -1
- package/src/View/Components/ColumnFilter/FloatingFilter.js +5 -1
- package/src/View/Components/ColumnFilter/components/FloatingFilterInputList.js +1 -1
- package/src/View/Components/ColumnFilter/components/FloatingFilterValues.js +34 -9
- package/src/View/Components/FilterForm/ListBoxFilterForm.d.ts +1 -0
- package/src/View/Components/FilterForm/ListBoxFilterForm.js +93 -16
- package/src/View/Layout/Wizard/sections/ColumnsSection.js +1 -1
- package/src/View/renderWithAdaptableContext.js +3 -1
- package/src/agGrid/AdaptableAgGrid.d.ts +3 -1
- package/src/agGrid/AdaptableAgGrid.js +361 -24
- package/src/agGrid/AdaptableFilterHandler.d.ts +3 -1
- package/src/agGrid/AdaptableFilterHandler.js +16 -12
- package/src/agGrid/AgGridAdapter.js +12 -6
- package/src/agGrid/AgGridColumnAdapter.js +19 -13
- package/src/components/OverlayTrigger/index.js +1 -1
- package/src/components/Select/Select.js +22 -22
- package/src/components/Tree/TreeDropdown/index.d.ts +27 -0
- package/src/components/Tree/TreeDropdown/index.js +250 -0
- package/src/components/Tree/TreeList/index.d.ts +25 -0
- package/src/components/Tree/TreeList/index.js +35 -0
- package/src/devTools/DevToolsTracks.d.ts +31 -0
- package/src/devTools/DevToolsTracks.js +31 -0
- package/src/devTools/PerfMarker.d.ts +12 -0
- package/src/devTools/PerfMarker.js +1 -0
- package/src/devTools/index.d.ts +102 -0
- package/src/devTools/index.js +159 -0
- package/src/env.js +2 -2
- package/src/layout-manager/src/index.d.ts +2 -0
- package/src/layout-manager/src/index.js +24 -0
- package/tsconfig.esm.tsbuildinfo +1 -1
|
@@ -8,6 +8,7 @@ import { agGridDataTypeDefinitions, ALL_ADAPTABLE_DATA_TYPES } from './agGridDat
|
|
|
8
8
|
import { isPivotGrandTotal } from '../Api/Implementation/ColumnApiImpl';
|
|
9
9
|
import { isPivotColumnTotal } from '../layout-manager/src/isPivotColumnTotal';
|
|
10
10
|
import { isPivotAggTotalColumn } from '../layout-manager/src/isPivotAggTotalColumn';
|
|
11
|
+
import { isWeightedAverageAggFuncName } from '../AdaptableState/Common/AggregationColumns';
|
|
11
12
|
// AG GRID obfuscates its internals, this is (currently) the best way to get hold of its internal services
|
|
12
13
|
const DANGER_AG_GRID_BEANS_MAP = {};
|
|
13
14
|
const getColumnApiModule = () => ColumnApiModule;
|
|
@@ -23,7 +24,7 @@ export class AgGridAdapter {
|
|
|
23
24
|
const ColumnDefFactory_Prototye_preWireBeans = ColumnDefFactory_Prototype.preWireBeans;
|
|
24
25
|
ColumnDefFactory_Prototype.preWireBeans = function (beans) {
|
|
25
26
|
ColumnDefFactory_Prototye_preWireBeans?.apply(this, arguments);
|
|
26
|
-
const gridId = beans?.context?.
|
|
27
|
+
const gridId = beans?.context?.getId();
|
|
27
28
|
if (!gridId) {
|
|
28
29
|
console.error('CRITICAL: No gridId found in beans, this should never happen!');
|
|
29
30
|
}
|
|
@@ -136,7 +137,8 @@ export class AgGridAdapter {
|
|
|
136
137
|
}
|
|
137
138
|
const pivotColumnFilters = self.adaptableApi.filterApi.columnFilterApi
|
|
138
139
|
.getActiveColumnFilters()
|
|
139
|
-
.filter((columnFilter) => self.adaptableApi.columnApi.isPivotResultColumn(columnFilter.ColumnId)
|
|
140
|
+
.filter((columnFilter) => self.adaptableApi.columnApi.isPivotResultColumn(columnFilter.ColumnId) ||
|
|
141
|
+
self.adaptableApi.columnApi.isAutoRowGroupColumnForSingle(columnFilter.ColumnId));
|
|
140
142
|
try {
|
|
141
143
|
if (pivotColumnFilters.length > 0) {
|
|
142
144
|
for (const columnFilter of pivotColumnFilters) {
|
|
@@ -159,7 +161,8 @@ export class AgGridAdapter {
|
|
|
159
161
|
agGridColumnFilterService.doFiltersPass = this.DANGER_doFiltersPassMonkeyPatcher;
|
|
160
162
|
this.DANGER_isAggFilterPresentMonkeyPatcher = function () {
|
|
161
163
|
const columnFilters = self.adaptableApi.filterApi.columnFilterApi.getActiveColumnFilters();
|
|
162
|
-
return columnFilters.some((colFilter) => self.adaptableApi.columnApi.isPivotResultColumn(colFilter.ColumnId)
|
|
164
|
+
return columnFilters.some((colFilter) => self.adaptableApi.columnApi.isPivotResultColumn(colFilter.ColumnId) ||
|
|
165
|
+
self.adaptableApi.columnApi.isAutoRowGroupColumnForSingle(colFilter.ColumnId));
|
|
163
166
|
};
|
|
164
167
|
agGridColumnFilterService.isAggFilterPresent = this.DANGER_isAggFilterPresentMonkeyPatcher;
|
|
165
168
|
}
|
|
@@ -483,8 +486,10 @@ export class AgGridAdapter {
|
|
|
483
486
|
this.logger.warn(`Column is undefined, returning 'text' for Type`);
|
|
484
487
|
return 'text';
|
|
485
488
|
}
|
|
486
|
-
if (this.adaptableApi.columnApi.isAutoRowGroupColumnForSingle(agColumn.getId())
|
|
487
|
-
|
|
489
|
+
if (this.adaptableApi.columnApi.isAutoRowGroupColumnForSingle(agColumn.getId())) {
|
|
490
|
+
return 'groupColumn';
|
|
491
|
+
}
|
|
492
|
+
if (this.adaptableApi.columnApi.isSelectionColumn(agColumn.getId())) {
|
|
488
493
|
return 'unknown';
|
|
489
494
|
}
|
|
490
495
|
if (this.adaptableApi.columnApi.isAutoRowGroupColumnForMulti(agColumn.getId())) {
|
|
@@ -608,9 +613,10 @@ export class AgGridAdapter {
|
|
|
608
613
|
return false;
|
|
609
614
|
}
|
|
610
615
|
getColumnAggregationFunctions(colDef) {
|
|
611
|
-
|
|
616
|
+
let result = [].concat(colDef.allowedAggFuncs || ['sum', 'min', 'max', 'count', 'avg', 'first', 'last']); // those are the default fns aggrid supports out-of-the-box
|
|
612
617
|
const gridOptionsAggFuncs = this.adaptableApi.agGridApi.getGridOption('aggFuncs') || {};
|
|
613
618
|
result.push(...Object.keys(gridOptionsAggFuncs));
|
|
619
|
+
result = result.filter((func) => !isWeightedAverageAggFuncName(func));
|
|
614
620
|
return [...new Set(result)];
|
|
615
621
|
}
|
|
616
622
|
isTreeColumn(isGeneratedRowGroupColumn) {
|
|
@@ -17,6 +17,7 @@ import { AdaptableFilterHandler } from './AdaptableFilterHandler';
|
|
|
17
17
|
import { AgGridFilterAdapterFactory } from './AgGridFilterAdapter';
|
|
18
18
|
import { AgGridFloatingFilterAdapterFactory } from './AgGridFloatingFilterAdapter';
|
|
19
19
|
import { errorOnce } from './AdaptableLogger';
|
|
20
|
+
import { isWeightedAverageAggFuncName } from '../AdaptableState/Common/AggregationColumns';
|
|
20
21
|
export function getEditorForColumnDataType(columnDataType, variant) {
|
|
21
22
|
if (columnDataType === 'number') {
|
|
22
23
|
return variant === 'react' ? AdaptableReactNumberEditor : AdaptableNumberEditor;
|
|
@@ -516,7 +517,10 @@ export class AgGridColumnAdapter {
|
|
|
516
517
|
}
|
|
517
518
|
setupColumnAllowedAggFuncs({ col, abColumn }) {
|
|
518
519
|
this.setColDefProperty(col, 'allowedAggFuncs', () => {
|
|
519
|
-
|
|
520
|
+
if (!abColumn.availableAggregationFunctions) {
|
|
521
|
+
return undefined;
|
|
522
|
+
}
|
|
523
|
+
return abColumn.availableAggregationFunctions.filter((func) => !isWeightedAverageAggFuncName(func));
|
|
520
524
|
});
|
|
521
525
|
}
|
|
522
526
|
setupColumnType(columnSetupInfo) {
|
|
@@ -588,31 +592,33 @@ export class AgGridColumnAdapter {
|
|
|
588
592
|
});
|
|
589
593
|
}
|
|
590
594
|
else {
|
|
595
|
+
// TODO only set auto group column as filterable if at least one group columns is filterable
|
|
591
596
|
this.setColDefProperty(col, 'filter', () => {
|
|
592
597
|
return 'agGroupColumnFilter';
|
|
593
598
|
});
|
|
594
599
|
}
|
|
595
|
-
|
|
600
|
+
if (this.adaptableApi.columnApi.isAutoRowGroupColumnForMulti(colId)) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
596
603
|
}
|
|
597
604
|
// setup "normal" column filter
|
|
598
605
|
this.setColDefProperty(col, 'filter', (original_filter) => {
|
|
599
|
-
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
606
|
+
const pivotMode = this.adaptableInstance.isInPivotMode();
|
|
602
607
|
if (!useAdaptableFilter) {
|
|
603
608
|
return original_filter;
|
|
604
609
|
}
|
|
605
|
-
if (
|
|
606
|
-
|
|
610
|
+
if (!colDef.filter) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (typeof original_filter !== 'boolean' &&
|
|
607
614
|
typeof original_filter?.handler !== 'function' &&
|
|
608
|
-
!
|
|
615
|
+
!pivotMode) {
|
|
609
616
|
this.adaptableApi.consoleError(`Column '${colId}' has a custom filter defined in colDef.filter, but Adaptable Filtering accepts only the TRUE/FALSE values!`);
|
|
610
617
|
return false;
|
|
611
618
|
}
|
|
612
619
|
return {
|
|
613
620
|
component: AgGridFilterAdapterFactory(this.adaptableInstance),
|
|
614
|
-
|
|
615
|
-
handler: (params) => new AdaptableFilterHandler(this.adaptableApi, columnSetup),
|
|
621
|
+
handler: () => new AdaptableFilterHandler(this.adaptableApi, columnSetup),
|
|
616
622
|
};
|
|
617
623
|
});
|
|
618
624
|
}
|
|
@@ -629,7 +635,7 @@ export class AgGridColumnAdapter {
|
|
|
629
635
|
!colDef.floatingFilter ||
|
|
630
636
|
!this.adaptableOptions.filterOptions.useAdaptableFiltering ||
|
|
631
637
|
!this.adaptableApi.filterApi.columnFilterApi.isQuickFilterVisible();
|
|
632
|
-
if (this.adaptableApi.columnApi.
|
|
638
|
+
if (this.adaptableApi.columnApi.isAutoRowGroupColumnForMulti(col.getColId())) {
|
|
633
639
|
this.setColDefProperty(col, 'floatingFilter', (original_floatingFilter) => {
|
|
634
640
|
// the floating filter for the group column is "inherited" from the base column
|
|
635
641
|
// via the colDef.filter = 'agGroupColumnFilter'
|
|
@@ -640,7 +646,7 @@ export class AgGridColumnAdapter {
|
|
|
640
646
|
});
|
|
641
647
|
this.setColDefProperty(col, 'suppressFloatingFilterButton', () => {
|
|
642
648
|
// hide button for multi column groups
|
|
643
|
-
return
|
|
649
|
+
return true;
|
|
644
650
|
});
|
|
645
651
|
return;
|
|
646
652
|
}
|
|
@@ -650,7 +656,7 @@ export class AgGridColumnAdapter {
|
|
|
650
656
|
}
|
|
651
657
|
return AgGridFloatingFilterAdapterFactory(this.adaptableInstance);
|
|
652
658
|
});
|
|
653
|
-
this.setColDefProperty(col, 'floatingFilter', (
|
|
659
|
+
this.setColDefProperty(col, 'floatingFilter', () => {
|
|
654
660
|
if (isFloatingFilterDisabled) {
|
|
655
661
|
return;
|
|
656
662
|
}
|
|
@@ -193,7 +193,7 @@ const OverlayTrigger = React.forwardRef((givenProps, ref) => {
|
|
|
193
193
|
clearAllOverlays();
|
|
194
194
|
hideOverlay('overlay-trigger');
|
|
195
195
|
}
|
|
196
|
-
} }, props.render({ targetWidth
|
|
196
|
+
} }, props.render({ targetWidth })));
|
|
197
197
|
let preparedConstrainTo;
|
|
198
198
|
if (constrainTo) {
|
|
199
199
|
preparedConstrainTo = getConstrainElement(targetRef.current, constrainTo);
|
|
@@ -4,13 +4,14 @@ import ReactSelect, { components, } from 'react-select';
|
|
|
4
4
|
import CreatableSelect from 'react-select/creatable';
|
|
5
5
|
import { Icon } from '../icons';
|
|
6
6
|
import { Box, Flex } from 'rebass';
|
|
7
|
-
import { DataSource, InfiniteTable,
|
|
7
|
+
import { DataSource, InfiniteTable, } from '@infinite-table/infinite-react';
|
|
8
8
|
import { useCallback, useMemo, useState } from 'react';
|
|
9
9
|
import join from '../utils/join';
|
|
10
10
|
import { Resizable } from 're-resizable';
|
|
11
11
|
import Tooltip from '../Tooltip';
|
|
12
12
|
import { ensurePortalElement } from '../OverlayTrigger';
|
|
13
|
-
import {
|
|
13
|
+
import { CheckBox } from '../CheckBox';
|
|
14
|
+
import { useAdaptableComputedCSSVars } from '../../View/AdaptableComputedCSSVarsContext';
|
|
14
15
|
const resizableDirections = {
|
|
15
16
|
right: true,
|
|
16
17
|
bottom: true,
|
|
@@ -52,15 +53,27 @@ const INFINITE_COLUMNS_WITH_CHECKBOX = {
|
|
|
52
53
|
renderSelectionCheckBox: (params) => {
|
|
53
54
|
// disable reacting to onChange
|
|
54
55
|
// as we handle selection change in the onCellClick
|
|
55
|
-
return React.createElement(
|
|
56
|
+
return React.createElement(CheckBox, { mx: 1, checked: params.rowInfo?.rowSelected ?? false });
|
|
56
57
|
},
|
|
57
58
|
renderHeaderSelectionCheckBox: true,
|
|
58
59
|
className: 'ab-Select-CheckboxColumn',
|
|
59
60
|
renderValue,
|
|
60
61
|
renderHeader: (headerParams) => {
|
|
62
|
+
const selected = headerParams.allRowsSelected
|
|
63
|
+
? true
|
|
64
|
+
: headerParams.someRowsSelected
|
|
65
|
+
? null
|
|
66
|
+
: false;
|
|
67
|
+
const { api } = headerParams;
|
|
61
68
|
return (React.createElement(React.Fragment, null,
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
React.createElement(CheckBox, { mx: 1, checked: selected, onChange: (selected) => {
|
|
70
|
+
if (selected) {
|
|
71
|
+
api.rowSelectionApi.selectAll();
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
api.rowSelectionApi.deselectAll();
|
|
75
|
+
}
|
|
76
|
+
} }, headerParams.allRowsSelected ? '(Deselect All)' : '(Select All)')));
|
|
64
77
|
},
|
|
65
78
|
renderMenuIcon: false,
|
|
66
79
|
},
|
|
@@ -102,15 +115,11 @@ const doesOptionMatchValue = function (value) {
|
|
|
102
115
|
};
|
|
103
116
|
export const Select = function (props) {
|
|
104
117
|
let maxLabelLength = 0;
|
|
105
|
-
const
|
|
106
|
-
'--ab-cmp-select-menu__max-width': 0,
|
|
107
|
-
'--ab-cmp-select-menu__min-width': 0,
|
|
108
|
-
'--ab-cmp-select-menu__max-height': 0,
|
|
109
|
-
});
|
|
118
|
+
const computedCSSVars = useAdaptableComputedCSSVars();
|
|
110
119
|
const CSS_VARS_VALUES = {
|
|
111
|
-
'--ab-cmp-select-menu__max-width':
|
|
112
|
-
'--ab-cmp-select-menu__min-width':
|
|
113
|
-
'--ab-cmp-select-menu__max-height':
|
|
120
|
+
'--ab-cmp-select-menu__max-width': computedCSSVars['--ab-cmp-select-menu__max-width'] || '60vw',
|
|
121
|
+
'--ab-cmp-select-menu__min-width': computedCSSVars['--ab-cmp-select-menu__min-width'] || 150,
|
|
122
|
+
'--ab-cmp-select-menu__max-height': computedCSSVars['--ab-cmp-select-menu__max-height'] || '60vh',
|
|
114
123
|
};
|
|
115
124
|
const searchableInMenulist = props.searchable === 'menulist';
|
|
116
125
|
const searchableInline = props.searchable === 'inline';
|
|
@@ -513,15 +522,6 @@ export const Select = function (props) {
|
|
|
513
522
|
props.onInputChange?.(value);
|
|
514
523
|
}, [props.onInputChange, isMulti]);
|
|
515
524
|
return (React.createElement(React.Fragment, null,
|
|
516
|
-
React.createElement(CSSNumericVariableWatch, { varName: "--ab-cmp-select-menu__max-width", onChange: (value) => {
|
|
517
|
-
cssVarsValuesRef.current['--ab-cmp-select-menu__max-width'] = value;
|
|
518
|
-
} }),
|
|
519
|
-
React.createElement(CSSNumericVariableWatch, { varName: "--ab-cmp-select-menu__min-width", onChange: (value) => {
|
|
520
|
-
cssVarsValuesRef.current['--ab-cmp-select-menu__min-width'] = value;
|
|
521
|
-
} }),
|
|
522
|
-
React.createElement(CSSNumericVariableWatch, { varName: "--ab-cmp-select-menu__max-height", onChange: (value) => {
|
|
523
|
-
cssVarsValuesRef.current['--ab-cmp-select-menu__max-height'] = value;
|
|
524
|
-
} }),
|
|
525
525
|
React.createElement(SelectComponent, { ref: ref, openMenuOnClick: searchableInMenulist ? false : undefined, openMenuOnFocus: searchableInMenulist ? false : undefined, menuIsOpen: searchableInMenulist ? isSelectMenuOpen : undefined, isSearchable: searchableInline, "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', hideSelectedOptions: false, isMulti: isMulti, value: selectedOption, blurInputOnSelect: false, menuPosition: props.menuPosition ?? 'absolute',
|
|
526
526
|
// This needed so the menu is not clipped by overflow: hidden
|
|
527
527
|
menuPortalTarget: ensurePortalElement(), isClearable: props.isClearable, closeMenuOnSelect: props.closeMenuOnSelect, onChange: (option) => {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TreeListItem, TreeListProps } from '../TreeList';
|
|
3
|
+
export type TreeDropdownProps<T extends TreeListItem<any>> = {
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
style?: React.CSSProperties;
|
|
6
|
+
fieldStyle?: React.CSSProperties;
|
|
7
|
+
listSizeConstraints?: {
|
|
8
|
+
minWidth?: number | string;
|
|
9
|
+
maxWidth?: number | string;
|
|
10
|
+
minHeight?: number | string;
|
|
11
|
+
maxHeight?: number | string;
|
|
12
|
+
};
|
|
13
|
+
options: TreeListProps<T>['options'];
|
|
14
|
+
labelField?: string;
|
|
15
|
+
primaryKey?: keyof T;
|
|
16
|
+
value?: any[][] | string[];
|
|
17
|
+
defaultValue?: any[][] | string[];
|
|
18
|
+
toDisplayValue?: (value: any[][] | string[]) => string;
|
|
19
|
+
onChange: (value: any[][] | string[]) => void;
|
|
20
|
+
onMenuOpen?: () => void;
|
|
21
|
+
onMenuClose?: () => void;
|
|
22
|
+
onMouseDown?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
23
|
+
resizable?: boolean;
|
|
24
|
+
clearable?: boolean;
|
|
25
|
+
};
|
|
26
|
+
export declare function toDisplayValueDefault(value: any[][] | string[]): string;
|
|
27
|
+
export declare function TreeDropdown<T extends TreeListItem<any>>(props: TreeDropdownProps<T>): React.JSX.Element;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import FieldWrap from '../../FieldWrap';
|
|
3
|
+
import { TreeList } from '../TreeList';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import OverlayTrigger from '../../OverlayTrigger';
|
|
6
|
+
import { Box, Flex } from 'rebass';
|
|
7
|
+
import NotifyResize from '../../NotifyResize';
|
|
8
|
+
import Input from '../../Input';
|
|
9
|
+
import { TreeSelectionState, withSelectedLeafNodesOnly, } from '../../InfiniteTable';
|
|
10
|
+
import SimpleButton from '../../SimpleButton';
|
|
11
|
+
import { CheckBox } from '../../CheckBox';
|
|
12
|
+
import { useLatest } from '../../utils/useLatest';
|
|
13
|
+
import { Resizable } from 're-resizable';
|
|
14
|
+
import { useAdaptableComputedCSSVars } from '../../../View/AdaptableComputedCSSVarsContext';
|
|
15
|
+
const resizableDirections = {
|
|
16
|
+
right: true,
|
|
17
|
+
bottom: true,
|
|
18
|
+
bottomRight: true,
|
|
19
|
+
};
|
|
20
|
+
export function toDisplayValueDefault(value) {
|
|
21
|
+
if (!Array.isArray(value)) {
|
|
22
|
+
return `${value}`;
|
|
23
|
+
}
|
|
24
|
+
return value.map((v) => (Array.isArray(v) ? v.join('-') : v)).join(', ');
|
|
25
|
+
}
|
|
26
|
+
const getLabelColumn = (field) => {
|
|
27
|
+
return {
|
|
28
|
+
field,
|
|
29
|
+
defaultFlex: 1,
|
|
30
|
+
renderTreeIcon: true,
|
|
31
|
+
defaultSortable: false,
|
|
32
|
+
resizable: false,
|
|
33
|
+
renderSelectionCheckBox: ({ rowInfo, dataSourceApi, api }) => {
|
|
34
|
+
if (!rowInfo.isTreeNode) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return (React.createElement(CheckBox, { mx: 1, checked: rowInfo?.rowSelected, onChange: (checked) => {
|
|
38
|
+
dataSourceApi.treeApi.setNodeSelection(rowInfo.nodePath, checked);
|
|
39
|
+
api.focus();
|
|
40
|
+
} }));
|
|
41
|
+
},
|
|
42
|
+
renderHeader: ({ dataSourceApi, api, allRowsSelected, someRowsSelected }) => {
|
|
43
|
+
const { treeApi } = dataSourceApi;
|
|
44
|
+
const allFirstLevelCollapsed = dataSourceApi
|
|
45
|
+
.getOriginalDataArray()
|
|
46
|
+
.every((item) => !treeApi.isNodeExpanded([item.id]));
|
|
47
|
+
return (React.createElement(Flex, { flexDirection: 'row', alignItems: 'center', width: '100%', onMouseDown: (e) => {
|
|
48
|
+
// so we can keep the focus on the Grid
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
} },
|
|
51
|
+
React.createElement(CheckBox, { checked: someRowsSelected && !allRowsSelected ? null : allRowsSelected, mr: 2, onChange: () => {
|
|
52
|
+
if (allRowsSelected) {
|
|
53
|
+
dataSourceApi.treeApi.deselectAll();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
dataSourceApi.treeApi.selectAll();
|
|
57
|
+
}
|
|
58
|
+
api.focus();
|
|
59
|
+
} }),
|
|
60
|
+
React.createElement(Flex, { flex: 1 }),
|
|
61
|
+
React.createElement(SimpleButton, { label: "toggle-expand-collapse", icon: allFirstLevelCollapsed ? 'expand-all' : 'collapse-all', onMouseDown: () => {
|
|
62
|
+
if (allFirstLevelCollapsed) {
|
|
63
|
+
dataSourceApi.treeApi.expandAll();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
dataSourceApi.treeApi.collapseAll();
|
|
67
|
+
}
|
|
68
|
+
}, iconPosition: "end" }, allFirstLevelCollapsed ? 'Expand All' : 'Collapse All')));
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
const sizeFull = {
|
|
73
|
+
width: '100%',
|
|
74
|
+
height: '100%',
|
|
75
|
+
};
|
|
76
|
+
function getRowCount(options) {
|
|
77
|
+
return options.reduce((acc, option) => {
|
|
78
|
+
if (Array.isArray(option.children)) {
|
|
79
|
+
return acc + getRowCount(option.children) + 1;
|
|
80
|
+
}
|
|
81
|
+
return acc + 1;
|
|
82
|
+
}, 0);
|
|
83
|
+
}
|
|
84
|
+
export function TreeDropdown(props) {
|
|
85
|
+
const [visible, doSetVisible] = useState(false);
|
|
86
|
+
const overlayDOMRef = useRef(null);
|
|
87
|
+
const getProps = useLatest(props);
|
|
88
|
+
const computedCSSVars = useAdaptableComputedCSSVars();
|
|
89
|
+
const [treeExpandState, setTreeExpandState] = useState(undefined);
|
|
90
|
+
const [searchValue, setSearchValue] = useState('');
|
|
91
|
+
const labelField = props.labelField ?? 'label';
|
|
92
|
+
const columns = useMemo(() => {
|
|
93
|
+
return {
|
|
94
|
+
label: getLabelColumn(labelField),
|
|
95
|
+
};
|
|
96
|
+
}, [labelField]);
|
|
97
|
+
const [stateValue, setStateValue] = useState(props.value !== undefined ? props.value : props.defaultValue || []);
|
|
98
|
+
const onChange = useCallback((value) => {
|
|
99
|
+
const paths = value instanceof TreeSelectionState
|
|
100
|
+
? value.getState().selectedPaths
|
|
101
|
+
: value.selectedPaths || [];
|
|
102
|
+
if (props.value === undefined) {
|
|
103
|
+
setStateValue(paths);
|
|
104
|
+
}
|
|
105
|
+
props.onChange?.(paths);
|
|
106
|
+
}, [props.onChange, props.value]);
|
|
107
|
+
const value = props.value !== undefined ? props.value : stateValue;
|
|
108
|
+
const treeSelection = useMemo(() => {
|
|
109
|
+
const selection = {
|
|
110
|
+
defaultSelection: false,
|
|
111
|
+
selectedPaths: value,
|
|
112
|
+
};
|
|
113
|
+
return selection;
|
|
114
|
+
}, [value]);
|
|
115
|
+
const rowCount = useMemo(() => {
|
|
116
|
+
return getRowCount(props.options);
|
|
117
|
+
}, [props.options]);
|
|
118
|
+
const [size, setSize] = useState({
|
|
119
|
+
width: 0,
|
|
120
|
+
height: rowCount * 35,
|
|
121
|
+
});
|
|
122
|
+
const setHeight = useCallback((height) => {
|
|
123
|
+
setSize((s) => {
|
|
124
|
+
return {
|
|
125
|
+
...s,
|
|
126
|
+
height,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}, []);
|
|
130
|
+
const setWidth = useCallback((width) => {
|
|
131
|
+
setSize((s) => {
|
|
132
|
+
return {
|
|
133
|
+
...s,
|
|
134
|
+
width,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
}, []);
|
|
138
|
+
const getSize = useLatest(size);
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!getSize().height) {
|
|
141
|
+
setHeight(rowCount * 35);
|
|
142
|
+
}
|
|
143
|
+
}, [rowCount]);
|
|
144
|
+
const setVisible = (visible) => {
|
|
145
|
+
if (visible) {
|
|
146
|
+
const { onMenuOpen } = getProps();
|
|
147
|
+
if (onMenuOpen) {
|
|
148
|
+
onMenuOpen();
|
|
149
|
+
}
|
|
150
|
+
requestAnimationFrame(() => {
|
|
151
|
+
doSetVisible(visible);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const { onMenuClose } = getProps();
|
|
156
|
+
if (onMenuClose) {
|
|
157
|
+
onMenuClose();
|
|
158
|
+
}
|
|
159
|
+
doSetVisible(visible);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const [treeListApi, setTreeListApi] = useState(null);
|
|
163
|
+
const { listSizeConstraints } = props;
|
|
164
|
+
const nodeMatches = useCallback(({ data }) => {
|
|
165
|
+
return !searchValue
|
|
166
|
+
? data
|
|
167
|
+
: `${data[labelField]}`.toLowerCase().includes(searchValue.toLowerCase());
|
|
168
|
+
}, [searchValue]);
|
|
169
|
+
const filterFunction = useCallback(({ data, filterTreeNode }) => {
|
|
170
|
+
if (!Array.isArray(data.children)) {
|
|
171
|
+
return nodeMatches({ data });
|
|
172
|
+
}
|
|
173
|
+
// allow non-leaf nodes to match
|
|
174
|
+
if (nodeMatches({ data })) {
|
|
175
|
+
return data;
|
|
176
|
+
}
|
|
177
|
+
return filterTreeNode(data);
|
|
178
|
+
}, [nodeMatches]);
|
|
179
|
+
return (React.createElement(Flex, { flexDirection: 'row', className: "ab-TreeDropdown", style: {
|
|
180
|
+
width: '100%',
|
|
181
|
+
...props.style,
|
|
182
|
+
}, onMouseDown: props.onMouseDown, onBlur: (e) => {
|
|
183
|
+
const { relatedTarget } = e;
|
|
184
|
+
const overlayDOMNode = overlayDOMRef.current;
|
|
185
|
+
if ((overlayDOMNode && relatedTarget == overlayDOMNode) ||
|
|
186
|
+
overlayDOMNode?.contains(relatedTarget)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
setVisible(false);
|
|
190
|
+
} },
|
|
191
|
+
React.createElement(NotifyResize, { onResize: (newSize) => {
|
|
192
|
+
setWidth(newSize.width);
|
|
193
|
+
} }),
|
|
194
|
+
React.createElement(OverlayTrigger, { visible: visible, targetOffset: 20, render: () => {
|
|
195
|
+
const minWidth = listSizeConstraints?.minWidth ||
|
|
196
|
+
computedCSSVars['--ab-cmp-select-menu__min-width'] ||
|
|
197
|
+
240;
|
|
198
|
+
const maxWidth = listSizeConstraints?.maxWidth ||
|
|
199
|
+
computedCSSVars['--ab-cmp-select-menu__max-width'] ||
|
|
200
|
+
'60vw';
|
|
201
|
+
const minHeight = listSizeConstraints?.minHeight || 200;
|
|
202
|
+
const maxHeight = listSizeConstraints?.maxHeight ||
|
|
203
|
+
computedCSSVars['--ab-cmp-select-menu__max-height'] ||
|
|
204
|
+
'50vh';
|
|
205
|
+
const resizable = getProps().resizable;
|
|
206
|
+
const treeList = (React.createElement(TreeList, { primaryKey: props.primaryKey ?? 'id', treeFilterFunction: filterFunction, columnHeaderHeight: 40, onReady: ({ api }) => {
|
|
207
|
+
setTreeListApi(api);
|
|
208
|
+
api.focus();
|
|
209
|
+
}, defaultTreeExpandState: treeExpandState, onTreeExpandStateChange: setTreeExpandState, columns: columns, options: props.options, treeSelection: treeSelection, onTreeSelectionChange: withSelectedLeafNodesOnly(onChange), style: resizable
|
|
210
|
+
? sizeFull
|
|
211
|
+
: {
|
|
212
|
+
width: size.width,
|
|
213
|
+
height: size.height,
|
|
214
|
+
maxWidth,
|
|
215
|
+
minHeight,
|
|
216
|
+
maxHeight,
|
|
217
|
+
minWidth,
|
|
218
|
+
} }));
|
|
219
|
+
let children = (React.createElement(Flex, { flexDirection: 'column', height: '100%' },
|
|
220
|
+
React.createElement(Flex, { backgroundColor: 'defaultbackground', p: 1, alignItems: 'center', justifyContent: 'stretch', justifyItems: 'stretch' },
|
|
221
|
+
React.createElement(Input, { "data-name": "menulist-search-input", placeholder: "Search...", style: { width: '100%' }, value: searchValue, onChange: (e) => setSearchValue(e.target.value) })),
|
|
222
|
+
treeList));
|
|
223
|
+
if (resizable) {
|
|
224
|
+
const onResizeStop = (_e, _direction, ref) => {
|
|
225
|
+
const newSize = {
|
|
226
|
+
width: ref.style.width,
|
|
227
|
+
height: ref.style.height,
|
|
228
|
+
};
|
|
229
|
+
setSize(newSize);
|
|
230
|
+
};
|
|
231
|
+
children = (React.createElement(Resizable, { enable: resizableDirections, minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, defaultSize: size, onResizeStop: onResizeStop, onResizeStart: (e) => {
|
|
232
|
+
// in order to prevent focus from being lost
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
} }, children));
|
|
235
|
+
}
|
|
236
|
+
return (React.createElement(Box, { ref: overlayDOMRef, className: "ab-TreeDropdownOverlay", "data-name": "menu-container" }, children));
|
|
237
|
+
} },
|
|
238
|
+
React.createElement(FieldWrap, { style: { width: '100%', ...props.fieldStyle } },
|
|
239
|
+
React.createElement(Input, { type: "text", readOnly: true, "data-name": "Select Values", placeholder: props.placeholder ?? 'Select a value', style: {
|
|
240
|
+
width: '100%',
|
|
241
|
+
}, pr: props.clearable ? 0 : undefined, value: props.toDisplayValue ? props.toDisplayValue(value) : toDisplayValueDefault(value), onFocus: () => {
|
|
242
|
+
if (!visible) {
|
|
243
|
+
setVisible(true);
|
|
244
|
+
}
|
|
245
|
+
treeListApi?.focus();
|
|
246
|
+
} }),
|
|
247
|
+
props.clearable && (React.createElement(SimpleButton, { style: {
|
|
248
|
+
visibility: Array.isArray(value) && value.length > 0 ? 'visible' : 'hidden',
|
|
249
|
+
}, variant: "text", icon: "close", onClick: () => onChange({ selectedPaths: [] }) }))))));
|
|
250
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { DataSourcePropOnTreeSelectionChange_MultiNode, InfiniteTableProps, TreeDataSourceProps } from '../../InfiniteTable';
|
|
3
|
+
export type TreeListItem<T = any> = {
|
|
4
|
+
id: string | number;
|
|
5
|
+
label: string;
|
|
6
|
+
children?: TreeListItem<T>[];
|
|
7
|
+
};
|
|
8
|
+
export type TreeListProps<T extends TreeListItem<T>> = {
|
|
9
|
+
options: T[];
|
|
10
|
+
debugId?: string;
|
|
11
|
+
primaryKey?: keyof T;
|
|
12
|
+
domProps?: InfiniteTableProps<T>['domProps'];
|
|
13
|
+
style?: React.CSSProperties;
|
|
14
|
+
rowHeight?: number | string;
|
|
15
|
+
treeSelection?: TreeDataSourceProps<T>['treeSelection'];
|
|
16
|
+
defaultTreeSelection?: TreeDataSourceProps<T>['defaultTreeSelection'];
|
|
17
|
+
onTreeSelectionChange?: DataSourcePropOnTreeSelectionChange_MultiNode;
|
|
18
|
+
defaultTreeExpandState?: TreeDataSourceProps<T>['defaultTreeExpandState'];
|
|
19
|
+
onTreeExpandStateChange?: TreeDataSourceProps<T>['onTreeExpandStateChange'];
|
|
20
|
+
columns?: InfiniteTableProps<T>['columns'];
|
|
21
|
+
columnHeaderHeight?: number | string;
|
|
22
|
+
onReady?: InfiniteTableProps<T>['onReady'];
|
|
23
|
+
treeFilterFunction?: TreeDataSourceProps<T>['treeFilterFunction'];
|
|
24
|
+
};
|
|
25
|
+
export declare function TreeList<T extends TreeListItem<T>>(props: TreeListProps<T>): React.JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TreeDataSource, TreeGrid, } from '../../InfiniteTable';
|
|
3
|
+
import { Flex } from 'rebass';
|
|
4
|
+
import join from '../../../../../adaptable-react-aggrid/src/utils/join';
|
|
5
|
+
const columns = {
|
|
6
|
+
label: {
|
|
7
|
+
field: 'label',
|
|
8
|
+
header: 'Label',
|
|
9
|
+
defaultFlex: 1,
|
|
10
|
+
renderTreeIcon: true,
|
|
11
|
+
renderSelectionCheckBox: true,
|
|
12
|
+
defaultSortable: false,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const domProps = {
|
|
16
|
+
style: {
|
|
17
|
+
height: '100%',
|
|
18
|
+
flex: 1,
|
|
19
|
+
},
|
|
20
|
+
className: 'ab-TreeList',
|
|
21
|
+
};
|
|
22
|
+
const DEFAULT_TREE_EXPAND_STATE = {
|
|
23
|
+
expandedPaths: [],
|
|
24
|
+
defaultExpanded: false,
|
|
25
|
+
};
|
|
26
|
+
export function TreeList(props) {
|
|
27
|
+
return (React.createElement(Flex, { flex: 1, flexDirection: 'column', className: "ab-TreeList", style: props.style },
|
|
28
|
+
React.createElement(TreeDataSource, { data: props.options, primaryKey: props.primaryKey ?? 'id', defaultTreeSelection: props.defaultTreeSelection, treeSelection: props.treeSelection, onTreeSelectionChange: props.onTreeSelectionChange, treeFilterFunction: props.treeFilterFunction, onTreeExpandStateChange: props.onTreeExpandStateChange, defaultTreeExpandState: props.defaultTreeExpandState ?? DEFAULT_TREE_EXPAND_STATE },
|
|
29
|
+
React.createElement(TreeGrid, { defaultActiveRowIndex: 0, onReady: props.onReady, debugId: props.debugId, keyboardSelection: true, rowHeight: props.rowHeight ?? '--ab-grid-row-height', keyboardNavigation: "row", columnHeaderHeight: props.columnHeaderHeight, domProps: {
|
|
30
|
+
...domProps,
|
|
31
|
+
...props.domProps,
|
|
32
|
+
className: join(domProps.className, props.domProps?.className),
|
|
33
|
+
style: { ...domProps.style, ...props.domProps?.style },
|
|
34
|
+
}, columns: props.columns ?? columns, showZebraRows: false }))));
|
|
35
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const DevToolsTracks: {
|
|
2
|
+
readonly Init: {
|
|
3
|
+
readonly track: "Initialisation";
|
|
4
|
+
readonly labels: {
|
|
5
|
+
readonly Init: "Init";
|
|
6
|
+
readonly InitStore: "Init store";
|
|
7
|
+
readonly LoadStore: "Loading store";
|
|
8
|
+
readonly InitAGGrid: "AG Grid init";
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
readonly LayoutManager: {
|
|
12
|
+
readonly track: "Layout Manager";
|
|
13
|
+
readonly labels: {
|
|
14
|
+
readonly SetLayout: "Setting layout";
|
|
15
|
+
readonly ApplyPivotLayout: "Apply pivot layout";
|
|
16
|
+
readonly ApplyTableLayout: "Apply table layout";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
readonly Runtime: {
|
|
20
|
+
readonly track: "Runtime";
|
|
21
|
+
readonly labels: {
|
|
22
|
+
readonly SetLayout: "Setting layout";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
readonly Redux: {
|
|
26
|
+
readonly track: "Redux";
|
|
27
|
+
readonly labels: {
|
|
28
|
+
readonly Action: "Action";
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const DevToolsTracks = {
|
|
2
|
+
Init: {
|
|
3
|
+
track: 'Initialisation',
|
|
4
|
+
labels: {
|
|
5
|
+
Init: 'Init',
|
|
6
|
+
InitStore: 'Init store',
|
|
7
|
+
LoadStore: 'Loading store',
|
|
8
|
+
InitAGGrid: 'AG Grid init',
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
LayoutManager: {
|
|
12
|
+
track: 'Layout Manager',
|
|
13
|
+
labels: {
|
|
14
|
+
SetLayout: 'Setting layout',
|
|
15
|
+
ApplyPivotLayout: 'Apply pivot layout',
|
|
16
|
+
ApplyTableLayout: 'Apply table layout',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
Runtime: {
|
|
20
|
+
track: 'Runtime',
|
|
21
|
+
labels: {
|
|
22
|
+
SetLayout: 'Setting layout',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
Redux: {
|
|
26
|
+
track: 'Redux',
|
|
27
|
+
labels: {
|
|
28
|
+
Action: 'Action',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type PerfMarkerDetails = {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string | number | boolean;
|
|
4
|
+
}[];
|
|
5
|
+
export interface PerfMarker {
|
|
6
|
+
start(options?: {
|
|
7
|
+
details?: PerfMarkerDetails;
|
|
8
|
+
}): PerfMarker;
|
|
9
|
+
end(options?: {
|
|
10
|
+
details?: PerfMarkerDetails;
|
|
11
|
+
}): PerfMarker;
|
|
12
|
+
}
|