@adaptabletools/adaptable
Version:
Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
210 lines (209 loc) • 8.87 kB
JavaScript
import tinycolor from 'tinycolor2';
import { convertAdaptableStyleToCSS } from '../StyleHelper';
import { getAutoContrastTextColor, getVariableColor } from '../StyleHelper';
import { clamp } from '../../Extensions/NumberExtensions';
export const DEFAULT_GRADIENT_MIN_ALPHA = 0.15;
export const DEFAULT_GRADIENT_MAX_ALPHA = 1;
export const hasGradientRangesConfigured = (gs) => !!(gs?.ZeroCentred || gs?.CellRanges?.length || gs?.ColumnComparison);
const evenlySpacedValues = (min, max, count) => {
if (count <= 1) {
return [min];
}
const step = (max - min) / (count - 1);
return Array.from({ length: count }, (_, i) => min + step * i);
};
export const STYLED_COLUMN_PREVIEW_SAMPLE_COUNT = 6;
export const ZERO_CENTRED_PREVIEW_SCALE_MIN = -20;
export const ZERO_CENTRED_PREVIEW_SCALE_MAX = 20;
export const ZERO_CENTRED_GRADIENT_PREVIEW_VALUES = evenlySpacedValues(ZERO_CENTRED_PREVIEW_SCALE_MIN, ZERO_CENTRED_PREVIEW_SCALE_MAX, STYLED_COLUMN_PREVIEW_SAMPLE_COUNT);
export const getZeroCentredGradientPreviewValues = () => evenlySpacedValues(ZERO_CENTRED_PREVIEW_SCALE_MIN, ZERO_CENTRED_PREVIEW_SCALE_MAX, STYLED_COLUMN_PREVIEW_SAMPLE_COUNT);
export function isDivergingZeroCellRanges(ranges) {
if (!ranges || ranges.length !== 2) {
return false;
}
const [a, b] = ranges;
return a.Max === 0 && b.Min === 0;
}
export function isZeroCentredGradientStyle(gs) {
return !!(gs.ZeroCentred || isDivergingZeroCellRanges(gs.CellRanges));
}
export const getGradientPreviewSampleValues = (styledColumn, api) => {
const gs = styledColumn.GradientStyle;
if (!gs || !hasGradientRangesConfigured(gs)) {
return [];
}
if (isZeroCentredGradientStyle(gs)) {
return getZeroCentredGradientPreviewValues();
}
if (gs.ColumnComparison) {
const min = Number(gs.ColumnComparison.MinValue);
const max = Number(gs.ColumnComparison.MaxValue);
if (!Number.isNaN(min) && !Number.isNaN(max)) {
return evenlySpacedValues(min, max, STYLED_COLUMN_PREVIEW_SAMPLE_COUNT);
}
return evenlySpacedValues(0, 100, STYLED_COLUMN_PREVIEW_SAMPLE_COUNT);
}
const column = styledColumn.ColumnId
? api.columnApi.getColumnWithColumnId(styledColumn.ColumnId)
: undefined;
if (!column) {
return evenlySpacedValues(0, 100, STYLED_COLUMN_PREVIEW_SAMPLE_COUNT);
}
const colMin = api.styledColumnApi.internalApi.getMinValueForNumericColumn(column);
const colMax = api.styledColumnApi.internalApi.getMaxValueForNumericColumn(column);
if (colMin === colMax) {
return [colMin];
}
return evenlySpacedValues(colMin, colMax, STYLED_COLUMN_PREVIEW_SAMPLE_COUNT);
};
const getZeroCentredPreviewColors = (gradientStyle) => {
if (gradientStyle.ZeroCentred) {
return {
negative: gradientStyle.ZeroCentred.NegativeColor,
positive: gradientStyle.ZeroCentred.PositiveColor,
};
}
const ranges = gradientStyle.CellRanges;
if (isDivergingZeroCellRanges(ranges)) {
return {
negative: ranges[0].Color,
positive: ranges[1].Color,
};
}
return null;
};
const getZeroCentredGradientPreviewCellStyle = (cellValue, gradientStyle) => {
const colors = getZeroCentredPreviewColors(gradientStyle);
if (!colors) {
return {};
}
const previewMin = ZERO_CENTRED_PREVIEW_SCALE_MIN;
const previewMax = ZERO_CENTRED_PREVIEW_SCALE_MAX;
const syntheticRanges = [
{ Min: previewMin, Max: 0, Color: colors.negative },
{ Min: 0, Max: previewMax, Color: colors.positive },
];
let matchingRange = null;
for (const range of syntheticRanges) {
const min = range.Min;
const max = range.Max;
if (cellValue >= min && cellValue <= max) {
matchingRange = range;
break;
}
}
if (!matchingRange) {
return {};
}
const min = matchingRange.Min;
const max = matchingRange.Max;
const reverseGradient = matchingRange.Min === previewMin && matchingRange.Max === 0;
const minAlphaBound = gradientStyle.MinAlpha ?? DEFAULT_GRADIENT_MIN_ALPHA;
const maxAlphaBound = gradientStyle.MaxAlpha ?? DEFAULT_GRADIENT_MAX_ALPHA;
const lo = Math.min(clamp(minAlphaBound, 0, 1), clamp(maxAlphaBound, 0, 1));
const hi = Math.max(clamp(minAlphaBound, 0, 1), clamp(maxAlphaBound, 0, 1));
const span = Math.abs(max - min);
let t = span === 0 ? 0.5 : (Number(cellValue) - min) / span;
t = clamp(t, 0, 1);
if (reverseGradient) {
t = 1 - t;
}
const alpha = Number((lo + t * (hi - lo)).toPrecision(4));
let style = {};
const preparedColor = getVariableColor(matchingRange.Color);
const bg = tinycolor(preparedColor).setAlpha(alpha);
style.backgroundColor = bg.toRgbString();
if (gradientStyle.AutoContrastText) {
style.color = getAutoContrastTextColor(preparedColor, alpha);
}
const autoContrastColor = gradientStyle.AutoContrastText && style.color ? style.color : undefined;
if (gradientStyle.Font) {
style = { ...style, ...convertAdaptableStyleToCSS(gradientStyle.Font) };
}
if (autoContrastColor) {
style.color = autoContrastColor;
}
if (gradientStyle.Font?.Alignment && gradientStyle.Font.Alignment !== 'Default') {
style.textAlign = gradientStyle.Font.Alignment.toLowerCase();
}
return style;
};
export const getGradientPreviewCellStyle = (cellValue, styledColumn, api, rowNode) => {
const gradientStyle = styledColumn.GradientStyle;
if (!gradientStyle || !hasGradientRangesConfigured(gradientStyle)) {
return {};
}
if (isZeroCentredGradientStyle(gradientStyle)) {
return getZeroCentredGradientPreviewCellStyle(cellValue, gradientStyle);
}
const column = styledColumn.ColumnId
? api.columnApi.getColumnWithColumnId(styledColumn.ColumnId)
: undefined;
if (!column) {
return {};
}
const node = rowNode ?? api.gridApi?.getRowNodeForIndex(0) ?? null;
const internalApi = api.styledColumnApi.internalApi;
let style = {};
const min = internalApi.getNumericStyleMinValue(styledColumn, column, node, cellValue);
const max = internalApi.getNumericStyleMaxValue(styledColumn, column, node, cellValue);
let cellBackColor;
let reverseGradient = false;
if (gradientStyle.ColumnComparison) {
cellBackColor = gradientStyle.ColumnComparison.Color;
}
else {
const matchingRange = internalApi.findRangeForColumn(expandGradientCellRanges(gradientStyle), column, gradientStyleRangeLookupValueType(gradientStyle), cellValue);
if (matchingRange) {
cellBackColor = matchingRange.Color;
reverseGradient =
(!!gradientStyle.ZeroCentred && isNegativeZeroCentredBand(matchingRange)) ||
!!matchingRange.ReverseGradient;
}
}
const minAlphaBound = gradientStyle.MinAlpha ?? DEFAULT_GRADIENT_MIN_ALPHA;
const maxAlphaBound = gradientStyle.MaxAlpha ?? DEFAULT_GRADIENT_MAX_ALPHA;
const lo = Math.min(clamp(minAlphaBound, 0, 1), clamp(maxAlphaBound, 0, 1));
const hi = Math.max(clamp(minAlphaBound, 0, 1), clamp(maxAlphaBound, 0, 1));
const span = Math.abs(max - min);
let t = span === 0 ? 0.5 : (Number(cellValue) - min) / span;
t = clamp(t, 0, 1);
if (reverseGradient) {
t = 1 - t;
}
const alpha = Number((lo + t * (hi - lo)).toPrecision(4));
if (cellBackColor) {
const preparedColor = getVariableColor(cellBackColor);
const bg = tinycolor(preparedColor).setAlpha(alpha);
style.backgroundColor = bg.toRgbString();
if (gradientStyle.AutoContrastText) {
style.color = getAutoContrastTextColor(preparedColor, alpha);
}
}
const autoContrastColor = gradientStyle.AutoContrastText && style.color ? style.color : undefined;
if (gradientStyle.Font) {
style = { ...style, ...convertAdaptableStyleToCSS(gradientStyle.Font) };
}
if (autoContrastColor) {
style.color = autoContrastColor;
}
if (gradientStyle.Font?.Alignment && gradientStyle.Font.Alignment !== 'Default') {
style.textAlign = gradientStyle.Font.Alignment.toLowerCase();
}
return style;
};
export function expandGradientCellRanges(gs) {
if (gs.ZeroCentred) {
return [
{ Min: 'Col-Min', Max: 0, Color: gs.ZeroCentred.NegativeColor },
{ Min: 0, Max: 'Col-Max', Color: gs.ZeroCentred.PositiveColor },
];
}
return gs.CellRanges;
}
export function gradientStyleRangeLookupValueType(gs) {
return gs.ZeroCentred ? 'Number' : gs.RangeValueType ?? 'Number';
}
export function isNegativeZeroCentredBand(range) {
return range.Min === 'Col-Min' && range.Max === 0;
}