UNPKG

@adaptabletools/adaptable

Version:

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

244 lines (243 loc) 12.7 kB
import * as React from 'react'; import { Box, Flex } from 'rebass'; import { CheckBox } from '../../../../components/CheckBox'; import FormLayout, { FormRow } from '../../../../components/FormLayout'; import Panel from '../../../../components/Panel'; import { Select } from '../../../../components/Select'; import SimpleButton from '../../../../components/SimpleButton'; import { Tabs } from '../../../../components/Tabs'; import { Tag } from '../../../../components/Tag'; import { summarySupportedExpressions, WEIGHTED_AVERAGE_AGGREGATED_FUNCTION, } from '../../../../AdaptableState/Common/RowSummary'; import { mapColumnDataTypeToExpressionFunctionType } from '../../../../Utilities/adaptableQlUtils'; import { LayoutModuleId } from '../../../../Utilities/Constants/ModuleConstants'; import { aggregatedExpressionFunctions } from '../../../../Utilities/ExpressionFunctions/aggregatedScalarExpressionFunctions'; import { useAdaptable } from '../../../AdaptableContext'; import { SuspendToggleButton } from '../../../Components/Buttons/SuspendToggleButton'; import { ValueSelector } from '../../../Components/ValueSelector'; import { useOnePageAdaptableWizardContext } from '../../../Wizard/OnePageAdaptableWizard'; import { columnFilter } from './Utilities'; import ArrayExtensions from '../../../../Utilities/Extensions/ArrayExtensions'; export const areSummaryRowsValid = (layout) => { if (!layout.RowSummaries) return true; let valid = true; layout.RowSummaries?.find((rowSummary) => { for (const [_, fn] of Object.entries(rowSummary.ColumnsMap ?? {})) { if (!fn) { valid = 'All row summary columns must have an aggregation function.'; return true; } } }); return valid; }; const availableExpressionsForColumnTypeCache = new Map(); const getAvailableExpressionsForColumnType = (columnType, availableScalarExpressions) => { const key = columnType; if (availableExpressionsForColumnTypeCache.has(key)) { return availableExpressionsForColumnTypeCache.get(key); } const columnInputType = mapColumnDataTypeToExpressionFunctionType(columnType); const expressions = Object.keys(availableScalarExpressions) .filter((availableExpression) => { if (!aggregatedExpressionFunctions.includes(availableExpression)) { // is custom return true; } return Boolean(summarySupportedExpressions.includes(availableExpression)); }) .map((expression) => { const expressionDef = availableScalarExpressions[expression]; let firstArg = null; // filter out expressions without inputs defined if (!expressionDef?.inputs) { return null; } if (Array.isArray(expressionDef?.inputs?.[0])) { // @ts-ignore firstArg = expressionDef.inputs.find((input) => input.includes(columnInputType))?.[0]; } else { firstArg = expressionDef.inputs[0]; } if (columnInputType === firstArg) { return expression; } else { return null; } }) .filter(Boolean); availableExpressionsForColumnTypeCache.set(key, expressions); return expressions; }; export const RowSummarySectionSummary = () => { const adaptable = useAdaptable(); const { data: layout } = useOnePageAdaptableWizardContext(); return (React.createElement(Box, { display: 'flex', flexDirection: 'column' }, layout.RowSummaries?.length ? (layout.RowSummaries.map((rowSummary, index) => { return (React.createElement(React.Fragment, null, React.createElement(Box, { mr: 1, mb: 1, key: index }, rowSummary.Position, ":", ' ', Object.keys(rowSummary.ColumnsMap).map((colId) => { if (colId === 'Source' || colId === 'Uuid') return null; return (React.createElement(Tag, { mr: 1, key: colId }, rowSummary.ColumnsMap[colId], "(", adaptable.api.columnApi.getFriendlyNameForColumnId(colId), ")")); })))); })) : (React.createElement(Tag, null, "No Row Summaries")))); }; const RowSummaryEditor = React.memo(({ rowSummary, onChange, availableScalarExpressions, onDelete, }) => { const { data: layout } = useOnePageAdaptableWizardContext(); const adaptable = useAdaptable(); const columns = React.useMemo(() => { const colIds = adaptable.api.columnApi .getUIAvailableColumns() .filter((column) => { if (!['number', 'text', 'date'].includes(column.dataType)) return false; return layout.TableColumns?.includes?.(column.columnId); }) .map((c) => c.columnId); return ArrayExtensions.sortArrayWithOrder(colIds, Object.keys(rowSummary.ColumnsMap), { sortUnorderedItems: false, }).map((colId) => adaptable.api.columnApi.getColumnWithColumnId(colId)); }, [rowSummary.ColumnsMap]); return (React.createElement(Panel, { header: React.createElement(Flex, { style: { width: '100%' } }, React.createElement(Flex, { flex: 1, alignItems: "center" }, "Row Summary"), React.createElement(Box, { mr: 2 }, React.createElement(SuspendToggleButton, { onSuspend: () => { onChange({ ...rowSummary, IsSuspended: true, }); }, onUnSuspend: () => { onChange({ ...rowSummary, IsSuspended: false, }); }, suspendableObject: rowSummary })), React.createElement(SimpleButton, { icon: "delete", onClick: () => { onDelete(); } })), p: 2 }, React.createElement(FormLayout, null, React.createElement(FormRow, { label: "Position" }, React.createElement(Select, { options: [ { label: 'Top', value: 'Top', }, { label: 'Bottom', value: 'Bottom', }, ], value: rowSummary.Position, onChange: (position) => { onChange({ ...rowSummary, Position: position, }); } })), React.createElement(FormRow, { label: "" }, React.createElement(CheckBox, { checked: rowSummary.IncludeOnlyFilteredRows ?? true, onChange: (IncludeOnlyFilteredRows) => { onChange({ ...rowSummary, IncludeOnlyFilteredRows, }); } }, "Include Only Filtered Rows"))), React.createElement(Flex, { flexDirection: 'column', mt: 2, mb: 1 }, React.createElement(Flex, { alignItems: "center", flex: 1, mb: 2 }, "Column Aggregations"), React.createElement(Panel, { bodyProps: { maxHeight: '100%' }, style: { height: 360 } }, React.createElement(ValueSelector, { style: { maxHeight: '100%' }, showFilterInput: true, filter: columnFilter, toIdentifier: (column) => column.columnId, toLabel: (option) => option.friendlyName ?? option.columnId, options: columns, xSelectedLabel: () => { return 'Active Aggregations:'; }, toListLabel: (column) => { const label = column.friendlyName ?? column.columnId; if (!(column.columnId in (rowSummary.ColumnsMap ?? {}))) { return label; } const expressionOptions = getAvailableExpressionsForColumnType(column.dataType, availableScalarExpressions).map((expression) => ({ label: expression, value: expression, })); // check out if this layout has a aggregation with weighted column const aggregation = layout.TableAggregationColumns?.find((agg) => agg.ColumnId === column.columnId)?.AggFunc; if (aggregation && typeof aggregation === 'object' && aggregation.weightedColumnId) { expressionOptions.push({ label: 'WEIGHTERD_AVG', value: WEIGHTED_AVERAGE_AGGREGATED_FUNCTION, }); } const expression = rowSummary.ColumnsMap[column.columnId]; return (React.createElement(Flex, null, React.createElement(Flex, { mr: 2, alignItems: 'center' }, label), React.createElement(Select, { value: expression, options: expressionOptions, onChange: (expression) => { onChange({ ...rowSummary, ColumnsMap: { ...rowSummary.ColumnsMap, [column.columnId]: expression, }, }); } }))); }, value: Object.keys(rowSummary.ColumnsMap), onChange: (colIds) => { const newColumnsMap = {}; colIds.forEach((colId) => { newColumnsMap[colId] = rowSummary.ColumnsMap[colId] ?? null; }); onChange({ ...rowSummary, ColumnsMap: newColumnsMap, }); } }))))); }); export const RowSummarySection = (props) => { const adaptable = useAdaptable(); const { data: layout } = useOnePageAdaptableWizardContext(); const availableScalarExpressions = React.useMemo(() => { const sytemExpressions = adaptable.api.internalApi .getQueryLanguageService() .getModuleExpressionFunctionsMap(LayoutModuleId).aggregatedScalarFunctions; return sytemExpressions; }, []); return (React.createElement(Tabs, { style: { height: '100%' } }, React.createElement(Tabs.Tab, null, "Row Summaries"), React.createElement(Tabs.Content, null, React.createElement("div", null, React.createElement(Flex, { mb: 1 }, React.createElement(Flex, { flex: 1 }), React.createElement(SimpleButton, { onClick: () => { props.onChange({ ...layout, RowSummaries: [ ...(layout.RowSummaries ?? []), { Position: 'Top', ColumnsMap: {}, IncludeOnlyFilteredRows: true, }, ], }); }, icon: "plus" }, "Add Row Summary")), (layout.RowSummaries ?? []).map((rowSummary, index) => { return (React.createElement(RowSummaryEditor, { key: index, onDelete: () => { const newSummaries = [...layout.RowSummaries]; newSummaries.splice(index, 1); props.onChange({ ...layout, RowSummaries: newSummaries, }); }, availableScalarExpressions: availableScalarExpressions, rowSummary: rowSummary, onChange: (rowSummary) => { const newSummaries = [...layout.RowSummaries]; newSummaries[index] = rowSummary; props.onChange({ ...layout, RowSummaries: newSummaries, }); } })); }))))); };