UNPKG

@adaptabletools/adaptable

Version:

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

925 lines (924 loc) 45.7 kB
import merge from 'lodash/merge'; import { FilterWrapperFactory } from './FilterWrapper'; import { FloatingFilterWrapperFactory } from './FloatingFilterWrapper'; import { convertAdaptableStyleToCSS, getVariableColor, 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 Helper from '../Utilities/Helpers/Helper'; import { AdaptableNumberEditor, AdaptableReactNumberEditor } from './editors/AdaptableNumberEditor'; import { AdaptableDateEditor, AdaptableReactDateEditor } from './editors/AdaptableDateEditor'; import { AgGridExportAdapter } from './AgGridExportAdapter'; import { AdaptableHelper, } from '../Utilities/Helpers/AdaptableHelper'; export function getEditorForColumnDataType(columnDataType, variant) { if (columnDataType === 'number') { return variant === 'react' ? AdaptableReactNumberEditor : AdaptableNumberEditor; } if (columnDataType === 'date' || columnDataType === 'dateString') { return variant === 'react' ? AdaptableReactDateEditor : AdaptableDateEditor; } } export class AgGridColumnAdapter { 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) { // already undefined, so don't set an own property to the same undefined value return; } colDef[propertyName] = theValue; } getUserColDefProperty(columnId, propertyName) { const userKey = `user.${columnId}.${propertyName}`; return this.colDefPropertyCache.get(userKey); } shouldSkipColumn(colId) { /** * This skips columns like `ag-Grid-SelectionColumn` and possibly other columns * that ag grid will implement in the future * * BUT DOES NOT SKIP GROUP COLUMNS!!! */ return colId.startsWith('ag-Grid-') && !this.adaptableApi.columnApi.isAutoRowGroupColumn(colId); } setupColumns() { const pivotMode = this.agGridApi.isPivotMode(); let cols = pivotMode ? // for pivot mode, we take only the initial columns this.agGridApi.getColumns() : // but for non-pivot mode, we ask for all columns // which also includes those that are generated for row grouping this.agGridApi.getAllGridColumns(); if (pivotMode) { const pivotResultColumns = this.agGridApi.getPivotResultColumns() || []; if (pivotResultColumns.length) { cols = [...cols, ...pivotResultColumns]; } } // this needs to be here, before the other setup below // so the setup methods below reference the correct columns in adaptable store 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, }; this.setupColumnCellRenderer(colSetupInfo); this.setupColumnCellStyle(colSetupInfo); this.setupColumnCellClass(colSetupInfo); this.setupColumnTooltipValueGetter(colSetupInfo); this.setupColumnValueGetter(colSetupInfo); this.setupColumnFilter(colSetupInfo); this.setupColumnFloatingFilter(colSetupInfo); this.setupColumnValueFormatter(colSetupInfo); this.setupColumnEditable(colSetupInfo); this.setupColumnValueSetter(colSetupInfo); this.setupColumnComparator(colSetupInfo); this.setupColumnCellEditor(colSetupInfo); this.setupColumnHeader(colSetupInfo); this.setupColumnQuickFilterText(colSetupInfo); this.setupColumnAllowedAggFuncs(colSetupInfo); this.setupColumnType(colSetupInfo); this.setupColumnCellDataType(colSetupInfo); }); } setupColumnValueGetter({ col }) { // need this here if we want plugins to intercept this.setColDefProperty(col, 'valueGetter', (userValue) => { return userValue; }); } setupColumnCellClass({ col, colId, abColumn }) { this.setColDefProperty(col, 'cellClass', (userCellClass) => { const formatColumns = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnWithStyleClassNameForColumn(abColumn); const quickSearchStyleClassName = this.adaptableApi.quickSearchApi.getQuickSearchStyle().ClassName; const hasQuickSearchStyleClassName = StringExtensions.IsNotNullOrEmpty(quickSearchStyleClassName); const cellClass = (params) => { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, abColumn.columnId); if (!gridCell.column) { return null; } // if a VisualExcel report format export is in progress, we are interested only in the Excel Style Class if (this.adaptableApi.exportApi.internalApi.isVisualDataExportInProgress()) { const userDefinedCellClass = typeof userCellClass === 'function' ? userCellClass(params) : userCellClass; const cellClassKey = AgGridExportAdapter.getExcelClassNameForCell(colId, gridCell.primaryKeyValue, userDefinedCellClass); return this.adaptableInstance.agGridExportAdapter.getExcelStyleIdForCellClassKey(cellClassKey); } const isQuickSearchActive = hasQuickSearchStyleClassName && this.isQuickSearchActive(gridCell, params); 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 ? quickSearchStyleClassName : null, editableClassName, readonlyClassName, editedClassName, highlightAlertClassName, flashingClassName, noteClassName, commentsClassName, ] // we flatten the array because some rules ('userCellClass' etc) might return a string[] .flat() .filter((x) => !!x); const result = returnValue.length ? returnValue : undefined; return result; }; return cellClass; }); } setupColumnCellStyle({ col, colId, abColumn }) { this.setColDefProperty(col, 'cellStyle', (userCellStyle) => { const quickSearchStyle = this.getQuickSearchCellStyle(); const hasQuickSearchStyle = quickSearchStyle != undefined; const cellStyle = (params) => { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, abColumn.columnId); if (!gridCell || !gridCell.column) { return {}; } const isQuickSearchActive = hasQuickSearchStyle && this.isQuickSearchActive(gridCell, params); const userDefined = typeof userCellStyle === 'function' ? userCellStyle(params) : userCellStyle; const result = { ...userDefined, ...this.getReadOnlyCellStyle(gridCell, params), ...this.getEditableCellStyle(gridCell, params), ...this.getEditedCellStyle(gridCell, params), ...this.getFormatColumnAndStyledColumnCellStyle(gridCell, params), ...(isQuickSearchActive ? quickSearchStyle : {}), ...this.getAlertCellStyle(gridCell, params), ...this.getFlashingCellStyle(gridCell, params), ...this.getCellHighlightStyle(gridCell, params), }; return normalizeStyleForAgGrid(result); }; return cellStyle; }); } setupColumnCellEditor({ colId, col, colDef, abColumn }) { const shouldShowSelectCellEditor = this.adaptableApi.userInterfaceApi.internalApi.shouldShowSelectCellEditor(abColumn); const hasRichSelectCellEditor = this.adaptableInstance.agGridAdapter.isAgGridModuleRegistered('RichSelect'); this.setColDefProperty(col, 'cellEditor', () => { if (shouldShowSelectCellEditor) { return hasRichSelectCellEditor ? 'agRichSelectCellEditor' : 'agSelectCellEditor'; } 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; })); return { values, 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.BadgeStyle) { return getBadgeRendererForColumn(styledColumn.BadgeStyle, 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 = 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 && styledColumn.PercentBarStyle && styledColumn.PercentBarStyle.ToolTipText) { hasTooptip = true; if (styledColumn?.PercentBarStyle) { return (params) => { const min = this.adaptableApi.styledColumnApi.internalApi.getNumericStyleMinValue(styledColumn, abColumn, params.node, params.value); const max = this.adaptableApi.styledColumnApi.internalApi.getNumericStyleMaxValue(styledColumn, abColumn, params.node, params.value); const textOptions = styledColumn.PercentBarStyle.ToolTipText; let returnValue = ''; if (textOptions.includes('CellValue')) { returnValue = params.value; } if (textOptions.includes('PercentageValue')) { const clampedValue = Helper.clamp(params.value, min, max); const percentageValue = ((clampedValue - min) / (max - min)) * 100; returnValue += ' ' + `(${percentageValue.toFixed(0)}%)`; } return returnValue ? returnValue : params.value; }; } } }); } setupColumnQuickFilterText({ col, abColumn }) { this.setColDefProperty(col, 'getQuickFilterText', (userGetQuickFilterText) => { if (userGetQuickFilterText) { return userGetQuickFilterText; } return (params) => { const visibleCoulmnsMap = this.adaptableApi.layoutApi.getCurrentVisibleColumnIdsMapForTableLayout(); const isVisible = visibleCoulmnsMap[abColumn.columnId]; if (!isVisible) { return ''; } return this.adaptableApi.gridApi.getDisplayValueFromRowNode(params.node, abColumn.columnId); }; }); } setupColumnAllowedAggFuncs({ col, abColumn }) { this.setColDefProperty(col, 'allowedAggFuncs', () => { return abColumn.availableAggregationFunctions; }); } setupColumnType(columnSetupInfo) { const { col, colId } = columnSetupInfo; // AG Grid introduced since v30.x an inferred cellDataType // the problem is that it breaks the default value formatter and/or editor (especially for Date columns) 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 } = columnSetupInfo; this.setColDefProperty(col, 'cellDataType', (original_cellDataType) => { return original_cellDataType ?? true; }); } setupColumnHeader({ col, abColumn }) { const previousColumnHeader = col?.getColDef()?.headerName; this.setColDefProperty(col, 'headerName', (userHeaderName) => { // set the default to the AG Grid provided values // from https://github.com/ag-grid/ag-grid/blob/v26.1.0/community-modules/core/src/ts/columns/columnModel.ts#L2515 let resultHeaderName = userHeaderName ?? StringExtensions.CamelCaseToHumanText(col.getColDef().field); const layoutCustomHeader = this.adaptableApi.layoutApi.getCurrentLayout().ColumnHeaders?.[col.getColId()]; if (layoutCustomHeader) { resultHeaderName = layoutCustomHeader; } return resultHeaderName; }); const newColumnHeader = col?.getColDef()?.headerName; return previousColumnHeader !== newColumnHeader; } setupColumnFilter({ col, colDef }) { this.setColDefProperty(col, 'filter', () => { if (!colDef.filter) { return; } if (!this.adaptableOptions.filterOptions.useAdaptableFiltering) { return; } this.agGridApi.destroyFilter(col); return FilterWrapperFactory(this.adaptableInstance); }); } setupColumnFloatingFilterTemporarily(initialGridOptions) { initialGridOptions.columnDefs ?.filter((colDef) => !this.isColGroupDef(colDef)) .map((colDef) => { const isFloatingFilterEnabled = initialGridOptions.defaultColDef?.floatingFilter || colDef.floatingFilter; if (isFloatingFilterEnabled) { colDef.floatingFilterComponent = FloatingFilterWrapperFactory(this.adaptableInstance); } }); } setupColumnFloatingFilter({ col, colDef }) { const isFloatingFilterDisabled = !colDef.floatingFilter || !this.adaptableOptions.filterOptions.useAdaptableFiltering || !this.adaptableOptions.filterOptions.columnFilterOptions.showQuickFilter; this.setColDefProperty(col, 'floatingFilterComponent', () => { if (isFloatingFilterDisabled) { return; } return FloatingFilterWrapperFactory(this.adaptableInstance); }); this.setColDefProperty(col, 'floatingFilter', (original_floatingFilter) => { if (isFloatingFilterDisabled) { return; } return FloatingFilterWrapperFactory(this.adaptableInstance); }); this.setColDefProperty(col, 'suppressFloatingFilterButton', () => { return !isFloatingFilterDisabled; }); } setupColumnValueFormatter({ col, abColumn }) { this.setColDefProperty(col, 'valueFormatter', (userPropertyValue) => { const activeFormatColumnsWithDisplayFormat = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnsWithDisplayFormatForColumn(abColumn); if (!activeFormatColumnsWithDisplayFormat.length) { return; } return (params) => { const { node, value } = params; const mostRelevantFormatColumn = this.adaptableApi.formatColumnApi.internalApi.getMostRelevantFormatColumnForColumn(activeFormatColumnsWithDisplayFormat, abColumn, { node, value }); if (!mostRelevantFormatColumn) { // ALL FormatColumns are conditional and NONE of them are relevant for this row return value; } const formatterOptions = mostRelevantFormatColumn.DisplayFormat.Options; if (mostRelevantFormatColumn.DisplayFormat.Formatter === 'NumberFormatter') { // change the Number format - if the scope allows it if (this.adaptableApi.columnScopeApi.isColumnInNumericScope(abColumn, mostRelevantFormatColumn.Scope)) { let cellValue = params.value; if (typeof params.value?.toNumber === 'function' && typeof params.value?.toString === 'function') { // aggregation values are wrapped in an AG Grid specific object cellValue = params.value.toNumber(); } return this.adaptableApi.formatColumnApi.internalApi.getNumberFormattedValue(cellValue, params.node, abColumn, formatterOptions); } } if (mostRelevantFormatColumn.DisplayFormat.Formatter === 'DateFormatter') { // change the Date format - if the scope allows it if (this.adaptableApi.columnScopeApi.isColumnInDateScope(abColumn, mostRelevantFormatColumn.Scope)) { let cellValue = params.value; if (typeof params.value?.toNumber === 'function' && typeof params.value?.toString === 'function') { // aggregation values are wrapped in an AG Grid specific object cellValue = params.value.toString(); } return this.adaptableApi.formatColumnApi.internalApi.getDateFormattedValue(cellValue, params.node, abColumn, formatterOptions); } } if (mostRelevantFormatColumn.DisplayFormat.Formatter === 'StringFormatter') { // change the String format - if the scope allows it if (this.adaptableApi.columnScopeApi.isColumnInTextScope(abColumn, mostRelevantFormatColumn.Scope)) { let cellValue = params.value; if (typeof params.value?.toNumber === 'function' && typeof params.value?.toString === 'function') { // aggregation values are wrapped in an AG Grid specific object cellValue = params.value.toString(); } return this.adaptableApi.formatColumnApi.internalApi.getStringFormattedValue(cellValue, params.node, abColumn, formatterOptions); } } // should NEVER arrive at this line, but just to be sure return value; }; }); } setupColumnEditable({ col }) { this.setColDefProperty(col, 'editable', (original_editable) => { // cell is NOT editable by default const editableCallback = (params) => { const getOriginalColDefEditable = () => { if (typeof original_editable === 'function') { return original_editable(params); } else { return original_editable; } }; // 1. evaluate EditOptions.isCellEditable if provided if (this.adaptableApi.gridApi.internalApi.hasCellEditableAccordingToEditOptions()) { const gridCell = this.adaptableApi.gridApi.getGridCellFromRowNode(params.node, params.column.getColId()); const editOptionsEditability = this.adaptableApi.gridApi.internalApi.isCellEditableAccordingToEditOptions(gridCell, getOriginalColDefEditable()); if (editOptionsEditability) { return editOptionsEditability; } } // 2. otherwise, fallback to colDef.editable 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) { //TODO also consider the case when userValueSetter is a string if (typeof userValueSetter === 'function') { return userValueSetter(params); } // we allowed it go reach this point // just to run isCellEditable // and since this has already run and we have no other validations // just assign the new value and exit 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; } /** * Validate on the future row, with the new value. * structuredClone fails, it contains functions. */ 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, })(); } //TODO also consider the case when userValueSetter is a string 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 columnSortComparer = this.adaptableApi.customSortApi.internalApi.getCustomSortComparer(abColumn.columnId); const comparatorGetter = (propName) => { return () => { return this.adaptableApi.columnApi.internalApi.getActiveColumnComparator(colId, customSort, columnSortComparer); }; }; this.setColDefProperty(col, 'comparator', comparatorGetter('comparator')); this.setColDefProperty(col, 'pivotComparator', comparatorGetter('pivotComparator')); } isQuickSearchActive(gridCell, params) { const quickSearchValue = this.adaptableApi.quickSearchApi.getQuickSearchValue(); if (StringExtensions.IsNullOrEmpty(quickSearchValue)) { return false; } if (!gridCell.rowNode) { return false; } let quickSearchContext; const isCellSearchable = this.adaptableOptions.quickSearchOptions.isCellSearchable; if (isCellSearchable) { quickSearchContext = { ...this.adaptableApi.internalApi.buildBaseContext(), gridCell, quickSearchValue, }; if (!isCellSearchable(quickSearchContext)) { return false; } } const runCustomQuickSearch = this.adaptableOptions.quickSearchOptions.runCustomQuickSearch; if (runCustomQuickSearch) { if (!quickSearchContext) { quickSearchContext = { ...this.adaptableApi.internalApi.buildBaseContext(), gridCell, quickSearchValue, }; } return runCustomQuickSearch(quickSearchContext); } const isCaseSensitive = this.adaptableOptions.quickSearchOptions.isQuickSearchCaseSensitive; const cellDisplayValue = String(gridCell.displayValue); const displayValue = isCaseSensitive ? cellDisplayValue : cellDisplayValue.toLocaleLowerCase(); const searchText = isCaseSensitive ? quickSearchValue : quickSearchValue.toLocaleLowerCase(); return displayValue.indexOf(searchText) !== -1; } getEditableCellClass(gridCell, params) { const editableCellStyle = this.adaptableApi.userInterfaceApi.getEditableCellStyle(); if (!editableCellStyle?.ClassName) { return null; } const isCellEditable = this.adaptableApi.gridApi.isCellEditable(gridCell); return isCellEditable ? editableCellStyle.ClassName : null; } getReadonlyCellClass(gridCell, params) { const readonlyCellStyle = this.adaptableApi.userInterfaceApi.getReadOnlyCellStyle(); if (!readonlyCellStyle?.ClassName) { return null; } const isCellReadonly = !this.adaptableApi.gridApi.isCellEditable(gridCell); return isCellReadonly ? readonlyCellStyle.ClassName : null; } getEditedCellClass(gridCell, params) { const editedCellStyle = this.adaptableApi.userInterfaceApi.getEditedCellStyle(); if (!editedCellStyle?.ClassName) { return null; } const isCellEdited = this.adaptableApi.gridApi.isCellEdited(gridCell); return isCellEdited ? editedCellStyle.ClassName : null; } getAlertCellClass(gridCell, params) { const alert = this.adaptableApi.alertApi.internalApi.getAdaptableAlertWithHighlightCell(gridCell.column.columnId, params.node); const highlightCell = alert?.alertDefinition?.AlertProperties?.HighlightCell; return typeof highlightCell === 'object' && highlightCell?.ClassName ? highlightCell?.ClassName : null; } getFlashingCellClass(gridcell, params) { const primaryKey = params.node.aggData ? params.node.id : gridcell.primaryKeyValue; const flashingCell = this.adaptableApi.flashingCellApi.internalApi.getAdaptableFlashingCellFor(primaryKey, gridcell.column.columnId); if (!flashingCell) { return; } return flashingCell.direction === 'up' ? flashingCell.flashingCellDefinition.UpChangeStyle?.ClassName : flashingCell.direction === 'down' ? flashingCell.flashingCellDefinition.DownChangeStyle?.ClassName : flashingCell.direction === 'neutral' ? flashingCell.flashingCellDefinition.NeutralChangeStyle?.ClassName : undefined; } getNoteCellClassName(gridCell, params) { if (!this.adaptableApi.internalApi.getModuleService().isModuleAvailable('Note')) { return; } if (!this.adaptableApi.noteApi.internalApi.areNotesSupported()) { return; } const cellPosition = { PrimaryKeyValue: gridCell.primaryKeyValue, ColumnId: gridCell.column.columnId, }; const cellNote = this.adaptableApi.noteApi.getNoteForCell(cellPosition); if (!cellNote) { return undefined; } return 'ab-Cell-Note'; } getCommentCellClassName(gridCell, params) { if (!this.adaptableApi.internalApi.getModuleService().isModuleAvailable('Comment')) { return; } if (!this.adaptableApi.commentApi.internalApi.areCommentsSupportedInLayout()) { return; } const position = { PrimaryKeyValue: gridCell.primaryKeyValue, ColumnId: gridCell.column.columnId, }; const cellComments = this.adaptableApi.commentApi.getCommentThreadForCell(position); if (!cellComments) { return undefined; } return 'ab-Cell-Comment'; } getFormatColumnCellClass(formatColumns, abColumn, params) { const classNames = formatColumns .map((formatColumn) => { if (formatColumn.Style?.ClassName && this.adaptableApi.formatColumnApi.internalApi.formatColumnShouldRender(formatColumn, abColumn, params.node, params.value)) { return formatColumn.Style?.ClassName; } }) .filter((x) => !!x); return classNames; } getQuickSearchCellStyle() { const quickSearchStyle = this.adaptableApi.quickSearchApi.getQuickSearchStyle(); if (!quickSearchStyle || StringExtensions.IsNotNullOrEmpty(quickSearchStyle.ClassName)) { return undefined; } return convertAdaptableStyleToCSS(quickSearchStyle); } getReadOnlyCellStyle(gridCell, params) { const editableCellStyle = this.adaptableApi.userInterfaceApi.getReadOnlyCellStyle(); if (!editableCellStyle) { return undefined; } if (gridCell) { if (!this.adaptableApi.gridApi.isCellEditable(gridCell)) { return convertAdaptableStyleToCSS(editableCellStyle); } } return undefined; } getEditableCellStyle(gridCell, params) { const editableCellStyle = this.adaptableApi.userInterfaceApi.getEditableCellStyle(); if (!editableCellStyle) { return undefined; } if (gridCell) { if (this.adaptableApi.gridApi.isCellEditable(gridCell)) { return convertAdaptableStyleToCSS(editableCellStyle); } } return undefined; } getEditedCellStyle(gridCell, params) { const editedCellStyle = this.adaptableApi.userInterfaceApi.getEditedCellStyle(); if (!editedCellStyle) { return undefined; } if (gridCell) { if (this.adaptableApi.gridApi.isCellEdited(gridCell)) { return convertAdaptableStyleToCSS(editedCellStyle); } } return undefined; } /** * The combination of styled column and format cells * This functiond decides when the two can be merged. */ getFormatColumnAndStyledColumnCellStyle(gridCell, params) { let styledColumn = this.adaptableApi.styledColumnApi.getStyledColumnForColumnId(gridCell.column.columnId); let styledColumnStyle = {}; if (styledColumn && !styledColumn?.IsSuspended) { const styledCellStyle = this.getStyledColumnStyle(styledColumn, gridCell.column, params); // for percentbar we want to merge if (styledColumn.PercentBarStyle || styledColumn.BadgeStyle || styledColumn.GradientStyle) { styledColumnStyle = styledCellStyle; } else { // For other ones wo do not want to merge return styledCellStyle; } } const activeFormatColumnsWithStyle = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnsWithStyleForColumn(gridCell.column); // background color of styledColumn.GradientStyle has precedence and wins over FormatColumn return { ...this.getFormatColumnCellStyle(gridCell.column, activeFormatColumnsWithStyle, params), ...styledColumnStyle, }; } getStyledColumnStyle(styledColumn, abColumn, params) { let style = {}; const gradientStyle = styledColumn?.GradientStyle; if (params.value === undefined) { return; } let colValue = params.value; if (this.adaptableApi.gridApi.isGroupRowNode(params.node)) { return style; // if (styledColumn.IncludeGroupedRows) { // const minColumnValue = // this.adaptableApi.styledColumnApi.internalApi.getMinValueForNumericColumn(abColumn); // const maxColumnValue = // this.adaptableApi.styledColumnApi.internalApi.getMaxValueForNumericColumn(abColumn); // /** // * Color should always be in bounds, it should not overflow. // * If the value is out of range, it shoul set the maximum/minimum color. // */ // colValue = clamp(params.value, minColumnValue, maxColumnValue); // } else { // return style; // } } if (styledColumn.BadgeStyle && !styledColumn.BadgeStyle.RowScope?.ExcludeSummaryRows && this.adaptableApi.gridApi.isSummaryNode(params?.node)) { return style; } if (gradientStyle) { const min = this.adaptableApi.styledColumnApi.internalApi.getNumericStyleMinValue(styledColumn, abColumn, params.node, colValue); const max = this.adaptableApi.styledColumnApi.internalApi.getNumericStyleMaxValue(styledColumn, abColumn, params.node, colValue); let cellBackColor; let reverseGradient = false; if (gradientStyle.ColumnComparison) { cellBackColor = gradientStyle.ColumnComparison.Color; } else { const matchingRange = this.adaptableApi.styledColumnApi.internalApi.findRangeForColumn(gradientStyle.CellRanges, abColumn, gradientStyle.RangeValueType, colValue); if (matchingRange) { cellBackColor = matchingRange.Color; reverseGradient = matchingRange.ReverseGradient; } } const increase = Math.abs(max - min); const percentage = ((colValue - min) / increase) * 100; let alpha = Number((percentage / 100).toPrecision(2)); if (reverseGradient) { alpha = 1 - alpha; } const preparedColor = getVariableColor(cellBackColor); // if no range match, do not apply color (black in this case) if (cellBackColor) { style.backgroundColor = tinycolor(preparedColor).setAlpha(alpha).toRgbString(); } } if (styledColumn.PercentBarStyle && styledColumn.PercentBarStyle.CellText) { style.paddingTop = 0; style.paddingBottom = 0; } return style; } getFormatColumnCellStyle(abColumn, activeFormatColumnsWithStyle, params) { if (!activeFormatColumnsWithStyle.length) { return {}; } const relevantFormatColumnsWithStyle = activeFormatColumnsWithStyle.filter((formatColumn) => { return this.adaptableApi.formatColumnApi.internalApi.formatColumnShouldRender(formatColumn, abColumn, params.node, params.value); }); return this.getFormatColumnAdaptableStyle(relevantFormatColumnsWithStyle); } getFormatColumnAdaptableStyle(formatColumns) { // first has more precedence, then they need to be applied in reverse order return formatColumns.reduceRight((style, formatColumn) => { const formatColumnStyle = formatColumn.Style ? convertAdaptableStyleToCSS(formatColumn.Style) : {}; if (formatColumn.CellAlignment) { switch (formatColumn.CellAlignment) { case 'Left': style.textAlign = 'left'; break; case 'Right': style.textAlign = 'right'; break; case 'Center': style.textAlign = 'center'; break; } } return { ...style, ...formatColumnStyle }; }, {}); } getAlertCellStyle(gridCell, params) { const alert = this.adaptableApi.alertApi.internalApi.getAdaptableAlertWithHighlightCell(gridCell.column.columnId, params.node); if (alert) { const highlightCell = alert.alertDefinition.AlertProperties.HighlightCell; if (typeof highlightCell === 'object') { return convertAdaptableStyleToCSS(highlightCell); } return { backgroundColor: UIHelper.getColorByMessageType(alert.alertDefinition.MessageType), }; } } getFlashingCellStyle(gridCell, params) { const primaryKey = params.node.aggData ? params.node.id : gridCell.primaryKeyValue; const flashingCell = this.adaptableApi.flashingCellApi.internalApi.getAdaptableFlashingCellFor(primaryKey, gridCell.column.columnId); if (!flashingCell) { return {}; } return convertAdaptableStyleToCSS((flashingCell.direction === 'up' ? flashingCell.flashingCellDefinition.UpChangeStyle : flashingCell.direction === 'down' ? flashingCell.flashingCellDefinition.DownChangeStyle : flashingCell.flashingCellDefinition.NeutralChangeStyle) ?? {}); } getCellHighlightStyle(gridCell, params) { const cellHightlight = this.adaptableApi.internalApi .getInternalState() .CellHighlightInfo.find((cellHighlightInfo) => { return (gridCell.column.columnId === cellHighlightInfo.columnId && cellHighlightInfo.primaryKeyValue === gridCell.primaryKeyValue); }); if (cellHightlight) { return convertAdaptableStyleToCSS(cellHightlight.highlightStyle); } } isColGroupDef(columnDefinition) { // @ts-ignore return columnDefinition['children'] != null; } }