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