@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.
Files changed (48) hide show
  1. package/agGrid.d.ts +37 -0
  2. package/agGrid.js +38 -0
  3. package/base.css +19 -3
  4. package/base.css.map +1 -1
  5. package/index.css +17 -3
  6. package/index.css.map +1 -1
  7. package/package.json +3 -3
  8. package/src/AdaptableOptions/LayoutOptions.d.ts +3 -6
  9. package/src/AdaptableState/Common/AdaptableColumn.d.ts +14 -5
  10. package/src/AdaptableState/Common/AdaptableColumn.js +26 -0
  11. package/src/AdaptableState/Common/AggregationColumns.d.ts +10 -0
  12. package/src/AdaptableState/Common/AggregationColumns.js +11 -0
  13. package/src/AdaptableState/LayoutState.d.ts +4 -4
  14. package/src/Api/Implementation/ColumnApiImpl.d.ts +2 -2
  15. package/src/Api/Implementation/ColumnApiImpl.js +36 -3
  16. package/src/Api/Implementation/LayoutHelpers.js +7 -0
  17. package/src/Api/Internal/ColumnInternalApi.js +3 -3
  18. package/src/View/Alert/Wizard/AlertButtonsEditor.js +9 -10
  19. package/src/View/Alert/Wizard/AlertNotificationWizardSection.js +26 -25
  20. package/src/View/Components/ColumnFilter/ColumnFilter.js +1 -1
  21. package/src/View/Components/FilterForm/ListBoxFilterForm.js +6 -3
  22. package/src/View/Components/NewScopeComponent.js +2 -1
  23. package/src/View/Layout/Wizard/LayoutWizard.js +2 -2
  24. package/src/View/Layout/Wizard/sections/AggregationsSection.js +16 -1
  25. package/src/View/Layout/Wizard/sections/PivotAggregationsSection.d.ts +2 -2
  26. package/src/View/Layout/Wizard/sections/PivotAggregationsSection.js +219 -43
  27. package/src/agGrid/AdaptableAgGrid.d.ts +1 -0
  28. package/src/agGrid/AdaptableAgGrid.js +9 -1
  29. package/src/agGrid/AgGridAdapter.d.ts +1 -0
  30. package/src/agGrid/AgGridAdapter.js +10 -1
  31. package/src/components/Select/Select.js +15 -3
  32. package/src/env.js +2 -2
  33. package/src/layout-manager/src/LayoutManagerModel.d.ts +4 -4
  34. package/src/layout-manager/src/index.d.ts +1 -1
  35. package/src/layout-manager/src/index.js +41 -21
  36. package/src/layout-manager/src/isPivotAggTotalColumn.d.ts +2 -0
  37. package/src/layout-manager/src/{isPivotTotalColumn.js → isPivotAggTotalColumn.js} +1 -1
  38. package/src/layout-manager/src/isPivotGrandTotalColumn.d.ts +2 -0
  39. package/src/layout-manager/src/isPivotGrandTotalColumn.js +3 -0
  40. package/src/layout-manager/src/isPivotGroupTotalColumn.d.ts +1 -2
  41. package/src/layout-manager/src/isPivotGroupTotalColumn.js +2 -2
  42. package/src/layout-manager/src/normalizeLayoutModel.js +0 -3
  43. package/src/layout-manager/src/simplifyLayoutModel.js +3 -0
  44. package/src/metamodel/adaptable.metamodel.d.ts +13 -7
  45. package/src/metamodel/adaptable.metamodel.js +1 -1
  46. package/src/types.d.ts +1 -1
  47. package/tsconfig.esm.tsbuildinfo +1 -1
  48. 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 { isPivotTotalColumn } from '../../layout-manager/src/isPivotTotalColumn';
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 (isPivotTotalColumn(params.colDef)) {
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(Box, { mr: 2 },
183
- React.createElement(Select, { options: ['text', 'outlined', 'raised'].map((variant) => {
184
- return {
185
- label: StringExtensions.CapitaliseFirstLetter(variant),
186
- value: variant,
187
- };
188
- }), renderSingleValue: (option) => `Variant: ${option.label}`, value: buttonStyle?.variant, onChange: (value) => {
189
- setVariant(value);
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(CheckBox, { checked: data.AlertProperties?.DisplayNotification, onChange: (DisplayNotification) => {
68
- const newAlertDefinition = {
69
- ...data,
70
- AlertProperties: {
71
- ...data.AlertProperties,
72
- DisplayNotification,
73
- },
74
- };
75
- if (!DisplayNotification && typeof newAlertDefinition.AlertForm === 'object') {
76
- delete newAlertDefinition.AlertForm.Buttons;
77
- }
78
- if (DisplayNotification && newAlertDefinition.AlertForm == undefined) {
79
- newAlertDefinition.AlertForm =
80
- api.alertApi.internalApi.getDefaultAlertNotificationForm();
81
- }
82
- // make sure we have at least one button
83
- if (typeof newAlertDefinition.AlertForm === 'object' &&
84
- (!newAlertDefinition.AlertForm?.Buttons ||
85
- newAlertDefinition.AlertForm?.Buttons?.length === 0)) {
86
- newAlertDefinition.AlertForm.Buttons = [
87
- api.alertApi.internalApi.getDefaultButton(),
88
- ];
89
- }
90
- props.onChange(newAlertDefinition);
91
- } }, "Display a Notification when Alert is triggered (with action buttons)"),
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 component = (React.createElement(Select, { isMulti: true, showHeaderSelectionCheckbox: true, searchable: true, closeMenuOnSelect: false, menuStyle: {
28
- minWidth: `var(--ab-cmp-select-column-menu-${column.columnId}__min-width, var(--ab-cmp-select-column-menu__min-width, 260px))`,
29
- }, ...props.selectProps, options: options, value: value, isLoading: props.isLoading, onChange: props.onChange }));
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) => isAggregationsSectionValid(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(CheckBox, { checked: layout.SuppressAggFuncInHeader, onChange: handleSuppressAggFuncInHeader }, "Suppress Aggregation Function Text in Column Header"))),
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 { Layout, PivotLayout } from '../../../../../types';
3
- export declare const isAggregationsSectionValid: (data: Layout) => true | string;
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
- if (fnName === WEIGHTED_AVERAGE_AGG_FN_NAME) {
29
- aggCols = aggCols.map((x) => {
30
- if (x.ColumnId === props.column.columnId) {
31
- return {
32
- ...x,
33
- type: 'weightedAverage',
34
- weightedColumnId: null,
35
- };
36
- }
37
- return x;
38
- });
39
- }
40
- else {
41
- aggCols = aggCols.map((x) => {
42
- if (x.ColumnId === props.column.columnId) {
43
- return {
44
- ...x,
45
- AggFunc: fnName,
46
- };
47
- }
48
- return x;
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
- aggCols = aggCols.map((x) => {
66
- if (x.ColumnId === props.column.columnId) {
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
- ...x,
69
- type: 'weightedAverage',
70
- weightedColumnId: col.columnId,
77
+ ColumnId,
78
+ AggFunc: AggFuncValue,
71
79
  };
72
80
  }
73
- return x;
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
- return (React.createElement(Flex, { alignItems: "center" },
87
- props.column.friendlyName,
88
- aggValue && (React.createElement(DropdownButton, { columns: ['label'], items: aggOptions, ml: 2 }, currentAggFnName)),
89
- currentAggFnName === WEIGHTED_AVERAGE_AGG_FN_NAME && (React.createElement(Flex, { backgroundColor: "primary", ml: 3, alignItems: "center" },
90
- React.createElement(Text, null, "Weight"),
91
- ' ',
92
- React.createElement(DropdownButton, { columns: ['label'], items: numericColumnsOptions, ml: 2 }, weightName)))));
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 WEIGHTED_AVERAGE_AGG_FN_NAME = 'weightedAvg';
95
- export const isAggregationsSectionValid = (data) => {
96
- const weightedAvg = data.TableAggregationColumns
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((PivotAggregationColumns) => {
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(CheckBox, { checked: layout.SuppressAggFuncInHeader, onChange: handleSuppressAggFuncInHeader }, "Suppress Aggregation Function Text in Column Header"))),
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 {
@@ -63,6 +63,7 @@ export declare class AgGridAdapter {
63
63
  private isCalculatedColumn;
64
64
  private isFreeTextColumn;
65
65
  private isActionColumn;
66
+ private isPivotTotalColumn;
66
67
  private isColumnFilterable;
67
68
  private getColumnTypes;
68
69
  private getColumnPinnedPosition;