@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
JavaScript
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,
});
} }));
})))));
};