UNPKG

@adaptabletools/adaptable

Version:

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

258 lines (257 loc) 16.9 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import * as React from 'react'; import { CheckBox } from '../../../../components/CheckBox'; import FormLayout, { FormRow } from '../../../../components/FormLayout'; import SimpleButton from '../../../../components/SimpleButton'; import { ButtonNew } from '../../../Components/Buttons/ButtonNew'; import { Card } from '../../../../components/Card'; import { ColumnTag, 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 { ColumnGroupTag } from '../../../Components/ColumnGroupTag'; import ArrayExtensions from '../../../../Utilities/Extensions/ArrayExtensions'; import { Box, Flex } from '../../../../components/Flex'; import { SingleSelect } from '../../../../components/NewSelect'; import { CollapsibleWizardCard, getWizardAccordionSectionClassName, useWizardCardAccordion, } from '../../../Wizard/CollapsibleWizardCard'; import { cn } from '../../../../lib/utils'; import { objectListActionButtonClassName } from '../../../Components/AdaptableObjectList/objectListActionButtonStyles'; 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 = 'Each row summary column requires an aggregation function.'; return true; } } }); return valid; }; const rowSummaryCardId = (index) => `row-summary-${index}`; 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)) { return true; } return Boolean(summarySupportedExpressions.includes(availableExpression)); }) .map((expression) => { const expressionDef = availableScalarExpressions[expression]; let firstArg = null; if (!expressionDef?.inputs) { return null; } if (Array.isArray(expressionDef?.inputs?.[0])) { 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; }; const buildRowSummaryExpressionOptions = (column, availableScalarExpressions, layout) => { const expressionOptions = getAvailableExpressionsForColumnType(column.dataType, availableScalarExpressions).map((expression) => ({ label: expression, value: expression, })); 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, }); } return expressionOptions; }; const getDefaultRowSummaryExpression = (column, availableScalarExpressions, layout) => { const optionValues = buildRowSummaryExpressionOptions(column, availableScalarExpressions, layout).map((option) => option.value); if (optionValues.includes('sum')) { return 'sum'; } const sumExpression = optionValues.find((value) => value.toLowerCase() === 'sum'); if (sumExpression) { return sumExpression; } return optionValues[0] ?? null; }; const RowSummaryPositionTag = ({ position }) => _jsx(Tag, { className: "twa:shrink-0", children: position }); const RowSummaryCardSummary = ({ rowSummary }) => { const adaptable = useAdaptable(); const columnIds = Object.keys(rowSummary.ColumnsMap ?? {}).filter((colId) => colId !== 'Source' && colId !== 'Uuid'); if (!columnIds.length) { return _jsx(Tag, { children: "No columns" }); } return (_jsx(Flex, { flexWrap: "wrap", className: "twa:gap-1", children: columnIds.map((colId) => (_jsxs(ColumnTag, { children: [rowSummary.ColumnsMap[colId], "(", adaptable.api.columnApi.getFriendlyNameForColumnId(colId), ")"] }, colId))) })); }; export const RowSummarySectionSummary = () => { const adaptable = useAdaptable(); const { data: layout } = useOnePageAdaptableWizardContext(); return (_jsx(Box, { className: "twa:flex twa:flex-col", children: layout.RowSummaries?.length ? (layout.RowSummaries.map((rowSummary, index) => { return (_jsxs(Box, { className: "twa:mr-1 twa:mb-1", children: [rowSummary.Position, ":", ' ', Object.keys(rowSummary.ColumnsMap).map((colId) => { if (colId === 'Source' || colId === 'Uuid') return null; return (_jsxs(ColumnTag, { className: "twa:mr-1", children: [rowSummary.ColumnsMap[colId], "(", adaptable.api.columnApi.getFriendlyNameForColumnId(colId), ")"] }, colId)); })] }, index)); })) : (_jsx(Tag, { children: "No Row Summaries" })) })); }; const RowSummaryEditorForm = React.memo(({ rowSummary, onChange, availableScalarExpressions, }) => { 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 (_jsxs(Box, { className: "twa:flex twa:flex-col twa:gap-3 twa:h-full twa:overflow-hidden", children: [_jsxs(FormLayout, { children: [_jsx(FormRow, { label: "Position", children: _jsx(SingleSelect, { items: [ { label: 'Top', value: 'Top', }, { label: 'Bottom', value: 'Bottom', }, ], value: rowSummary.Position, onValueChange: (position) => { onChange({ ...rowSummary, Position: position, }); } }) }), _jsx(FormRow, { label: "", children: _jsx(CheckBox, { checked: rowSummary.IncludeOnlyFilteredRows ?? true, onChange: (IncludeOnlyFilteredRows) => { onChange({ ...rowSummary, IncludeOnlyFilteredRows, }); }, children: "Include Only Filtered Rows" }) })] }), _jsxs(Card, { shadow: false, className: "twa:flex-1 twa:min-h-0 twa:overflow-hidden twa:flex twa:flex-col", children: [_jsxs(Card.Title, { children: [_jsx(Box, { className: "twa:font-medium", children: "Column Aggregations" }), _jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:font-normal twa:max-w-[520px]", children: "Select columns and choose an aggregation function for each" })] }), _jsx(Card.Body, { className: "twa:flex-1 twa:min-h-0 twa:overflow-hidden twa:p-1", children: _jsx(ValueSelector, { showFilterInput: true, className: "twa:max-h-full twa:min-h-0", toggleSelectionOnRowClick: false, filter: columnFilter, toIdentifier: (column) => column.columnId, toLabel: (option) => option.friendlyName ?? option.columnId, options: columns, optionLayout: "label-beside-checkbox", toListLabel: (column) => { const label = column.friendlyName ?? column.columnId; const disabled = !(column.columnId in (rowSummary.ColumnsMap ?? {})); if (disabled) { return (_jsxs(Flex, { alignItems: "center", className: "twa:gap-2", children: [label, _jsx(ColumnGroupTag, { column: column }), _jsx(SingleSelect, { disabled: true, items: [{ label: 'Select Aggregation', value: null }] })] })); } const expressionOptions = buildRowSummaryExpressionOptions(column, availableScalarExpressions, layout); const expression = rowSummary.ColumnsMap[column.columnId]; return (_jsxs(Flex, { children: [_jsxs(Flex, { className: "twa:mr-2", alignItems: 'center', children: [label, _jsx(ColumnGroupTag, { column: column })] }), _jsx(SingleSelect, { value: expression, items: expressionOptions, onValueChange: (expression) => { onChange({ ...rowSummary, ColumnsMap: { ...rowSummary.ColumnsMap, [column.columnId]: expression, }, }); } })] })); }, value: Object.keys(rowSummary.ColumnsMap), onChange: (colIds) => { const newColumnsMap = {}; colIds.forEach((colId) => { const existing = rowSummary.ColumnsMap[colId]; if (existing) { newColumnsMap[colId] = existing; return; } const column = adaptable.api.columnApi.getColumnWithColumnId(colId); newColumnsMap[colId] = column ? getDefaultRowSummaryExpression(column, availableScalarExpressions, layout) : null; }); onChange({ ...rowSummary, ColumnsMap: newColumnsMap, }); } }) })] })] })); }); export const RowSummarySection = (props) => { const adaptable = useAdaptable(); const { data: layout } = useOnePageAdaptableWizardContext(); const rowSummaries = layout.RowSummaries ?? []; const availableScalarExpressions = React.useMemo(() => { const sytemExpressions = adaptable.api.internalApi .getQueryLanguageService() .getModuleExpressionFunctionsMap(LayoutModuleId).aggregatedScalarFunctions; return sytemExpressions; }, []); const { bindCard, hasExpandedCard, expandedFillsSpace, expandedId, setExpandedId } = useWizardCardAccordion(null); const handleAddRowSummary = () => { const newIndex = rowSummaries.length; props.onChange({ ...layout, RowSummaries: [ ...rowSummaries, { Position: 'Top', ColumnsMap: {}, IncludeOnlyFilteredRows: true, }, ], }); setExpandedId(rowSummaryCardId(newIndex)); }; const handleDeleteRowSummary = (index) => { const cardId = rowSummaryCardId(index); const newSummaries = [...rowSummaries]; newSummaries.splice(index, 1); props.onChange({ ...layout, RowSummaries: newSummaries, }); if (expandedId === cardId) { setExpandedId(null); return; } if (expandedId?.startsWith('row-summary-')) { const expandedIndex = Number(expandedId.replace('row-summary-', '')); if (!Number.isNaN(expandedIndex) && expandedIndex > index) { setExpandedId(rowSummaryCardId(expandedIndex - 1)); } } }; return (_jsxs(Box, { className: cn(getWizardAccordionSectionClassName(hasExpandedCard, expandedFillsSpace), 'twa:p-3'), children: [_jsx(Box, { className: "twa:text-xs twa:opacity-70 twa:mb-2 twa:max-w-[520px] twa:shrink-0", children: "Add summary rows at the top or bottom of the grid with aggregated column values" }), _jsx(Flex, { className: "twa:justify-end twa:mb-2 twa:shrink-0", children: _jsx(ButtonNew, { onClick: handleAddRowSummary, children: "Add Row Summary" }) }), _jsx(Flex, { flexDirection: "column", className: "twa:gap-3 twa:min-h-0 twa:flex-1 twa:overflow-y-auto", children: rowSummaries.map((rowSummary, index) => { const cardBinding = bindCard(rowSummaryCardId(index), { fillAvailable: true }); return (_jsx(CollapsibleWizardCard, { ...cardBinding, surface: "panel", "data-name": `row-summary-${index}`, title: `Row Summary ${index + 1}`, help: "Configure position, filters, and column aggregations for this summary row", collapsedHelp: false, compactSummary: _jsx(RowSummaryPositionTag, { position: rowSummary.Position }), headerVisual: !cardBinding.expanded ? (_jsx(RowSummaryPositionTag, { position: rowSummary.Position })) : undefined, headerActions: _jsxs(_Fragment, { children: [cardBinding.expanded ? (_jsx(SuspendToggleButton, { className: objectListActionButtonClassName('suspend'), onSuspend: () => { const newSummaries = [...rowSummaries]; newSummaries[index] = { ...rowSummary, IsSuspended: true }; props.onChange({ ...layout, RowSummaries: newSummaries }); }, onUnSuspend: () => { const newSummaries = [...rowSummaries]; newSummaries[index] = { ...rowSummary, IsSuspended: false }; props.onChange({ ...layout, RowSummaries: newSummaries }); }, suspendableObject: rowSummary })) : null, _jsx(SimpleButton, { icon: "delete", variant: "text", tooltip: "Delete row summary", className: objectListActionButtonClassName('delete'), onClick: () => handleDeleteRowSummary(index) })] }), summary: _jsx(RowSummaryCardSummary, { rowSummary: rowSummary }), className: cn('twa:overflow-hidden twa:flex twa:flex-col', cardBinding.expanded && 'twa:min-h-[500px]'), bodyClassName: " twa:overflow-hidden twa:flex twa:flex-col twa:pt-0!", children: _jsx(RowSummaryEditorForm, { rowSummary: rowSummary, availableScalarExpressions: availableScalarExpressions, onChange: (nextRowSummary) => { const newSummaries = [...rowSummaries]; newSummaries[index] = nextRowSummary; props.onChange({ ...layout, RowSummaries: newSummaries, }); } }) }, rowSummaryCardId(index))); }) })] })); };