@adaptabletools/adaptable 20.0.7-canary.1 → 20.0.7-canary.3
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/agGrid.d.ts +37 -0
- package/agGrid.js +38 -0
- package/base.css +19 -3
- package/base.css.map +1 -1
- package/index.css +17 -3
- package/index.css.map +1 -1
- package/package.json +3 -3
- package/src/AdaptableOptions/LayoutOptions.d.ts +3 -6
- package/src/AdaptableState/Common/AdaptableColumn.d.ts +14 -5
- package/src/AdaptableState/Common/AdaptableColumn.js +26 -0
- package/src/AdaptableState/Common/AggregationColumns.d.ts +10 -0
- package/src/AdaptableState/Common/AggregationColumns.js +11 -0
- package/src/AdaptableState/LayoutState.d.ts +4 -4
- package/src/Api/Implementation/ColumnApiImpl.d.ts +2 -2
- package/src/Api/Implementation/ColumnApiImpl.js +36 -3
- package/src/Api/Implementation/LayoutHelpers.js +7 -0
- package/src/Api/Internal/ColumnInternalApi.js +3 -3
- package/src/View/Alert/Wizard/AlertButtonsEditor.js +9 -10
- package/src/View/Alert/Wizard/AlertNotificationWizardSection.js +26 -25
- package/src/View/Components/ColumnFilter/ColumnFilter.js +1 -1
- package/src/View/Components/FilterForm/ListBoxFilterForm.js +6 -3
- package/src/View/Components/NewScopeComponent.js +2 -1
- package/src/View/Layout/Wizard/LayoutWizard.js +2 -2
- package/src/View/Layout/Wizard/sections/AggregationsSection.js +16 -1
- package/src/View/Layout/Wizard/sections/PivotAggregationsSection.d.ts +2 -2
- package/src/View/Layout/Wizard/sections/PivotAggregationsSection.js +219 -43
- package/src/agGrid/AdaptableAgGrid.d.ts +1 -0
- package/src/agGrid/AdaptableAgGrid.js +9 -1
- package/src/agGrid/AgGridAdapter.d.ts +1 -0
- package/src/agGrid/AgGridAdapter.js +10 -1
- package/src/components/Select/Select.js +15 -3
- package/src/env.js +2 -2
- package/src/layout-manager/src/LayoutManagerModel.d.ts +4 -4
- package/src/layout-manager/src/index.d.ts +1 -1
- package/src/layout-manager/src/index.js +41 -21
- package/src/layout-manager/src/isPivotAggTotalColumn.d.ts +2 -0
- package/src/layout-manager/src/{isPivotTotalColumn.js → isPivotAggTotalColumn.js} +1 -1
- package/src/layout-manager/src/isPivotGrandTotalColumn.d.ts +2 -0
- package/src/layout-manager/src/isPivotGrandTotalColumn.js +3 -0
- package/src/layout-manager/src/isPivotGroupTotalColumn.d.ts +1 -2
- package/src/layout-manager/src/isPivotGroupTotalColumn.js +2 -2
- package/src/layout-manager/src/normalizeLayoutModel.js +0 -3
- package/src/layout-manager/src/simplifyLayoutModel.js +3 -0
- package/src/metamodel/adaptable.metamodel.d.ts +13 -7
- package/src/metamodel/adaptable.metamodel.js +1 -1
- package/src/types.d.ts +1 -1
- package/tsconfig.esm.tsbuildinfo +1 -1
- package/src/layout-manager/src/isPivotTotalColumn.d.ts +0 -2
|
@@ -5,7 +5,7 @@ import StringExtensions from '../../Utilities/Extensions/StringExtensions';
|
|
|
5
5
|
import { isPivotGrandTotalColumn, isPivotResultColumn } from '../Implementation/ColumnApiImpl';
|
|
6
6
|
import { destructurePivotColumnId } from '../../layout-manager/src/destructurePivotColumnId';
|
|
7
7
|
import { isPivotGroupTotalColumn } from '../../layout-manager/src/isPivotGroupTotalColumn';
|
|
8
|
-
import {
|
|
8
|
+
import { isPivotAggTotalColumn } from '../../layout-manager/src/isPivotAggTotalColumn';
|
|
9
9
|
export function getAutoRowGroupColumnIdFor(columnId) {
|
|
10
10
|
return `${AG_GRID_GROUPED_COLUMN}-${columnId}`;
|
|
11
11
|
}
|
|
@@ -221,7 +221,7 @@ export class ColumnInternalApi extends ApiBase {
|
|
|
221
221
|
aggregation: pivotValueCol.getAggFunc(),
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
|
-
if (isPivotGroupTotalColumn(params.colDef)) {
|
|
224
|
+
if (isPivotGroupTotalColumn(params.colDef?.colId)) {
|
|
225
225
|
const columnType = 'pivotGroupTotal';
|
|
226
226
|
const pivotColInfo = destructurePivotColumnId(params.colDef, {
|
|
227
227
|
pivotColIds: currentPivotLayout.PivotColumns,
|
|
@@ -239,7 +239,7 @@ export class ColumnInternalApi extends ApiBase {
|
|
|
239
239
|
aggregation: params.colDef?.pivotValueColumn?.getAggFunc(),
|
|
240
240
|
};
|
|
241
241
|
}
|
|
242
|
-
if (
|
|
242
|
+
if (isPivotAggTotalColumn(params.colDef)) {
|
|
243
243
|
const columnType = 'pivotAggregationTotal';
|
|
244
244
|
const pivotValueCol = params.colDef.pivotValueColumn;
|
|
245
245
|
const pivotColInfo = destructurePivotColumnId(params.colDef, {
|
|
@@ -179,16 +179,15 @@ export const AlertButtonsEditor = (props) => {
|
|
|
179
179
|
return btn;
|
|
180
180
|
}));
|
|
181
181
|
} }),
|
|
182
|
-
React.createElement(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
React.createElement(Select, { options: TONE_OPTIONS, onChange: (value) => {
|
|
182
|
+
React.createElement(Select, { options: ['text', 'outlined', 'raised'].map((variant) => {
|
|
183
|
+
return {
|
|
184
|
+
label: StringExtensions.CapitaliseFirstLetter(variant),
|
|
185
|
+
value: variant,
|
|
186
|
+
};
|
|
187
|
+
}), renderSingleValue: (option) => `Variant: ${option.label}`, value: buttonStyle?.variant, onChange: (value) => {
|
|
188
|
+
setVariant(value);
|
|
189
|
+
} }),
|
|
190
|
+
React.createElement(Select, { style: { marginLeft: 'var(--ab-space-2)' }, options: TONE_OPTIONS, onChange: (value) => {
|
|
192
191
|
if (value === 'text') {
|
|
193
192
|
setTone(null);
|
|
194
193
|
return;
|
|
@@ -64,31 +64,32 @@ export const AlertNotificationWizardSection = (props) => {
|
|
|
64
64
|
React.createElement(Tabs, { "data-name": "display-options", mt: 2, mb: 3, autoFocus: false },
|
|
65
65
|
React.createElement(Tabs.Tab, null, "Notification Options"),
|
|
66
66
|
React.createElement(Tabs.Content, null,
|
|
67
|
-
React.createElement(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
newAlertDefinition.AlertForm
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
(
|
|
85
|
-
newAlertDefinition.AlertForm?.Buttons
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
67
|
+
React.createElement("div", null,
|
|
68
|
+
React.createElement(CheckBox, { checked: data.AlertProperties?.DisplayNotification, onChange: (DisplayNotification) => {
|
|
69
|
+
const newAlertDefinition = {
|
|
70
|
+
...data,
|
|
71
|
+
AlertProperties: {
|
|
72
|
+
...data.AlertProperties,
|
|
73
|
+
DisplayNotification,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
if (!DisplayNotification && typeof newAlertDefinition.AlertForm === 'object') {
|
|
77
|
+
delete newAlertDefinition.AlertForm.Buttons;
|
|
78
|
+
}
|
|
79
|
+
if (DisplayNotification && newAlertDefinition.AlertForm == undefined) {
|
|
80
|
+
newAlertDefinition.AlertForm =
|
|
81
|
+
api.alertApi.internalApi.getDefaultAlertNotificationForm();
|
|
82
|
+
}
|
|
83
|
+
// make sure we have at least one button
|
|
84
|
+
if (typeof newAlertDefinition.AlertForm === 'object' &&
|
|
85
|
+
(!newAlertDefinition.AlertForm?.Buttons ||
|
|
86
|
+
newAlertDefinition.AlertForm?.Buttons?.length === 0)) {
|
|
87
|
+
newAlertDefinition.AlertForm.Buttons = [
|
|
88
|
+
api.alertApi.internalApi.getDefaultButton(),
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
props.onChange(newAlertDefinition);
|
|
92
|
+
} }, "Display a Notification when Alert is triggered (with action buttons)")),
|
|
92
93
|
data.AlertProperties?.DisplayNotification ? (typeof data.AlertForm === 'string' ? (React.createElement(Text, { fontSize: 2 }, "Alert buttons cannot be customized because form is dynamically driven")) : (React.createElement(AlertButtonsEditor, { alertType: props.alertType, AlertButtons: data.AlertForm?.Buttons, api: api, adaptableAlert: adaptableAlert, onChange: (buttons) => {
|
|
93
94
|
props.onChange({
|
|
94
95
|
...data,
|
|
@@ -18,7 +18,7 @@ const ColumnFilterPredicateDropdown = (props) => {
|
|
|
18
18
|
});
|
|
19
19
|
const operator = props.predicate?.operator;
|
|
20
20
|
const isAndOr = operator === 'AND' || operator === 'OR';
|
|
21
|
-
return (React.createElement(Box, { display: 'flex', alignItems: "center", style: {
|
|
21
|
+
return (React.createElement(Box, { display: 'flex', alignItems: "center", className: "ab-ColumnFilterPredicateDropdown", style: {
|
|
22
22
|
//@ts-ignore ignore
|
|
23
23
|
'--ab-cmp-input__background': 'var(--ab-color-primary)',
|
|
24
24
|
} },
|
|
@@ -24,9 +24,12 @@ export const ColumnValuesSelect = (props) => {
|
|
|
24
24
|
}
|
|
25
25
|
return true;
|
|
26
26
|
});
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const menuStyle = React.useMemo(() => {
|
|
28
|
+
return {
|
|
29
|
+
minWidth: `var(--ab-cmp-select-column-menu-${column.columnId}__min-width, var(--ab-cmp-select-column-menu__min-width, 160px))`,
|
|
30
|
+
};
|
|
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 }));
|
|
30
33
|
return (React.createElement("div", { className: join(baseClassName, props.isLoading && `${baseClassName}--loading`, !value.length && `${baseClassName}--empty`), onKeyDownCapture: (e) => {
|
|
31
34
|
const event = e.nativeEvent || e;
|
|
32
35
|
event.stopPropagation = () => {
|
|
@@ -9,6 +9,7 @@ import { NewColumnSelector } from './ColumnSelector';
|
|
|
9
9
|
import { AdaptableFormControlTextClear } from './Forms/AdaptableFormControlTextClear';
|
|
10
10
|
import { useAdaptable } from '../AdaptableContext';
|
|
11
11
|
import { ValueOptionsTags } from './ValueSelector';
|
|
12
|
+
import { getColumnTypeFriendlyName } from '../../AdaptableState/Common/AdaptableColumn';
|
|
12
13
|
export const isScopeValid = ({ Scope }) => {
|
|
13
14
|
const result = [];
|
|
14
15
|
if (!Scope) {
|
|
@@ -175,5 +176,5 @@ export const NewScopeComponent = (props) => {
|
|
|
175
176
|
ColumnTypes: columnTypes,
|
|
176
177
|
};
|
|
177
178
|
props.updateScope(newScope);
|
|
178
|
-
} }, columnType)))))))));
|
|
179
|
+
} }, getColumnTypeFriendlyName(columnType))))))))));
|
|
179
180
|
};
|
|
@@ -18,7 +18,7 @@ import { areSummaryRowsValid, RowSummarySection, RowSummarySectionSummary, } fro
|
|
|
18
18
|
import { WEIGHTED_AVERAGE_AGGREGATED_FUNCTION } from '../../../AdaptableState/Common/RowSummary';
|
|
19
19
|
import { isPivotLayout } from '../../../Utilities/isPivotLayout';
|
|
20
20
|
import { PivotRowGroupingSection, PivotRowGroupingSectionSummary, } from './sections/PivotRowGroupingSection';
|
|
21
|
-
import { PivotAggregationsSection, PivotAggregationsSectionSummary, } from './sections/PivotAggregationsSection';
|
|
21
|
+
import { isPivotAggregationsSectionValid, PivotAggregationsSection, PivotAggregationsSectionSummary, } from './sections/PivotAggregationsSection';
|
|
22
22
|
export const LayoutWizard = (props) => {
|
|
23
23
|
const dispatch = useDispatch();
|
|
24
24
|
const adaptable = useAdaptable();
|
|
@@ -113,7 +113,7 @@ export const LayoutWizard = (props) => {
|
|
|
113
113
|
isVisible: () => layoutSupportedFeatures.PivotAggregationColumns && layoutSupportedFeatures.PivotColumns,
|
|
114
114
|
details: 'Select Pivot Column Aggregations',
|
|
115
115
|
renderSummary: () => React.createElement(PivotAggregationsSectionSummary, null),
|
|
116
|
-
isValid: (data) =>
|
|
116
|
+
isValid: (data) => isPivotAggregationsSectionValid(data),
|
|
117
117
|
render: () => (React.createElement(Box, { p: 2, style: { height: '100%' } },
|
|
118
118
|
React.createElement(PivotAggregationsSection, { onChange: (layout) => {
|
|
119
119
|
let newLayout = cloneObject(layout);
|
|
@@ -10,6 +10,8 @@ import { ValueSelector } from '../../../Components/ValueSelector';
|
|
|
10
10
|
import { useOnePageAdaptableWizardContext } from '../../../Wizard/OnePageAdaptableWizard';
|
|
11
11
|
import { columnFilter } from './Utilities';
|
|
12
12
|
import ArrayExtensions from '../../../../Utilities/Extensions/ArrayExtensions';
|
|
13
|
+
import { Select } from '../../../../components/Select';
|
|
14
|
+
import StringExtensions from '../../../../Utilities/Extensions/StringExtensions';
|
|
13
15
|
const WEIGHTED_AVERAGE_AGG_FN_NAME = 'weightedAvg';
|
|
14
16
|
export const isAggregationsSectionValid = (data) => {
|
|
15
17
|
const weightedAvg = data.TableAggregationColumns
|
|
@@ -190,7 +192,20 @@ export const AggregationsSection = (props) => {
|
|
|
190
192
|
React.createElement(Tabs.Content, null,
|
|
191
193
|
React.createElement(Flex, null,
|
|
192
194
|
React.createElement(FormLayout, null,
|
|
193
|
-
React.createElement(
|
|
195
|
+
React.createElement(FormRow, { label: 'Omit Aggregation from Header' },
|
|
196
|
+
React.createElement(CheckBox, { checked: layout.SuppressAggFuncInHeader, onChange: handleSuppressAggFuncInHeader })),
|
|
197
|
+
React.createElement(FormRow, { label: 'Grand Total Row' },
|
|
198
|
+
React.createElement(Select, { style: { width: 120 }, options: ['top', 'bottom'].map((position) => {
|
|
199
|
+
return {
|
|
200
|
+
label: StringExtensions.CapitaliseFirstLetter(position),
|
|
201
|
+
value: position,
|
|
202
|
+
};
|
|
203
|
+
}), placeholder: "Off", value: layout.GrandTotalRow, onChange: (value) => {
|
|
204
|
+
props.onChange({
|
|
205
|
+
...layout,
|
|
206
|
+
GrandTotalRow: value,
|
|
207
|
+
});
|
|
208
|
+
}, isClearable: true })))),
|
|
194
209
|
React.createElement(ValueSelector, { showFilterInput: true, showSelectedOnlyPosition: "top", filter: columnFilter, toIdentifier: (option) => `${option.columnId}`, toLabel: (option) => option.friendlyName ?? option.columnId, toListLabel: (column) => (React.createElement(ColumnRow, { onChangeAggFunction: handleAggregationChange, layout: layout, column: column, aggregationColumnsMap: aggregationColumnsMap, numberColumns: numberColumns })), options: sortedAggregableColumns, value: (layout.TableAggregationColumns || []).map((agg) => agg.ColumnId), allowReorder: () => true, xSelectedLabel: () => {
|
|
195
210
|
return `Active aggregations:`;
|
|
196
211
|
}, onChange: handleColumnsSelectionChange }))));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export declare const
|
|
2
|
+
import { PivotLayout } from '../../../../../types';
|
|
3
|
+
export declare const isPivotAggregationsSectionValid: (data: PivotLayout) => true | string;
|
|
4
4
|
export declare const PivotAggregationsSectionSummary: React.FunctionComponent;
|
|
5
5
|
interface PivotAggregationsSectionProps {
|
|
6
6
|
onChange: (data: PivotLayout) => void;
|
|
@@ -5,11 +5,14 @@ import DropdownButton from '../../../../components/DropdownButton';
|
|
|
5
5
|
import FormLayout, { FormRow } from '../../../../components/FormLayout';
|
|
6
6
|
import { Tabs } from '../../../../components/Tabs';
|
|
7
7
|
import { Tag } from '../../../../components/Tag';
|
|
8
|
+
import { getAggFuncName, WEIGHTED_AVERAGE_AGG_FN_NAME, } from '../../../../AdaptableState/Common/AggregationColumns';
|
|
8
9
|
import { useAdaptable } from '../../../AdaptableContext';
|
|
9
10
|
import { ValueSelector } from '../../../Components/ValueSelector';
|
|
10
11
|
import { useOnePageAdaptableWizardContext } from '../../../Wizard/OnePageAdaptableWizard';
|
|
11
12
|
import { columnFilter } from './Utilities';
|
|
12
13
|
import ArrayExtensions from '../../../../Utilities/Extensions/ArrayExtensions';
|
|
14
|
+
import StringExtensions from '../../../../Utilities/Extensions/StringExtensions';
|
|
15
|
+
import { Select } from '../../../../components/Select';
|
|
13
16
|
const PivotColumnRow = (props) => {
|
|
14
17
|
const adaptable = useAdaptable();
|
|
15
18
|
const aggValue = (props.layout.PivotAggregationColumns || []).find((x) => x.ColumnId === props.column.columnId)?.AggFunc;
|
|
@@ -21,31 +24,31 @@ const PivotColumnRow = (props) => {
|
|
|
21
24
|
return {
|
|
22
25
|
label: fnName,
|
|
23
26
|
onClick: () => {
|
|
24
|
-
let aggCols = props.layout.PivotAggregationColumns;
|
|
27
|
+
let aggCols = props.layout.PivotAggregationColumns || [];
|
|
25
28
|
if (!aggCols) {
|
|
26
29
|
return;
|
|
27
30
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
const AggFuncValue = fnName === WEIGHTED_AVERAGE_AGG_FN_NAME
|
|
32
|
+
? {
|
|
33
|
+
type: 'weightedAverage',
|
|
34
|
+
weightedColumnId: null,
|
|
35
|
+
}
|
|
36
|
+
: fnName;
|
|
37
|
+
let found = false;
|
|
38
|
+
aggCols = aggCols.map(({ ColumnId, AggFunc }) => {
|
|
39
|
+
if (ColumnId === props.column.columnId) {
|
|
40
|
+
found = true;
|
|
41
|
+
return {
|
|
42
|
+
ColumnId,
|
|
43
|
+
AggFunc: AggFuncValue,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { ColumnId, AggFunc };
|
|
47
|
+
});
|
|
48
|
+
if (!found) {
|
|
49
|
+
aggCols.push({
|
|
50
|
+
ColumnId: props.column.columnId,
|
|
51
|
+
AggFunc: AggFuncValue,
|
|
49
52
|
});
|
|
50
53
|
}
|
|
51
54
|
props.onChangeAggFunction(aggCols);
|
|
@@ -62,16 +65,27 @@ const PivotColumnRow = (props) => {
|
|
|
62
65
|
if (!aggCols) {
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
const AggFuncValue = {
|
|
69
|
+
type: 'weightedAverage',
|
|
70
|
+
weightedColumnId: col.columnId,
|
|
71
|
+
};
|
|
72
|
+
let found = false;
|
|
73
|
+
aggCols = aggCols.map(({ ColumnId, AggFunc }) => {
|
|
74
|
+
if (ColumnId === props.column.columnId) {
|
|
75
|
+
found = true;
|
|
67
76
|
return {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
weightedColumnId: col.columnId,
|
|
77
|
+
ColumnId,
|
|
78
|
+
AggFunc: AggFuncValue,
|
|
71
79
|
};
|
|
72
80
|
}
|
|
73
|
-
return
|
|
81
|
+
return { ColumnId, AggFunc };
|
|
74
82
|
});
|
|
83
|
+
if (!found) {
|
|
84
|
+
aggCols.push({
|
|
85
|
+
ColumnId: props.column.columnId,
|
|
86
|
+
AggFunc: AggFuncValue,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
75
89
|
props.onChangeAggFunction(aggCols);
|
|
76
90
|
},
|
|
77
91
|
};
|
|
@@ -83,18 +97,122 @@ const PivotColumnRow = (props) => {
|
|
|
83
97
|
? adaptable.api.columnApi.getFriendlyNameForColumnId(aggValue.weightedColumnId)
|
|
84
98
|
: 'Select Weight';
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
100
|
+
const totalOptions = ['Off', 'Before', 'After', 'Pivot Specific'].map((totalVariant) => {
|
|
101
|
+
return {
|
|
102
|
+
label: totalVariant,
|
|
103
|
+
onClick: () => {
|
|
104
|
+
let aggCols = props.layout.PivotAggregationColumns;
|
|
105
|
+
if (!aggCols) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
aggCols = aggCols.map((aggCol) => {
|
|
109
|
+
if (aggCol.ColumnId === props.column.columnId) {
|
|
110
|
+
let updatedTotalColumn = false;
|
|
111
|
+
if (totalVariant === 'Before') {
|
|
112
|
+
updatedTotalColumn = 'before';
|
|
113
|
+
}
|
|
114
|
+
if (totalVariant === 'After') {
|
|
115
|
+
updatedTotalColumn = 'after';
|
|
116
|
+
}
|
|
117
|
+
if (totalVariant === 'Pivot Specific') {
|
|
118
|
+
updatedTotalColumn = props.layout.PivotColumns.map((pivotColId) => {
|
|
119
|
+
return {
|
|
120
|
+
PivotColumnId: pivotColId,
|
|
121
|
+
ShowTotal: true,
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
...aggCol,
|
|
127
|
+
TotalColumn: updatedTotalColumn,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return aggCol;
|
|
131
|
+
});
|
|
132
|
+
props.onChangeAggFunction(aggCols);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
const currentAggCol = props.layout.PivotAggregationColumns?.find((aggCol) => aggCol.ColumnId === props.column.columnId);
|
|
137
|
+
const currentTotal = currentAggCol?.TotalColumn
|
|
138
|
+
? currentAggCol.TotalColumn === true || currentAggCol.TotalColumn === 'before'
|
|
139
|
+
? 'Before'
|
|
140
|
+
: currentAggCol.TotalColumn === 'after'
|
|
141
|
+
? 'After'
|
|
142
|
+
: Array.isArray(currentAggCol.TotalColumn)
|
|
143
|
+
? 'Pivot Specific'
|
|
144
|
+
: 'Off'
|
|
145
|
+
: 'Off';
|
|
146
|
+
const hasPivotSpecificTotals = Array.isArray(currentAggCol?.TotalColumn);
|
|
147
|
+
const pivotSpecificTotals = Array.isArray(currentAggCol?.TotalColumn)
|
|
148
|
+
? currentAggCol.TotalColumn
|
|
149
|
+
: props.layout.PivotColumns?.map((pivotColId) => ({
|
|
150
|
+
PivotColumnId: pivotColId,
|
|
151
|
+
ShowTotal: false,
|
|
152
|
+
}));
|
|
153
|
+
const handleTotalChange = (pivotColId, value) => {
|
|
154
|
+
const updatedAggCols = (props.layout.PivotAggregationColumns || []).map((aggCol) => {
|
|
155
|
+
if (aggCol.ColumnId === props.column.columnId) {
|
|
156
|
+
const updatedTotals = pivotSpecificTotals.map((total) => {
|
|
157
|
+
if (total.PivotColumnId === pivotColId) {
|
|
158
|
+
return { ...total, ShowTotal: value };
|
|
159
|
+
}
|
|
160
|
+
return total;
|
|
161
|
+
});
|
|
162
|
+
return { ...aggCol, TotalColumn: updatedTotals };
|
|
163
|
+
}
|
|
164
|
+
return aggCol;
|
|
165
|
+
});
|
|
166
|
+
props.onChangeAggFunction(updatedAggCols);
|
|
167
|
+
};
|
|
168
|
+
const pivotTotalOptions = ['Off', 'Before', 'After'].map((option) => ({
|
|
169
|
+
label: option,
|
|
170
|
+
value: option === 'Off' ? false : option.toLowerCase(),
|
|
171
|
+
}));
|
|
172
|
+
return (React.createElement(Box, { "data-name": props.column.columnId, className: "ab-Layout-Wizard__ColumnRow" },
|
|
173
|
+
React.createElement(Flex, { alignItems: "flex-start" },
|
|
174
|
+
React.createElement(Flex, { flex: "0 0 auto", alignItems: "center" },
|
|
175
|
+
React.createElement(Flex, { minWidth: 80 }, props.column.friendlyName),
|
|
176
|
+
aggValue && (React.createElement(DropdownButton, { columns: ['label'], items: aggOptions, ml: 2 }, currentAggFnName)),
|
|
177
|
+
currentAggFnName === WEIGHTED_AVERAGE_AGG_FN_NAME && (React.createElement(Flex, { backgroundColor: "primary", ml: 3, alignItems: "center" },
|
|
178
|
+
React.createElement(Text, null, "Weight"),
|
|
179
|
+
' ',
|
|
180
|
+
React.createElement(DropdownButton, { columns: ['label'], items: numericColumnsOptions, ml: 2 }, weightName))),
|
|
181
|
+
aggValue && (React.createElement(Flex, { backgroundColor: "primary", ml: 3, alignItems: "center" },
|
|
182
|
+
React.createElement(Text, null, "Total Column"),
|
|
183
|
+
' ',
|
|
184
|
+
React.createElement(DropdownButton, { columns: ['label'], items: totalOptions, ml: 2 }, currentTotal)))),
|
|
185
|
+
hasPivotSpecificTotals && (React.createElement(Flex, { ml: 2, pl: 2, flexWrap: "wrap", flex: "1 1 auto", alignItems: "center", style: {
|
|
186
|
+
minWidth: '200px',
|
|
187
|
+
borderLeft: '1px solid var(--ab-color-text-on-primary)',
|
|
188
|
+
rowGap: '8px',
|
|
189
|
+
columnGap: '12px',
|
|
190
|
+
} }, props.layout.PivotColumns.map((pivotColId) => {
|
|
191
|
+
const pivotTotalSetting = pivotSpecificTotals.find((t) => t.PivotColumnId === pivotColId);
|
|
192
|
+
const currentValue = pivotTotalSetting == undefined
|
|
193
|
+
? 'Off'
|
|
194
|
+
: pivotTotalSetting.ShowTotal === false
|
|
195
|
+
? 'Off'
|
|
196
|
+
: pivotTotalSetting.ShowTotal === 'before' ||
|
|
197
|
+
pivotTotalSetting.ShowTotal === true ||
|
|
198
|
+
pivotTotalSetting.ShowTotal == undefined
|
|
199
|
+
? 'Before'
|
|
200
|
+
: pivotTotalSetting.ShowTotal === 'after'
|
|
201
|
+
? 'After'
|
|
202
|
+
: 'Off';
|
|
203
|
+
return (React.createElement(Flex, { key: pivotColId, alignItems: "center" },
|
|
204
|
+
React.createElement(Text, { mr: 1 },
|
|
205
|
+
adaptable.api.columnApi.getFriendlyNameForColumnId(pivotColId),
|
|
206
|
+
":"),
|
|
207
|
+
React.createElement(DropdownButton, { columns: ['label'], items: pivotTotalOptions.map((opt) => ({
|
|
208
|
+
label: opt.label,
|
|
209
|
+
onClick: () => handleTotalChange(pivotColId, opt.value),
|
|
210
|
+
})) }, currentValue)));
|
|
211
|
+
}))))));
|
|
93
212
|
};
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
? (data.TableAggregationColumns || [])
|
|
213
|
+
export const isPivotAggregationsSectionValid = (data) => {
|
|
214
|
+
const weightedAvg = data.PivotAggregationColumns
|
|
215
|
+
? (data.PivotAggregationColumns || [])
|
|
98
216
|
.map((agg) => agg.AggFunc)
|
|
99
217
|
.find((agg) => typeof agg === 'object' && agg.type === 'weightedAverage')
|
|
100
218
|
: null;
|
|
@@ -126,7 +244,6 @@ export const PivotAggregationsSection = (props) => {
|
|
|
126
244
|
const sortedAggregableColumns = React.useMemo(() => {
|
|
127
245
|
return ArrayExtensions.sortArrayWithOrder(allAggregableColumns.map((col) => col.columnId), (layout.PivotAggregationColumns ?? []).map((col) => col.ColumnId), { sortUnorderedItems: false }).map((colId) => adaptable.api.columnApi.getColumnWithColumnId(colId));
|
|
128
246
|
}, [layout, allAggregableColumns]);
|
|
129
|
-
globalThis.layout = layout;
|
|
130
247
|
const handleColumnsSelectionChange = React.useCallback((columnIds) => {
|
|
131
248
|
const currentAggColumns = layout.PivotAggregationColumns ?? [];
|
|
132
249
|
const PivotAggregationColumns = columnIds.map((colId) => {
|
|
@@ -134,6 +251,7 @@ export const PivotAggregationsSection = (props) => {
|
|
|
134
251
|
ColumnId: colId,
|
|
135
252
|
AggFunc: currentAggColumns.find((x) => x.ColumnId === colId)?.AggFunc ??
|
|
136
253
|
adaptable.api.columnApi.getDefaultAggFunc(colId),
|
|
254
|
+
TotalColumn: currentAggColumns.find((x) => x.ColumnId === colId)?.TotalColumn,
|
|
137
255
|
};
|
|
138
256
|
});
|
|
139
257
|
props.onChange({
|
|
@@ -141,10 +259,14 @@ export const PivotAggregationsSection = (props) => {
|
|
|
141
259
|
PivotAggregationColumns,
|
|
142
260
|
});
|
|
143
261
|
}, [layout]);
|
|
144
|
-
const handleAggregationChange = React.useCallback((
|
|
262
|
+
const handleAggregationChange = React.useCallback((pivotAggregationColumns) => {
|
|
263
|
+
if (pivotAggregationColumns.some((aggCol) => aggCol.TotalColumn !== false || aggCol.TotalColumn !== null)) {
|
|
264
|
+
// if any Aggregation Column has a Total Column, we need to disable the Pivot Group Total Column
|
|
265
|
+
delete layout['PivotGroupTotalColumn'];
|
|
266
|
+
}
|
|
145
267
|
props.onChange({
|
|
146
268
|
...layout,
|
|
147
|
-
PivotAggregationColumns,
|
|
269
|
+
PivotAggregationColumns: pivotAggregationColumns,
|
|
148
270
|
});
|
|
149
271
|
}, [layout]);
|
|
150
272
|
const aggregationColumnsMap = React.useMemo(() => {
|
|
@@ -174,12 +296,66 @@ export const PivotAggregationsSection = (props) => {
|
|
|
174
296
|
SuppressAggFuncInHeader: checked,
|
|
175
297
|
});
|
|
176
298
|
};
|
|
299
|
+
const checkIfPivotGroupTotalColumnEnabled = () => {
|
|
300
|
+
// check that all AggregationColumns have identical AggFuncs
|
|
301
|
+
const aggregationColumns = layout.PivotAggregationColumns;
|
|
302
|
+
if (!aggregationColumns || aggregationColumns.length === 0) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
const firstAggFuncName = getAggFuncName(aggregationColumns[0].AggFunc);
|
|
306
|
+
if (layout.PivotAggregationColumns?.some((aggCol) => getAggFuncName(aggCol.AggFunc) !== firstAggFuncName)) {
|
|
307
|
+
return 'Requires identical aggregation on all columns';
|
|
308
|
+
}
|
|
309
|
+
// check that no Aggregation Total Column is enabled
|
|
310
|
+
const hasAggregationTotalColumn = aggregationColumns.some((aggCol) => aggCol.TotalColumn !== false && aggCol.TotalColumn != null);
|
|
311
|
+
if (hasAggregationTotalColumn) {
|
|
312
|
+
return 'Can only be enabled when no Aggregation Total Column is present';
|
|
313
|
+
}
|
|
314
|
+
return true;
|
|
315
|
+
};
|
|
316
|
+
const isPivotGroupTotalColumnEnabled = checkIfPivotGroupTotalColumnEnabled();
|
|
177
317
|
return (React.createElement(Tabs, { style: { height: '100%' } },
|
|
178
318
|
React.createElement(Tabs.Tab, null, "Column Aggregations"),
|
|
179
319
|
React.createElement(Tabs.Content, null,
|
|
180
320
|
React.createElement(Flex, null,
|
|
181
321
|
React.createElement(FormLayout, null,
|
|
182
|
-
React.createElement(
|
|
322
|
+
React.createElement(FormRow, { label: 'Omit Aggregation from Header' },
|
|
323
|
+
React.createElement(CheckBox, { checked: layout.SuppressAggFuncInHeader, onChange: handleSuppressAggFuncInHeader })),
|
|
324
|
+
React.createElement(FormRow, { label: 'Grand Total Row' },
|
|
325
|
+
React.createElement(Select, { style: { width: 120 }, options: ['top', 'bottom'].map((position) => {
|
|
326
|
+
return {
|
|
327
|
+
label: StringExtensions.CapitaliseFirstLetter(position),
|
|
328
|
+
value: position,
|
|
329
|
+
};
|
|
330
|
+
}), placeholder: "Off", value: layout.GrandTotalRow, onChange: (value) => {
|
|
331
|
+
props.onChange({
|
|
332
|
+
...layout,
|
|
333
|
+
GrandTotalRow: value,
|
|
334
|
+
});
|
|
335
|
+
}, isClearable: true })),
|
|
336
|
+
React.createElement(FormRow, { label: 'Grand Total Column' },
|
|
337
|
+
React.createElement(Select, { style: { width: 120 }, options: ['before', 'after'].map((position) => {
|
|
338
|
+
return {
|
|
339
|
+
label: StringExtensions.CapitaliseFirstLetter(position),
|
|
340
|
+
value: position,
|
|
341
|
+
};
|
|
342
|
+
}), placeholder: "Off", value: layout.GrandTotalColumn, onChange: (value) => {
|
|
343
|
+
props.onChange({
|
|
344
|
+
...layout,
|
|
345
|
+
GrandTotalColumn: value,
|
|
346
|
+
});
|
|
347
|
+
}, isClearable: true })),
|
|
348
|
+
React.createElement(FormRow, { label: 'Pivot Group Total Column' }, isPivotGroupTotalColumnEnabled === true ? (React.createElement(Select, { style: { width: 120 }, options: ['before', 'after'].map((position) => {
|
|
349
|
+
return {
|
|
350
|
+
label: StringExtensions.CapitaliseFirstLetter(position),
|
|
351
|
+
value: position,
|
|
352
|
+
};
|
|
353
|
+
}), placeholder: "Off", value: layout.PivotGroupTotalColumn, onChange: (value) => {
|
|
354
|
+
props.onChange({
|
|
355
|
+
...layout,
|
|
356
|
+
PivotGroupTotalColumn: value,
|
|
357
|
+
});
|
|
358
|
+
}, isClearable: true })) : (React.createElement(Text, { marginLeft: 1, style: { fontStyle: 'italic' } }, isPivotGroupTotalColumnEnabled))))),
|
|
183
359
|
React.createElement(ValueSelector, { showFilterInput: true, showSelectedOnlyPosition: "top", filter: columnFilter, toIdentifier: (option) => `${option.columnId}`, toLabel: (option) => option.friendlyName ?? option.columnId, toListLabel: (column) => (React.createElement(PivotColumnRow, { onChangeAggFunction: handleAggregationChange, layout: layout, column: column, aggregationColumnsMap: aggregationColumnsMap, numberColumns: numberColumns })), options: sortedAggregableColumns, value: (layout.PivotAggregationColumns || []).map((col) => col.ColumnId), allowReorder: true, xSelectedLabel: () => {
|
|
184
360
|
return `Active aggregations:`;
|
|
185
361
|
}, onChange: handleColumnsSelectionChange }))));
|
|
@@ -332,6 +332,7 @@ export declare class AdaptableAgGrid implements IAdaptable {
|
|
|
332
332
|
private filterOnDataChange;
|
|
333
333
|
refreshLayout(): void;
|
|
334
334
|
private isRowGroupDifferentInLayout;
|
|
335
|
+
private hasPivotTotalsInLayout;
|
|
335
336
|
private onLayoutChange;
|
|
336
337
|
private validateColumnDefTypes;
|
|
337
338
|
}
|
|
@@ -3313,13 +3313,21 @@ You need to define at least one Layout!`);
|
|
|
3313
3313
|
}
|
|
3314
3314
|
return prevRowGroupedColumns.join(',') !== currentRowGroupedColumns.join(',');
|
|
3315
3315
|
}
|
|
3316
|
+
hasPivotTotalsInLayout(one, other) {
|
|
3317
|
+
const prevAggregationColumns = one.PivotAggregationColumns || [];
|
|
3318
|
+
const currentAggregationColumns = other.PivotAggregationColumns || [];
|
|
3319
|
+
const prevHasPivotTotals = prevAggregationColumns.some((col) => !!col.TotalColumn);
|
|
3320
|
+
const currentHasPivotTotals = currentAggregationColumns.some((col) => !!col.TotalColumn);
|
|
3321
|
+
return prevHasPivotTotals || currentHasPivotTotals;
|
|
3322
|
+
}
|
|
3316
3323
|
onLayoutChange(layout) {
|
|
3317
3324
|
this.logger.info('onLayoutChange()');
|
|
3318
3325
|
const prevOnChangeLayout = this.__prevLayoutForOnChange || this.api.layoutApi.getCurrentLayout();
|
|
3319
3326
|
// see #on-regroup-expect-group-column-to-be-recomputed-and-setup-properly
|
|
3320
3327
|
const rowGroupsChanged = this.isRowGroupDifferentInLayout(prevOnChangeLayout, layout);
|
|
3328
|
+
const hasPivotTotalsInLayout = this.hasPivotTotalsInLayout(prevOnChangeLayout, layout);
|
|
3321
3329
|
const pivotColsChanged = JSON.stringify(layout.PivotColumns) !== JSON.stringify(prevOnChangeLayout.PivotColumns);
|
|
3322
|
-
if (rowGroupsChanged || pivotColsChanged) {
|
|
3330
|
+
if (rowGroupsChanged || pivotColsChanged || hasPivotTotalsInLayout) {
|
|
3323
3331
|
this.updateColumnModelAndRefreshGrid();
|
|
3324
3332
|
}
|
|
3325
3333
|
else {
|