UNPKG

@adaptabletools/adaptable

Version:

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

389 lines (388 loc) 17.3 kB
import { ApiBase } from '../Implementation/ApiBase'; import StringExtensions from '../../Utilities/Extensions/StringExtensions'; import FormatHelper from '../../Utilities/Helpers/FormatHelper'; import ObjectFactory from '../../Utilities/ObjectFactory'; import * as ModuleConstants from '../../Utilities/Constants/ModuleConstants'; import { errorOnce } from '../../agGrid/AdaptableLogger'; function getParentGroup(source) { if (!source) { return undefined; } let current = source; while (current.getParent()) { current = current.getParent(); if (!current) { return undefined; } const currentProvidedColumnGroup = current.getProvidedColumnGroup(); // for columns that don't belong to groups or belong to a group that doesn't have full depth // there are artificial parent groups created by ag-grid // so for the current column group, lets grab the leaf columns // and if the only leaf column is the source column, then we can continue // and do another iteration where we go up one level if (currentProvidedColumnGroup) { const leafColumns = current.getLeafColumns(); if (leafColumns.length === 1 && leafColumns[0] === source) { continue; } // otherwise, we have found the parent group corresponding to the source column return currentProvidedColumnGroup; } } return undefined; } export class FormatColumnInternalApi extends ApiBase { /** * Retrieves all Format Columns in Adaptable State with the `Style` property set * @returns format columns */ getAllFormatColumnWithStyle() { return this.getFormatColumnApi() .getFormatColumns() .filter((fc) => fc.Style); } /** * Retrieves all Format Columns in Adaptable State with the `Style` or the `CellAlignment` property set * @returns format columns */ getAllFormatColumnWithStyleAndCellAlignment() { return this.getFormatColumnApi() .getFormatColumns() .filter((fc) => fc.Style || fc.CellAlignment); } /** * Retrieves all Format Columns in Adaptable State with `DisplayFormat` property set * @returns format columns */ getAllFormatColumnWithDisplayFormat() { return this.getFormatColumnApi() .getFormatColumns() .filter((fc) => fc.DisplayFormat); } /** * Retrieves all Format Columns in Adaptable State with `CellAlignment` property set * @returns format columns */ getAllFormatColumnWithCellAlignment() { return this.getFormatColumnApi() .getFormatColumns() .filter((fc) => fc.CellAlignment); } /** * Get all FormatColumns which are defined for this column and have a custom AdaptableStyle * @param column * @param config * @returns list of FormatColumn */ getFormatColumnsWithStyleForColumn(column, config) { const formatColumns = this.getAllFormatColumnWithStyleAndCellAlignment() .filter((formatColumn) => { // FormatColumn default target is 'cell', so if no target is specified, we assume 'cell' const fcTarget = formatColumn.Target ?? 'cell'; return fcTarget === config.target; }) .filter((formatColumn) => config?.includeSuspended || !formatColumn.IsSuspended); return this.getFormatColumnWithColumnInScope(formatColumns, column); } /** * Gets Format Column if any for given Column which includes Style element with ClassName * @param column The Column for which to retrieve the Format Column * @returns format columns */ getFormatColumnWithStyleClassNameForColumn(column, config) { return this.getFormatColumnsWithStyleForColumn(column, config).filter((formatColumn) => StringExtensions.IsNotNullOrEmpty(formatColumn?.Style?.ClassName)); } getFormatColumnWithColumnInScope(formatColumns, column) { return this.getFormatColumnInColumnScope(formatColumns).filter((scopedFormatColumn) => this.getColumnScopeApi().isColumnInScope(column, scopedFormatColumn.Scope)); } // TODO is this really needed, I don't think it achieves anything getFormatColumnInColumnScope(formatColumns) { // we need to maintain the format columns order, therefore we will extract all 3 scope types in a single iteration return formatColumns.filter((fc) => { return ( // this.getFormatColumnsWithColumnScope(formatColumns) (this.getColumnScopeApi().scopeHasColumns(fc.Scope) || // this.getFormatColumnsWithDataTypeScope(formatColumns) this.getColumnScopeApi().scopeHasDataType(fc.Scope) || // this.getFormatColumnsWithAllScope(formatColumns) this.getColumnScopeApi().scopeIsAll(fc.Scope) || this.getColumnScopeApi().scopeHasColumnType(fc.Scope)) ); }); } /** * Get all FormatColumns which are defined for this column and have a custom DisplayFormat * @param column * @param config * @returns list of FormatColumn */ getFormatColumnsWithDisplayFormatForColumn(column, config) { const formatColumns = this.getAllFormatColumnWithDisplayFormat() .filter((formatColumn) => { // FormatColumn default target is 'cell', so if no target is specified, we assume 'cell' const fcTarget = formatColumn.Target ?? 'cell'; return fcTarget === config.target; }) .filter((formatColumn) => config?.includeSuspended || !formatColumn.IsSuspended); return this.getFormatColumnWithColumnInScope(formatColumns, column); } /** * Format value according to format options. * * @param customDisplayFormatterContext context that includes value to format * @param options formatter options */ getNumberFormattedValue(value, node, column, options) { const preparedValue = this.applyCustomFormatters(value, node, column, options); return FormatHelper.NumberFormatter(preparedValue, options, node, column, this.getAdaptableApi()); } /** * Format value according to format options. * * @param value context that includes value to format * @param options formatter options */ getStringFormattedValue(value, node, column, options) { const preparedValue = this.applyCustomFormatters(value, node, column, options); return FormatHelper.StringFormatter(preparedValue, options, node, column, this.getAdaptableApi()); } /** * Format value according to format options. * * @param customDisplayFormatterContext context that includes value to format * @param options formatter options */ getDateFormattedValue(value, node, abColumn, options) { const preparedValue = this.applyCustomFormatters(value, node, abColumn, options); const dateFormatterOptions = options; // only use DateFormatter if we have a pattern return dateFormatterOptions.Pattern ? FormatHelper.DateFormatter(preparedValue, options) : preparedValue; } applyCustomFormatters(value, node, abColumn, options) { const columnCustomFormatters = options?.CustomDisplayFormats ?? []; if (!columnCustomFormatters?.length) { return value; } const customFormattersFromOptions = this.getFormatColumnOptions()?.customDisplayFormatters ?? []; // formatters are applied in the order they are defined in the options const customFormatters = customFormattersFromOptions.filter((customFormatterOption) => columnCustomFormatters.includes(customFormatterOption.id)); const customDisplayFormatterContext = ObjectFactory.CreateCustomDisplayFormatterContext(value, node, abColumn, this.getAdaptableApi()); return customFormatters.reduce((context, formatter) => { if (formatter && formatter.handler) { return formatter.handler(context); } return context.cellValue; }, customDisplayFormatterContext); } /** * Returns all Predicates appropriate for the given Scope * @param scope Scope to check */ getFormatColumnDefsForScope(scope) { return this.getAdaptableApi() .predicateApi.internalApi.getFormatColumnPredicateDefs(scope) .filter((predicateDef) => this.getColumnScopeApi().isScopeInScope(scope, predicateDef.columnScope)); } formatColumnWithColumnGroupScopeShouldRender(formatColumn, column) { if (!formatColumn.ColumnGroupScope) { return true; } const agGridApi = this.getAdaptableApi().agGridApi; const agGridColumn = agGridApi.isPivotMode() ? agGridApi.getPivotResultColumns().find((col) => col.getColId() === column.columnId) : agGridApi.getColumn(column.columnId); if (!agGridColumn) { return false; } const columnGroupParentForCurrentColumn = getParentGroup(agGridColumn); if (!columnGroupParentForCurrentColumn) { return false; } const columnGroupScope = columnGroupParentForCurrentColumn.isExpanded() ? 'Expanded' : 'Collapsed'; const columnGroupLeafColumns = columnGroupParentForCurrentColumn.getLeafColumns(); const columnIsTopLevel = columnGroupParentForCurrentColumn.getLevel() === 0 && columnGroupLeafColumns.length === 1 && columnGroupLeafColumns[0] === agGridColumn; if (columnIsTopLevel) { // for top-level columns don't apply formatColumns that have an explicit ColumnGroupScope defined return false; } if (formatColumn.ColumnGroupScope === 'Both') { return true; } return formatColumn.ColumnGroupScope === columnGroupScope; } /** * Checks if format column is relevant for a given cell (intersection of given AdaptableColumn and RowNode) * * @param formatColumn * @param column * @param params */ formatColumnShouldRenderInCell(formatColumn, column, rowNode, cellValue) { // suspended is important to be first if (formatColumn.IsSuspended) { return false; } const isSummaryNode = this.getGridApi().isSummaryNode(rowNode); const isGroupedRowNode = this.getGridApi().isGroupRowNode(rowNode); const isGrandTotalRowNode = this.getGridApi().isGrandTotalRowNode(rowNode); if (isGrandTotalRowNode) { if (formatColumn.RowScope?.ExcludeTotalRows) { return false; } } else if (isSummaryNode) { if (formatColumn.RowScope?.ExcludeSummaryRows) { return false; } } else if (isGroupedRowNode) { if (formatColumn.RowScope?.ExcludeGroupRows) { return false; } } else { if (formatColumn.RowScope?.ExcludeDataRows) { return false; } } if (formatColumn.ColumnGroupScope && !this.formatColumnWithColumnGroupScopeShouldRender(formatColumn, column)) { return false; } if (!formatColumn.Rule) { return true; } // first run the predicate if (formatColumn.Rule.Predicates && formatColumn.Rule?.Predicates?.length) { const predicateDefHandlerContext = { value: cellValue, oldValue: null, displayValue: cellValue, node: rowNode, column: column, ...this.getAdaptableInternalApi().buildBaseContext(), }; return this.evaluatePredicate(formatColumn, predicateDefHandlerContext); } // then run the Expression else if (formatColumn.Rule.BooleanExpression) { return this.evaluateExpression(formatColumn, rowNode); } // nothing has passed then return false return false; } /** * Checks if format column is relevant for a given Column Header * * @param formatColumn * @param column */ formatColumnShouldRenderInHeader(formatColumn, column) { if (formatColumn.IsSuspended) { return false; } if (formatColumn.ColumnGroupScope && !this.getFormatColumnApi().internalApi.formatColumnWithColumnGroupScopeShouldRender(formatColumn, column)) { return false; } return true; } evaluatePredicate(formatColumn, predicateDefHandlerContext) { return this.getPredicateApi().handleColumnPredicates(formatColumn.Rule?.Predicates, predicateDefHandlerContext, false); } evaluateExpression(formatColumn, node) { const isValidExpression = this.getExpressionApi().isValidBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, `Invalid format column rule '${formatColumn.Rule.BooleanExpression}'`); try { return (isValidExpression && this.getAdaptableApi() .internalApi.getQueryLanguageService() .evaluateBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, node)); } catch (error) { errorOnce(error.message); return false; } } /** * Extract from the given FormatColumns only the ones which are relevant for a given cell (intersection of given AdaptableColumn and RowNode) * * @param formatColumns * @param column * @param params */ getFormatColumnsRelevantForColumn(formatColumns, column, params) { return formatColumns.filter((formatColumn) => this.formatColumnShouldRenderInCell(formatColumn, column, params.node, params.value)); } /** * Extract from the given FormatColumns the one which is the most relevant for a given cell (intersection of given AdaptableColumn and RowNode) * * @param formatColumns * @param column * @param params */ getMostRelevantFormatColumnForColumn(formatColumns, column, params) { return formatColumns.find((formatColumn) => this.formatColumnShouldRenderInCell(formatColumn, column, params.node, params.value)); } /** * Retrieves all Format Columns which have an Expression * @returns Format Columns with Expression */ getFormatColumnsWithExpression() { return this.getFormatColumnApi() .getFormatColumns() .filter((fc) => !!fc.Rule?.BooleanExpression); } getFormatColumnsDependentOnColumns(columnSet) { return this.getFormatColumnApi() .getFormatColumns() .filter((fc) => fc.Rule?.Predicates?.some((p) => columnSet.has(p.ColumnId))); } /** * Retrieves the columns that need rerendering based on format column predicates. */ getFormatColumnColumnsDependentOnColumnChange(column) { const impactedColumnIds = new Set(); impactedColumnIds.add(column.columnId); this.getCalculatedColumnApi() .internalApi.getCalculatedColumnsDependentOnColumn(column) .forEach((calculatedColumnId) => { impactedColumnIds.add(calculatedColumnId); }); const columnsThatNeedRefresh = new Set(); this.getFormatColumnsDependentOnColumns(impactedColumnIds).forEach((formatColumn) => { this.getAdaptableApi() .columnScopeApi.getColumnsInScope(formatColumn.Scope) .forEach((col) => { columnsThatNeedRefresh.add(col.columnId); }); }); return [...columnsThatNeedRefresh]; } formatColumnHeaderName(headerName, params) { // currently we only format the header name for normal columns (NOT column groups) if (!params.column) { return headerName; } const abColumn = this.getColumnApi().getColumnWithColumnId(params.column.getColId()); const activeFormatColumnsWithDisplayFormat = this.getFormatColumnApi().internalApi.getFormatColumnsWithDisplayFormatForColumn(abColumn, { target: 'columnHeader', }); if (!activeFormatColumnsWithDisplayFormat.length) { return headerName; } const mostRelevantFormatColumn = activeFormatColumnsWithDisplayFormat.find((formatColumn) => this.formatColumnShouldRenderInHeader(formatColumn, abColumn)); if (!mostRelevantFormatColumn) { return headerName; } if (mostRelevantFormatColumn.DisplayFormat?.Formatter !== 'StringFormatter') { // headers are always strings, so we can only use StringFormatter return headerName; } const formattedHeaderName = this.getFormatColumnApi().internalApi.getStringFormattedValue(headerName, null, abColumn, mostRelevantFormatColumn.DisplayFormat.Options); return formattedHeaderName; } }