UNPKG

@adaptabletools/adaptable

Version:

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

998 lines (997 loc) 68.7 kB
import { kebabCase } from '../Utilities/Extensions/StringExtensions'; import { resolveDisplayFormat } from '../AdaptableState/Common/AdaptableFormatPresets'; import { merge } from '../Utilities/Extensions/ObjectExtensions'; import { resolveSparklineOptionsForRender } from '../Utilities/Helpers/StyledColumns/SparklineStyleHelper'; import { hasBarStyleCellTextConfigured } from '../Utilities/Helpers/StyledColumns/BarStylesHelper'; import { convertAdaptableStyleToCSS, getAutoContrastTextColor, getVariableColor, hasCellBoxStyle, hasCellFontStyle, normalizeStyleForAgGrid, } from '../Utilities/Helpers/StyleHelper'; import StringExtensions from '../Utilities/Extensions/StringExtensions'; import { ACTION_COLUMN_TYPE, CALCULATED_COLUMN_TYPE, FDC3_COLUMN_TYPE, FREE_TEXT_COLUMN_TYPE, } from '../AdaptableState/Common/AdaptableColumn'; import tinycolor from 'tinycolor2'; import UIHelper from '../View/UIHelper'; import { getPercentBarRendererForColumn } from './cellRenderers/PercentBarRenderer'; import { getBadgeRendererForColumn } from './cellRenderers/BadgeRenderer'; import { getBulletChartRendererForColumn } from './cellRenderers/BulletChartRenderer'; import { getRangeBarRendererForColumn } from './cellRenderers/RangeBarRenderer'; import { getRatingRendererForColumn } from './cellRenderers/RatingRenderer'; import { shouldRenderStyledColumnOnRow } from '../Utilities/Helpers/StyledColumns/StyledColumnHelper'; import { findIconStyleMappingForValue, getIconRendererForColumn, } from './cellRenderers/IconRenderer'; import { clamp } from '../Utilities/Extensions/NumberExtensions'; import { getRuleAlertProperties } from '../Utilities/Helpers/Scheduling/ScheduledAlertHelper'; import { isNegativeZeroCentredBand, expandGradientCellRanges, gradientStyleRangeLookupValueType, DEFAULT_GRADIENT_MIN_ALPHA, DEFAULT_GRADIENT_MAX_ALPHA, } from '../Utilities/Helpers/StyledColumns/GradientStyleHelper'; import { AdaptableNumberEditor, AdaptableReactNumberEditor } from './editors/AdaptableNumberEditor'; import { AdaptableDateEditor, AdaptableReactDateEditor } from './editors/AdaptableDateEditor'; import { AgGridExportAdapter } from './AgGridExportAdapter'; import { AdaptableHelper } from '../Utilities/Helpers/AdaptableHelper'; import { isProvidedByAdaptable } from '../Utilities/adaptableOverrideCheck'; import { AdaptableFilterHandler } from './AdaptableFilterHandler'; import { AgGridFilterAdapterFactory } from './AgGridFilterAdapter'; import { AgGridFloatingFilterAdapterFactory } from './AgGridFloatingFilterAdapter'; import { errorOnce } from './AdaptableLogger'; import { isUnsupportedColumnDataTypeForIconStyle } from '../Utilities/Helpers/StyledColumns/IconStyleHelper'; import { isWeightedAverageAggFuncName, } from '../AdaptableState/Common/AggregationColumns'; const FONT_CSS_KEYS = [ 'color', 'fontWeight', 'fontStyle', 'fontSize', 'textDecoration', 'textAlign', ]; const CELL_BOX_CSS_KEYS = [ 'backgroundColor', 'borderColor', 'borderRadius', ]; const stripCssKeys = (style, keys) => { const out = { ...style }; keys.forEach((k) => delete out[k]); return out; }; const stripFontCssKeys = (style) => stripCssKeys(style, FONT_CSS_KEYS); const stripCellBoxCssKeys = (style) => stripCssKeys(style, CELL_BOX_CSS_KEYS); export function getEditorForColumnDataType(columnDataType, variant) { if (columnDataType === 'number') { return variant === 'react' ? AdaptableReactNumberEditor : AdaptableNumberEditor; } if (columnDataType === 'date') { return variant === 'react' ? AdaptableReactDateEditor : AdaptableDateEditor; } } export class AgGridColumnAdapter { adaptableInstance; colDefPropertyCache; constructor(adaptableInstance) { this.adaptableInstance = adaptableInstance; this.colDefPropertyCache = new Map(); } getVariant() { return this.adaptableInstance.variant; } destroy() { this.adaptableInstance = null; this.colDefPropertyCache.clear(); this.colDefPropertyCache = null; } get adaptableApi() { return this.adaptableInstance.api; } get adaptableOptions() { return this.adaptableInstance.adaptableOptions; } get agGridApi() { return this.adaptableApi.agGridApi; } setColDefProperty(col, propertyName, propertyGetter) { const colId = col.getColId(); const colDef = col.getColDef(); const colSetupInfo = { col, colDef, colId, }; const userKey = `user.${colId}.${propertyName}`; const adaptableKey = `adaptable.${colId}.${propertyName}`; const value = colDef[propertyName]; const isUserDefined = value !== this.colDefPropertyCache.get(adaptableKey); if (isUserDefined) { this.colDefPropertyCache.set(userKey, value); } const userValue = this.colDefPropertyCache.get(userKey); const adaptableValue = propertyGetter(userValue); if (adaptableValue != null) { this.colDefPropertyCache.set(adaptableKey, adaptableValue); } let theValue = adaptableValue ?? userValue; this.adaptableInstance.forPlugins((plugin) => { if (plugin.interceptSetupColumnProperty) { theValue = plugin.interceptSetupColumnProperty(colSetupInfo, propertyName, theValue, this.adaptableApi); } }); if (propertyName === 'aggFunc') { if (colDef[propertyName] !== (theValue ?? null)) { this.agGridApi?.setColumnAggFunc(colId, theValue ?? null); } } if (theValue === undefined && colDef[propertyName] === undefined) { return; } colDef[propertyName] = theValue; } getUserColDefProperty(columnId, propertyName) { const userKey = `user.${columnId}.${propertyName}`; return this.colDefPropertyCache.get(userKey); } shouldSkipColumn(colId) { return (colId.startsWith('ag-Grid-') && !this.adaptableApi.columnApi.isAutoRowGroupColumn(colId) && !this.adaptableApi.columnApi.isSelectionColumn(colId)); } getAllAgGridColumns() { const pivotMode = this.adaptableInstance.isInPivotMode(); let cols = pivotMode ? this.agGridApi.getColumns() : this.agGridApi.getAllGridColumns(); if (pivotMode) { const pivotResultColumns = this.agGridApi.getPivotResultColumns() || []; const autoGroupColumns = this.agGridApi .getAllGridColumns() .filter((column) => this.adaptableApi.columnApi.isAutoRowGroupColumn(column.getColId())); cols = [...autoGroupColumns, ...cols, ...pivotResultColumns]; } return cols; } setupColumns(specificColumnIds) { let cols = this.getAllAgGridColumns(); if (specificColumnIds?.length) { cols = cols.filter((col) => specificColumnIds.includes(col.getColId())); } cols.forEach((col) => { const colDef = col.getColDef(); const colId = col.getColId(); if (this.shouldSkipColumn(colId)) { return; } const abColumn = this.adaptableApi.columnApi.getColumnWithColumnId(colId); const colSetupInfo = { col, colDef, colId, abColumn, }; const isAutoRowGroupColumn = this.adaptableApi.columnApi.isAutoRowGroupColumn(colId); this.setupColumnCellRenderer(colSetupInfo); this.setupColumnCellStyle(colSetupInfo); this.setupColumnCellClass(colSetupInfo); this.setupColumnHeaderStyle(colSetupInfo); this.setupColumnHeaderClass(colSetupInfo); this.setupColumnValueGetter(colSetupInfo); this.setupColumnTooltipValueGetter(colSetupInfo); if (!isAutoRowGroupColumn) { this.setupColumnFilter(colSetupInfo); this.setupColumnFloatingFilter(colSetupInfo); } this.setupColumnValueFormatter(colSetupInfo); this.setupColumnEditable(colSetupInfo); this.setupColumnValueSetter(colSetupInfo); this.setupColumnComparator(colSetupInfo); this.setupColumnGetFindText(colSetupInfo); this.setupColumnCellEditor(colSetupInfo); this.setupColumnHeader(colSetupInfo); this.setupColumnQuickFilterText(colSetupInfo); this.setupColumnAllowedAggFuncs(colSetupInfo); this.setupColumnType(colSetupInfo); this.setupColumnCellDataType(colSetupInfo); }); } setupColumnValueGetter({ col }) { this.setColDefProperty(col, 'valueGetter', (userValue) => { return userValue; }); } setupColumnCellClass({ col, colId, abColumn }) { this.setColDefProperty(col, 'cellClass', (userCellClass) => { const formatColumns = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnWithStyleClassNameForColumn(abColumn, { target: 'cell' }); const cellClass = (params) => { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, abColumn.columnId); if (!gridCell.column) { return null; } const isExcelExport = this.adaptableApi.exportApi.internalApi.isExcelExportInProgress(); const isVisualDataExport = this.adaptableApi.exportApi.internalApi.isVisualDataExportInProgress(); if (isExcelExport || isVisualDataExport) { const excelStyleClasses = []; const userDefinedCellClass = typeof userCellClass === 'function' ? userCellClass(params) : userCellClass; if (userDefinedCellClass) { if (Array.isArray(userDefinedCellClass)) { excelStyleClasses.push(...userDefinedCellClass.filter(Boolean)); } else { excelStyleClasses.push(userDefinedCellClass); } } if (isVisualDataExport) { const cellClassKey = AgGridExportAdapter.getExcelClassNameForCell(colId, gridCell.primaryKeyValue, userDefinedCellClass); const customCellClass = this.adaptableInstance.agGridExportAdapter.getExcelStyleIdForClassKey(cellClassKey); if (customCellClass) { excelStyleClasses.push(customCellClass); } } return excelStyleClasses.length ? excelStyleClasses : null; } const isQuickSearchActive = this.isQuickSearchActive(gridCell); const isServerSideRowModel = this.adaptableApi.gridApi.getAgGridRowModelType() === 'serverSide'; const editableClassName = this.getEditableCellClass(gridCell, params); const readonlyClassName = this.getReadonlyCellClass(gridCell, params); const editedClassName = this.getEditedCellClass(gridCell, params); const highlightAlertClassName = this.getAlertCellClass(gridCell, params); const flashingClassName = this.getFlashingCellClass(gridCell, params); const styledColumn = this.adaptableApi.styledColumnApi.getStyledColumnForColumnId(colId); const hasStyledColumn = !!styledColumn && !styledColumn.IsSuspended; const noteClassName = this.getNoteCellClassName(gridCell, params); const commentsClassName = this.getCommentCellClassName(gridCell, params); const returnValue = [ typeof userCellClass === 'function' ? userCellClass(params) : userCellClass, !hasStyledColumn && formatColumns.length ? this.getFormatColumnCellClass(formatColumns, abColumn, params) : null, isQuickSearchActive && !isServerSideRowModel ? 'ab-QuickSearchFind' : null, editableClassName, readonlyClassName, editedClassName, highlightAlertClassName, flashingClassName, noteClassName, commentsClassName, ] .flat() .filter((x) => !!x); const result = returnValue.length ? returnValue : undefined; return result; }; return cellClass; }); } setupColumnHeaderClass({ col, colId, abColumn }) { this.setColDefProperty(col, 'headerClass', (userHeaderClass) => { const headerClass = (params) => { let baseHeaderClass = []; const userDefinedHeaderClass = typeof userHeaderClass === 'function' ? userHeaderClass(params) : userHeaderClass; if (userDefinedHeaderClass) { baseHeaderClass = typeof userDefinedHeaderClass === 'string' ? [userDefinedHeaderClass] : Array.isArray(userDefinedHeaderClass) ? userDefinedHeaderClass : [userDefinedHeaderClass]; } if (params.floatingFilter) { return baseHeaderClass; } if (params.columnGroup) { return baseHeaderClass; } const isExcelExport = this.adaptableApi.exportApi.internalApi.isExcelExportInProgress(); const isVisualDataExport = this.adaptableApi.exportApi.internalApi.isVisualDataExportInProgress(); if (isExcelExport || isVisualDataExport) { const excelStyleHeaderClasses = []; if (userDefinedHeaderClass) { if (Array.isArray(userDefinedHeaderClass)) { excelStyleHeaderClasses.push(...userDefinedHeaderClass.filter(Boolean)); } else { excelStyleHeaderClasses.push(userDefinedHeaderClass); } } if (isVisualDataExport) { const headerClassKey = AgGridExportAdapter.getExcelClassNameForHeader(colId, userDefinedHeaderClass); const customHeaderClass = this.adaptableInstance.agGridExportAdapter.getExcelStyleIdForClassKey(headerClassKey); if (customHeaderClass) { excelStyleHeaderClasses.push(customHeaderClass); } } return excelStyleHeaderClasses.length ? excelStyleHeaderClasses : null; } const columnId = params.column.getColId(); const adaptableColumn = this.adaptableApi.columnApi.getColumnWithColumnId(columnId); if (!adaptableColumn) { return baseHeaderClass; } const target = 'columnHeader'; const formatColumnWithTextAlignment = this.getRelevantFormatColumnHeaderStyles(abColumn) .find((fc) => fc.Style?.Alignment != undefined); if (formatColumnWithTextAlignment) { const alignment = formatColumnWithTextAlignment.Style.Alignment.toLowerCase(); baseHeaderClass = [...baseHeaderClass, `ab-header__align-${alignment}`]; } const formatColumns = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnWithStyleClassNameForColumn(abColumn, { target, }); if (!formatColumns.length) { return baseHeaderClass; } const formatColumnClasses = formatColumns .map((formatColumn) => { if (formatColumn.Style?.ClassName && this.adaptableApi.formatColumnApi.internalApi.formatColumnShouldRenderInHeader(formatColumn, abColumn)) { return formatColumn.Style?.ClassName; } }) .filter((x) => !!x); return [...baseHeaderClass, ...formatColumnClasses]; }; return headerClass; }); } quickSearchStyleToDynamicCssVars(style) { if (!style) { return undefined; } return Object.entries(style).reduce((acc, [key, value]) => { acc[`--ab-dynamic-${kebabCase(key)}`] = value; return acc; }, {}); } setupColumnCellStyle({ col }) { this.setColDefProperty(col, 'cellStyle', (userCellStyle) => { const cellStyle = (params) => { const quickSearchStyle = this.getQuickSearchCellStyle(); const quickSearchTextMatchStyle = this.getQuickSearchTextMatchStyle(); const quickSearchCurrentTextMatchStyle = this.getQuickSearchCurrentTextMatchStyle(); const textMatchStyle = this.quickSearchStyleToDynamicCssVars(quickSearchTextMatchStyle); const currentTextMatchStyle = this.quickSearchStyleToDynamicCssVars(quickSearchCurrentTextMatchStyle); const hasQuickSearchStyle = quickSearchStyle != undefined || quickSearchTextMatchStyle != undefined || quickSearchCurrentTextMatchStyle != undefined; const columnId = params.column.getColId(); const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, columnId); if (!gridCell || !gridCell.column) { return {}; } const isQuickSearchActive = hasQuickSearchStyle && this.isQuickSearchActive(gridCell); const isQuickSearchAvailable = this.adaptableApi.internalApi .getModuleService() .isAdapTableModulePresent('QuickSearch'); const isCurrentMatch = isQuickSearchAvailable && this.adaptableApi.agGridApi.findGetActiveMatch()?.node === params.node; const textStyleToApply = isCurrentMatch ? { ...textMatchStyle, ...currentTextMatchStyle } : textMatchStyle; let baseStyles = {}; if (this.adaptableApi.columnApi.isPivotResultColumn(columnId)) { const baseColumn = params.column.getColDef()?.pivotValueColumn; if (baseColumn) { const baseColDefCellStyle = baseColumn?.getColDef()?.cellStyle; const baseColParams = { ...params, column: baseColumn, __pivotResultColumn: params.column, }; baseStyles = typeof baseColDefCellStyle === 'function' ? baseColDefCellStyle(baseColParams) : baseColDefCellStyle; } } else { baseStyles = typeof userCellStyle === 'function' ? userCellStyle(params) : userCellStyle; } const result = { ...baseStyles, ...this.getReadOnlyCellStyle(gridCell, params), ...this.getEditableCellStyle(gridCell, params), ...this.getEditedCellStyle(gridCell, params), ...this.getFormatColumnAndStyledColumnCellStyle(gridCell.column, params), ...(isQuickSearchActive ? quickSearchStyle : {}), ...(isQuickSearchActive && textStyleToApply ? textStyleToApply : {}), ...this.getAlertCellStyle(gridCell, params), ...this.getFlashingCellStyle(gridCell, params), ...this.getCellHighlightStyle(gridCell, params), ...this.getColumnHighlightStyle(gridCell, params), }; return normalizeStyleForAgGrid(result); }; return cellStyle; }); } setupColumnHeaderStyle({ col }) { this.setColDefProperty(col, 'headerStyle', (userHeaderStyle) => { const headerStyleFunc = (params) => { let baseStyles = {}; baseStyles = typeof userHeaderStyle === 'function' ? userHeaderStyle(params) : userHeaderStyle; if (params.floatingFilter) { return baseStyles; } const columnId = params.column.getColId(); const adaptableColumn = this.adaptableApi.columnApi.getColumnWithColumnId(columnId); if (!adaptableColumn) { return baseStyles; } const result = { ...baseStyles, ...this.getFormatColumnHeaderStyle(adaptableColumn, params), }; if (result['borderColor']) { result['borderWidth'] = `1px`; result['borderStyle'] = 'solid'; } return normalizeStyleForAgGrid(result); }; return headerStyleFunc; }); } setupColumnCellEditor({ colId, col, colDef, abColumn }) { const shouldShowSelectCellEditor = this.adaptableApi.userInterfaceApi.internalApi.shouldShowSelectCellEditor(abColumn); const hasRichSelectCellEditor = this.adaptableApi.internalApi .getAgGridModulesAdapter() .isAgGridModuleRegistered('RichSelectModule'); this.setColDefProperty(col, 'cellEditor', () => { if (shouldShowSelectCellEditor) { if (hasRichSelectCellEditor) { return 'agRichSelectCellEditor'; } else { this.adaptableApi.logWarn(`Cannot show Select Editor as missing required AG Grid module: RichSelect`); return colDef.cellEditor; } } else { if (colDef.cellEditor) { return colDef.cellEditor; } const cellDataTypeEditor = getEditorForColumnDataType(abColumn.dataType, this.getVariant()); return cellDataTypeEditor; } }); this.setColDefProperty(col, 'cellEditorParams', (params) => { if (shouldShowSelectCellEditor) { return (params) => { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params?.node, colId); const options = this.adaptableApi.gridApi.internalApi.getDistinctEditDisplayValuesForColumn({ columnId: colId, gridCell, currentSearchValue: '', }); const valueToLabelMap = new Map(); const values = options.then((options) => options.map((option) => { valueToLabelMap.set(option.value, option.label); return option.value; })); const fetchSelectItems = (_params) => { return values; }; return { values: fetchSelectItems, formatValue: (value) => valueToLabelMap.get(value) ?? value, }; }; } }); } setupColumnCellRenderer({ col, colId, abColumn }) { this.setColDefProperty(col, 'cellRenderer', () => { const styledColumn = this.adaptableApi.styledColumnApi.getStyledColumnForColumnId(abColumn.columnId); if (styledColumn && !styledColumn.IsSuspended) { if (styledColumn.PercentBarStyle) { return getPercentBarRendererForColumn(styledColumn, abColumn, this.adaptableApi); } if (styledColumn.BulletChartStyle) { return getBulletChartRendererForColumn(styledColumn, abColumn, this.adaptableApi); } if (styledColumn.RangeBarStyle) { return getRangeBarRendererForColumn(styledColumn, abColumn, this.adaptableApi); } if (styledColumn.RatingStyle) { return getRatingRendererForColumn(styledColumn, abColumn, this.adaptableApi); } if (styledColumn.IconStyle) { return getIconRendererForColumn(styledColumn, abColumn, this.adaptableApi); } if (styledColumn.BadgeStyle) { return getBadgeRendererForColumn(styledColumn, abColumn, this.adaptableApi); } if (styledColumn.SparklineStyle) { return 'agSparklineCellRenderer'; } } }); this.setColDefProperty(col, 'cellRendererParams', (userDefined) => { const styledColumn = this.adaptableApi.styledColumnApi.getStyledColumnForColumnId(abColumn.columnId); if (styledColumn && !styledColumn.IsSuspended) { if (styledColumn.SparklineStyle) { const sanitizedSparklineOptions = AdaptableHelper.removeAdaptableObjectPrimitives(styledColumn.SparklineStyle.options); const sparklineOptions = resolveSparklineOptionsForRender(merge({}, userDefined?.sparklineOptions, sanitizedSparklineOptions)); return { ...userDefined, sparklineOptions, }; } } }); } setupColumnTooltipValueGetter({ col, colId, abColumn }) { let hasTooptip = false; this.setColDefProperty(col, 'tooltipValueGetter', () => { const styledColumn = this.adaptableApi.styledColumnApi.getStyledColumnForColumnId(colId); if (!styledColumn || styledColumn.IsSuspended) { return undefined; } if (styledColumn.IconStyle && !isUnsupportedColumnDataTypeForIconStyle(abColumn.dataType)) { const iconTokens = styledColumn.IconStyle.ToolTipText ?? []; if (iconTokens.length === 0) { return undefined; } hasTooptip = true; return (params) => { const parts = []; if (iconTokens.includes('CellValue')) { parts.push(String(params.value ?? '')); } if (iconTokens.includes('IconDescription')) { const matched = findIconStyleMappingForValue(styledColumn.IconStyle, params.value); if (matched?.Description) { parts.push(matched.Description); } } const trimmed = parts.filter(Boolean).join(' · ').trim(); return trimmed ? trimmed : String(params.value ?? ''); }; } if (styledColumn.RatingStyle) { const ratingToolTip = styledColumn.RatingStyle.ToolTipText ?? []; if (ratingToolTip.length === 0) { return undefined; } hasTooptip = true; const ratingMax = styledColumn.RatingStyle.Max ?? 5; return (params) => { let returnValue = ''; if (ratingToolTip.includes('CellValue')) { returnValue = String(params.value ?? ''); } if (ratingToolTip.includes('PercentageValue') && ratingMax > 0) { const n = Number(params.value); if (!isNaN(n)) { const clamped = clamp(n, 0, ratingMax); const percentageValue = (clamped / ratingMax) * 100; const pct = `${percentageValue.toFixed(0)}%`; returnValue = returnValue ? `${returnValue} (${pct})` : pct; } } const trimmed = returnValue.trim(); return trimmed ? trimmed : String(params.value ?? ''); }; } const tooltipStyle = styledColumn.PercentBarStyle ?? styledColumn.BulletChartStyle ?? styledColumn.GradientStyle ?? styledColumn.RangeBarStyle; const textOptions = tooltipStyle?.ToolTipText; if (!tooltipStyle || !textOptions || textOptions.length === 0) { return undefined; } hasTooptip = true; return (params) => { const isRangeBar = tooltipStyle === styledColumn.RangeBarStyle; let min; let max; if (isRangeBar) { const rangeStyle = styledColumn.RangeBarStyle; min = this.resolveRangeBarBound(rangeStyle.Min, abColumn, params.node); max = this.resolveRangeBarBound(rangeStyle.Max, abColumn, params.node); } else { min = this.adaptableApi.styledColumnApi.internalApi.getNumericStyleMinValue(styledColumn, abColumn, params.node, params.value); max = this.adaptableApi.styledColumnApi.internalApi.getNumericStyleMaxValue(styledColumn, abColumn, params.node, params.value); } let returnValue = ''; if (textOptions.includes('CellValue')) { returnValue = String(params.value ?? ''); } if (textOptions.includes('PercentageValue') && min != undefined && max != undefined) { let percentageValue; if (styledColumn.PercentBarStyle?.ColumnComparison) { const absMax = Math.abs(max); percentageValue = absMax === 0 ? 0 : (Number(params.value) / absMax) * 100; } else { const scaleSpan = max - min; const clamped = clamp(Number(params.value), min, max); percentageValue = scaleSpan === 0 ? 0 : ((clamped - min) / scaleSpan) * 100; } returnValue += ` (${percentageValue.toFixed(0)}%)`; } const trimmed = returnValue.trim(); return trimmed ? trimmed : String(params.value ?? ''); }; }); } resolveRangeBarBound(bound, abColumn, rowNode) { if (bound == undefined) { return undefined; } if (typeof bound === 'number') { return bound; } const internalApi = this.adaptableApi.styledColumnApi.internalApi; if (bound === 'Col-Min') { return internalApi.getMinValueForNumericColumn(abColumn); } if (bound === 'Col-Max') { return internalApi.getMaxValueForNumericColumn(abColumn); } if (bound === 'Col-Avg') { return internalApi.getAvgValueForNumericColumn(abColumn); } if (bound === 'Col-Median') { return internalApi.getMedianValueForNumericColumn(abColumn); } if (typeof bound === 'string') { if (!this.adaptableApi.columnApi.isColumnInGrid(bound)) { return undefined; } const raw = this.adaptableApi.gridApi.getRawValueFromRowNode(rowNode, bound); const num = typeof raw === 'number' ? raw : Number(raw); return isNaN(num) ? undefined : num; } return undefined; } setupColumnQuickFilterText({ col, abColumn }) { this.setColDefProperty(col, 'getQuickFilterText', (userGetQuickFilterText) => { if (userGetQuickFilterText) { return userGetQuickFilterText; } return (params) => { const visibleColumnsMap = this.adaptableApi.layoutApi.getCurrentVisibleColumnIdsMapForTableLayout(); const isVisible = visibleColumnsMap[abColumn.columnId]; if (!isVisible) { return ''; } return this.adaptableApi.gridApi.getDisplayValueFromRowNode(params.node, abColumn.columnId); }; }); } setupColumnAllowedAggFuncs({ col, abColumn }) { this.setColDefProperty(col, 'allowedAggFuncs', () => { if (!abColumn.availableAggregationFunctions) { return undefined; } return abColumn.availableAggregationFunctions.filter((func) => !isWeightedAverageAggFuncName(func)); }); } setupColumnType(columnSetupInfo) { const { col, colId } = columnSetupInfo; this.setColDefProperty(col, 'type', (original_columnType) => { const originalTypes = original_columnType == undefined ? [] : Array.isArray(original_columnType) ? original_columnType : [original_columnType]; const columnTypes = new Set(originalTypes); if (this.adaptableApi.columnApi.isCalculatedColumn(colId)) { columnTypes.add(CALCULATED_COLUMN_TYPE); } if (this.adaptableApi.columnApi.isFreeTextColumn(colId)) { columnTypes.add(FREE_TEXT_COLUMN_TYPE); } if (this.adaptableApi.columnApi.isActionColumn(colId)) { columnTypes.add(ACTION_COLUMN_TYPE); } if (this.adaptableApi.columnApi.isFdc3Column(colId)) { columnTypes.add(FDC3_COLUMN_TYPE); } return Array.from(columnTypes); }); } setupColumnCellDataType(columnSetupInfo) { const { col, colId } = columnSetupInfo; this.setColDefProperty(col, 'cellDataType', (original_cellDataType) => { if (original_cellDataType === 'dateString') { errorOnce(`AG Grid: Column '${colId}' specifies cellDataType='dateString' which is no longer supported. It has been automatically replaced with cellDataType='date'. Please update your column definition.`); return 'date'; } return original_cellDataType ?? true; }); } setupColumnHeader({ col }) { this.setColDefProperty(col, 'headerValueGetter', (original_headerValueGetter) => { if (!isProvidedByAdaptable(original_headerValueGetter)) { this.adaptableApi.logWarn(`colDef.headerValueGetter is defined for column '${col.getColId()}', and overrides the Adaptable custom header mechanism! We recommend using a ColumnOptions.columnHeader instead!`); } return original_headerValueGetter; }); } setupColumnFilter(columnSetup) { const { col, colDef, abColumn, colId } = columnSetup; const useAdaptableFilter = this.adaptableOptions.filterOptions.useAdaptableFiltering; if (!useAdaptableFilter) { return; } if (this.adaptableApi.columnApi.isAutoRowGroupColumn(col.getColId())) { if (this.adaptableApi.gridApi.isTreeDataGrid()) { this.setColDefProperty(col, 'filter', (original_filter) => { const autoGroupColumnDef = this.agGridApi.getGridOption('autoGroupColumnDef'); if (autoGroupColumnDef?.filter != undefined) { return original_filter; } else { return false; } }); } else { this.setColDefProperty(col, 'filter', () => { return 'agGroupColumnFilter'; }); } if (this.adaptableApi.columnApi.isAutoRowGroupColumnForMulti(colId)) { return; } } this.setColDefProperty(col, 'filter', (original_filter) => { const pivotMode = this.adaptableInstance.isInPivotMode(); if (!useAdaptableFilter) { return original_filter; } if (!colDef.filter) { return; } if (typeof original_filter !== 'boolean' && typeof original_filter?.handler !== 'function' && !pivotMode) { this.adaptableApi.consoleError(`Column "${colId}" has a custom filter in colDef.filter, but Adaptable filtering only accepts boolean (true/false) values for this property.`); return false; } return { component: AgGridFilterAdapterFactory(this.adaptableInstance), handler: () => new AdaptableFilterHandler(this.adaptableApi), }; }); } setupColumnFloatingFilter({ col, colDef }) { const userProvidedFilterProp = this.getUserColDefProperty(col.getColId(), 'filter'); const hasInvalidFilterProp = typeof userProvidedFilterProp !== 'boolean' && typeof userProvidedFilterProp?.handler !== 'function' && !this.adaptableInstance.isInPivotMode(); if (hasInvalidFilterProp) { return false; } const isFloatingFilterDisabled = !colDef.filter || !colDef.floatingFilter || !this.adaptableOptions.filterOptions.useAdaptableFiltering || !this.adaptableApi.filterApi.columnFilterApi.isQuickFilterVisible(); if (this.adaptableApi.columnApi.isAutoRowGroupColumnForMulti(col.getColId())) { this.setColDefProperty(col, 'floatingFilter', (original_floatingFilter) => { return original_floatingFilter; }); this.setColDefProperty(col, 'suppressFloatingFilterButton', () => { return true; }); return; } this.setColDefProperty(col, 'floatingFilterComponent', () => { if (isFloatingFilterDisabled) { return; } return AgGridFloatingFilterAdapterFactory(this.adaptableInstance); }); this.setColDefProperty(col, 'floatingFilter', () => { if (isFloatingFilterDisabled) { return; } return AgGridFloatingFilterAdapterFactory(this.adaptableInstance); }); this.setColDefProperty(col, 'suppressFloatingFilterButton', () => { return !isFloatingFilterDisabled; }); } setupColumnValueFormatter({ col, abColumn }) { this.setColDefProperty(col, 'valueFormatter', (userPropertyValue) => { const activeFormatColumnsWithDisplayFormat = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnsWithDisplayFormatForColumn(abColumn, { target: 'cell' }); if (!activeFormatColumnsWithDisplayFormat.length) { return; } return (params) => { const { node, value } = params; const mostRelevantFormatColumn = this.adaptableApi.formatColumnApi.internalApi.getMostRelevantFormatColumnForColumn(activeFormatColumnsWithDisplayFormat, abColumn, { node, value }); if (!mostRelevantFormatColumn) { return value; } const resolvedDisplayFormat = resolveDisplayFormat(mostRelevantFormatColumn.DisplayFormat); if (!resolvedDisplayFormat) { return value; } const formatterOptions = resolvedDisplayFormat.Options; if (resolvedDisplayFormat.Formatter === 'NumberFormatter') { if (this.adaptableApi.columnScopeApi.isColumnInNumericScope(abColumn, mostRelevantFormatColumn.Scope)) { let cellValue = params.value; if (typeof params.value?.toNumber === 'function' && typeof params.value?.toString === 'function') { cellValue = params.value.toNumber(); } return this.adaptableApi.formatColumnApi.internalApi.getNumberFormattedValue(cellValue, params.node, abColumn, formatterOptions); } } if (resolvedDisplayFormat.Formatter === 'DateFormatter') { if (this.adaptableApi.columnScopeApi.isColumnInDateScope(abColumn, mostRelevantFormatColumn.Scope)) { let cellValue = params.value; if (typeof params.value?.toNumber === 'function' && typeof params.value?.toString === 'function') { cellValue = params.value.toString(); } return this.adaptableApi.formatColumnApi.internalApi.getDateFormattedValue(cellValue, params.node, abColumn, formatterOptions); } } if (resolvedDisplayFormat.Formatter === 'StringFormatter') { if (this.adaptableApi.columnScopeApi.isColumnInTextScope(abColumn, mostRelevantFormatColumn.Scope)) { let cellValue = params.value; if (typeof params.value?.toNumber === 'function' && typeof params.value?.toString === 'function') { cellValue = params.value.toString(); } return this.adaptableApi.formatColumnApi.internalApi.getStringFormattedValue(cellValue, params.node, abColumn, formatterOptions); } } return value; }; }); } setupColumnEditable({ col }) { this.setColDefProperty(col, 'editable', (original_editable) => { const editableCallback = (params) => { const getOriginalColDefEditable = () => { if (typeof original_editable === 'function') { return original_editable(params); } else { return original_editable; } }; if (this.adaptableApi.gridApi.internalApi.hasCellEditableAccordingToEditOptions()) { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, params.column.getColId()); return this.adaptableApi.gridApi.internalApi.isCellEditableAccordingToEditOptions(gridCell, getOriginalColDefEditable()); } return getOriginalColDefEditable(); }; return editableCallback; }); } setupColumnValueSetter({ col, colId, abColumn }) { this.setColDefProperty(col, 'valueSetter', (userValueSetter) => { const preventEditAlertsForColumn = this.adaptableApi.alertApi.internalApi .getAlertDefinitionsWithPreventEdit() .filter((alertDefinition) => { return this.adaptableApi.columnScopeApi.isColumnInScope(abColumn, alertDefinition.Scope); }); const noValidations = !preventEditAlertsForColumn.length && !this.adaptableOptions.editOptions?.validateOnServer; if (noValidations) { return; } const valueSetter = (params) => { const field = params.column.getColDef().field; if (noValidations) { if (typeof userValueSetter === 'function') { return userValueSetter(params); } if (field) { params.data[field] = params.newValue; } return true; } const cellDataChangedInfo = this.adaptableApi.internalApi.buildCellDataChangedInfo({ oldValue: params.oldValue, newValue: params.newValue, column: this.adaptableApi.columnApi.getColumnWithColumnId(params.column.getColId()), primaryKeyValue: this.adaptableApi.gridApi.getPrimaryKeyValueForRowNode(params.node), rowNode: params.node, trigger: 'edit', }); if (cellDataChangedInfo.oldValue === cellDataChangedInfo.newValue) { return true; } const newRow = { ...params.node, data: { ...params.node.data } }; newRow.data[field] = params.newValue; const cellDataChangeInfoForSyncValidation = { ...cellDataChangedInfo, rowNode: newRow, }; if (!this.adaptableApi.internalApi .getValidationService() .performValidation(cellDataChangeInfoForSyncValidation)) { return false; } const onServerValidationCompleted = () => { }; if (this.adaptableOptions.editOptions?.validateOnServer) { this.adaptableApi.internalApi .getValidationService() .performServerValidation(cellDataChangedInfo, { onServerValidationCompleted, })(); } if (typeof userValueSetter === 'function') { return userValueSetter(params); } if (field) { params.data[field] = params.newValue; } else { throw `Cannot edit a column without a field - column id was ${colId}`; } return true; }; return valueSetter; }); } setupColumnComparator({ col, colId, abColumn }) { const customSort = this.adaptableApi.customSortApi.getCustomSortForColumn(colId); const customSortComparer = this.adaptableApi.customSortApi.internalApi.getCustomSortComparer(abColumn.columnId); const memoizedDateValues = new Map(); const getDateValue = (rawDateValue) => { if (memoizedDateValues.has(rawDateValue)) { return memoizedDateValues.get(rawDateValue); } const dateValue = this.adaptableApi.internalApi.parseDateValue(rawDateValue); memoizedDateValues.set(rawDateValue, dateValue); return dateValue; }; const comparatorGetter = (propName) => { return (userPropertyValue) => { if (customSortComparer) { return customSortComparer.comparer; } if (customSort && !customSort.IsSuspended) { return this.adaptableApi.customSortApi.internalApi.getDefaultCustomSortComparer(customSort.ColumnId, customSort.SortedValues); } if (userPropertyValue) { return userPropertyValue; } if (abColumn.dataType === 'date') { return (valueA, valueB) => { const dateA = getDateValue(valueA); const dateB = getDateValue(valueB); if (dateA && dateB) { return dateA.getTime() - dateB.getTime(); } else if (dateA) { return 1; } else if (dateB) { return -1; } else { return 0; } }; } }; }; this.setColDefProperty(col, 'comparator', comparatorGetter('comparator')); this.setColDefProperty(col, 'pivotComparator', comparatorGetter('pivotComparator')); } setupColumnGetFindText({ col, abColumn }) { this.setColDefProperty(col, 'getFindText', (userGetFindText) => { return (params) => { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, abColumn.columnId); if (!this.isCellSearchable(gridCell)) { return null; } const getCellSearchText = this.adaptableOptions.quickSearchOptions.getCellSearchText; if (getCellSearchText) { return this.getCellSearchText(gridCell); } return userGetFindText?.(params) ?? gridCell.displayValue; }; }); } getCellSearchText(gridCell) { const getCellSearchText = this.adaptableOptions.quickSearchOptions.getCellSearchText; if (getCellSearchText) { const quickSearchValue = this.adaptableApi.quickSearchApi.getQuickSearchValue(); const quickSearchContext = { ...this.adaptableApi.internalApi.buildBaseContext(), gridCell, quickSearchValue, }; return getCellSearchText(quickSearchContext);