UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

226 lines (225 loc) 11.5 kB
import * as React from 'react'; import { Box, Flex, Text } from 'rebass'; import { CheckBox } from '../../../../components/CheckBox'; import DropdownButton from '../../../../components/DropdownButton'; import FormLayout, { FormRow } from '../../../../components/FormLayout'; import { Tabs } from '../../../../components/Tabs'; import { Tag } from '../../../../components/Tag'; import { useAdaptable } from '../../../AdaptableContext'; import { ValueSelector } from '../../../Components/ValueSelector'; import { useOnePageAdaptableWizardContext } from '../../../Wizard/OnePageAdaptableWizard'; import { columnFilter } from './Utilities'; import ArrayExtensions from '../../../../Utilities/Extensions/ArrayExtensions'; import { Select } from '../../../../components/Select'; import StringExtensions from '../../../../Utilities/Extensions/StringExtensions'; const WEIGHTED_AVERAGE_AGG_FN_NAME = 'weightedAvg'; export const isAggregationsSectionValid = (data) => { const weightedAvg = data.TableAggregationColumns ? (data.TableAggregationColumns || []).find(({ AggFunc }) => typeof AggFunc === 'object' && AggFunc.type === 'weightedAverage')?.AggFunc : null; if (weightedAvg && !weightedAvg.weightedColumnId) { return 'The Weighted Average Aggregation requires a Weighted Column'; } return true; }; export const AggregationsSectionSummary = () => { const adaptable = useAdaptable(); const { data: layout } = useOnePageAdaptableWizardContext(); const entries = layout.TableAggregationColumns || []; let content = null; if (entries.length) { content = (React.createElement(FormLayout, null, entries.map(({ ColumnId, AggFunc }) => (React.createElement(FormRow, { key: ColumnId, label: adaptable.api.columnApi.getFriendlyNameForColumnId(ColumnId) }, React.createElement(Tag, null, typeof AggFunc === 'object' ? AggFunc.type : AggFunc)))))); } else { content = React.createElement(Tag, null, "No Aggregations"); } return React.createElement(Box, null, content); }; const ColumnRow = (props) => { const adaptable = useAdaptable(); const aggValue = props.layout?.TableAggregationColumns?.find((agg) => agg.ColumnId === props.column.columnId)?.AggFunc; const adaptableAggFunctions = []; if (props.column.dataType === 'number' && props.column.aggregatable) { adaptableAggFunctions.push(WEIGHTED_AVERAGE_AGG_FN_NAME); } // props.column.availableAggregationFunctions is null for non-aggregatable columns // but we want to support non-aggregatable columns that are specified to be aggregated by default // so we need to allow this. as soon as they will be unchecked from aggregation, they will no longer be displayed in the aggregation wizard. // but until the user checks them off, we want to allow them. const availableAggregationFunctions = props.column.availableAggregationFunctions || []; const aggOptions = [...new Set([...availableAggregationFunctions, ...adaptableAggFunctions])].map((fnName) => { return { label: fnName, onClick: () => { let aggCols = props.layout.TableAggregationColumns || []; if (!aggCols) { return; } const AggFuncValue = fnName === WEIGHTED_AVERAGE_AGG_FN_NAME ? { type: 'weightedAverage', weightedColumnId: null, } : fnName; let found = false; aggCols = aggCols.map(({ ColumnId, AggFunc }) => { if (ColumnId === props.column.columnId) { found = true; return { ColumnId, AggFunc: AggFuncValue, }; } return { ColumnId, AggFunc }; }); if (!found) { aggCols.push({ ColumnId: props.column.columnId, AggFunc: AggFuncValue, }); } props.onChangeAggFunction(aggCols); }, }; }); const numericColumnsOptions = props.numberColumns .filter((col) => col.columnId !== props.column.columnId) .map((col) => { return { label: col.friendlyName, onClick: () => { let aggCols = [...(props.layout.TableAggregationColumns || [])]; if (!aggCols || !aggCols.length) { return; } const AggFuncValue = { type: 'weightedAverage', weightedColumnId: col.columnId, }; let found = false; aggCols = aggCols.map(({ ColumnId, AggFunc }) => { if (ColumnId === props.column.columnId) { found = true; return { ColumnId, AggFunc: AggFuncValue, }; } return { ColumnId, AggFunc }; }); if (!found) { aggCols.push({ ColumnId: props.column.columnId, AggFunc: AggFuncValue, }); } props.onChangeAggFunction(aggCols); }, }; }); const currentAggFnName = props.aggregationColumnsMap[props.column.columnId]; let weightName = null; if (typeof aggValue === 'object' && aggValue.type === 'weightedAverage') { weightName = aggValue.weightedColumnId ? adaptable.api.columnApi.getFriendlyNameForColumnId(aggValue.weightedColumnId) : 'Select Weight'; } return (React.createElement(Flex, { alignItems: "center" }, props.column.friendlyName, aggValue && (React.createElement(DropdownButton, { columns: ['label'], items: aggOptions, ml: 2 }, currentAggFnName)), currentAggFnName === WEIGHTED_AVERAGE_AGG_FN_NAME && (React.createElement(Flex, { backgroundColor: "primary", ml: 3, alignItems: "center" }, React.createElement(Text, null, "Weight"), ' ', React.createElement(DropdownButton, { columns: ['label'], items: numericColumnsOptions, ml: 2 }, weightName))))); }; export const AggregationsSection = (props) => { const adaptable = useAdaptable(); const { data: layout } = useOnePageAdaptableWizardContext(); const allAggregableColumns = adaptable.api.columnApi.getAggregatableColumns(); const allColumns = adaptable.api.columnApi.getUIAvailableColumns(); const numberColumns = adaptable.api.columnApi.getNumericColumns(); const allAggregatableColumnIds = allAggregableColumns.map((col) => col.columnId); (layout.TableAggregationColumns || []).forEach((agg) => { // we need to also display the columns currently aggregated, // even if they are not aggregatable if (!allAggregatableColumnIds.includes(agg.ColumnId)) { allAggregatableColumnIds.push(agg.ColumnId); } }); const sortedAggregableColumns = ArrayExtensions.sortArrayWithOrder(allAggregatableColumnIds, (layout.TableAggregationColumns ?? []).map((agg) => agg.ColumnId), { sortUnorderedItems: false }) .map((colId) => adaptable.api.columnApi.getColumnWithColumnId(colId)) .filter(Boolean); const handleColumnsSelectionChange = React.useCallback((columnIds) => { const currentAggsMap = (layout.TableAggregationColumns || []).reduce((acc, { ColumnId, AggFunc }) => { acc[ColumnId] = AggFunc; return acc; }, {}); props.onChange({ ...layout, TableAggregationColumns: columnIds.map((colId) => { const AggFunc = currentAggsMap[colId] ?? adaptable.api.columnApi.getDefaultAggFunc(colId); return { ColumnId: colId, AggFunc, }; }), }); }, [layout]); const handleAggregationChange = React.useCallback((aggFunctions) => { props.onChange({ ...layout, TableAggregationColumns: aggFunctions, }); }, [layout]); const aggregationColumnsMap = React.useMemo(() => { const allColumnsMap = allColumns.reduce((acc, col) => { acc[col.columnId] = col; return acc; }, {}); return (layout.TableAggregationColumns || []).reduce((acc, { ColumnId: colId, AggFunc }) => { let fn = AggFunc; let fnName = ''; if (typeof fn === 'boolean') { fnName = allColumnsMap[colId].aggregationFunction; } if (typeof fn === 'object' && fn.type === 'weightedAverage') { fnName = WEIGHTED_AVERAGE_AGG_FN_NAME; } else if (typeof fn === 'string') { fnName = fn; } acc[colId] = fnName; return acc; }, {}); }, [layout]); const handleSuppressAggFuncInHeader = (checked) => { props.onChange({ ...layout, SuppressAggFuncInHeader: checked, }); }; return (React.createElement(Tabs, { style: { height: '100%' } }, React.createElement(Tabs.Tab, null, "Column Aggregations"), React.createElement(Tabs.Content, null, React.createElement(Flex, null, React.createElement(FormLayout, null, React.createElement(FormRow, { label: 'Omit Aggregation from Header' }, React.createElement(CheckBox, { checked: layout.SuppressAggFuncInHeader, onChange: handleSuppressAggFuncInHeader })), React.createElement(FormRow, { label: 'Grand Total Row' }, React.createElement(Select, { style: { width: 160 }, options: ['top', 'bottom', 'pinnedTop', 'pinnedBottom'].map((position) => { return { label: StringExtensions.CamelCaseToHumanText(position), value: position, }; }), placeholder: "Off", value: layout.GrandTotalRow, onChange: (value) => { props.onChange({ ...layout, GrandTotalRow: value, }); }, isClearable: true })))), 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: () => { return `Active aggregations:`; }, onChange: handleColumnsSelectionChange })))); };