@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
383 lines (337 loc) • 11.9 kB
JavaScript
import { resolveElementColor } from "@progress/kendo-drawing";
const SERIES_COLORS = 30;
const seriesVar = '--kendo-chart-series-';
const elementStyles = element => element.ownerDocument.defaultView.getComputedStyle(element);
const getProp = (element, prop) => elementStyles(element).getPropertyValue(prop);
const getNumberProp = (element, prop) => parseFloat(elementStyles(element).getPropertyValue(prop));
let colorCache;
const getColorProp = (element, prop) => {
let value = elementStyles(element).getPropertyValue(prop);
if (!value) {
return undefined;
}
if (!colorCache) {
colorCache = new Map();
}
let color;
if (colorCache.has(value)) {
color = colorCache.get(value);
} else {
color = resolveElementColor(element, prop);
colorCache.set(value, color);
}
return color;
};
const getFont = (element, weightProp, sizeProp, familyProp) => {
const styles = elementStyles(element);
return [styles.getPropertyValue(weightProp), styles.getPropertyValue(sizeProp), styles.getPropertyValue(familyProp) || styles.fontFamily].join(" ");
};
const seriesColorsVars = {};
for (let i = 1; i <= SERIES_COLORS; i++) {
seriesColorsVars[`series-color-${i}`] = `${seriesVar}${i}`;
}
const seriesColorByIndex = (element, index) => {
const propName = `${seriesVar}${index}`;
return getColorProp(element, propName);
};
const getSeriesColors = (element) => {
const result = [];
let count = 0;
while (count++ <= SERIES_COLORS) {
const seriesColor = seriesColorByIndex(element, count);
if (seriesColor === undefined) {
break;
}
result.push(seriesColor);
}
return result;
};
const parseToMS = (value) => {
// Handle CSS time values (e.g., "200ms", "0.5s", ".3s")
if (typeof value === 'string' && value.endsWith('ms')) {
return parseFloat(value);
} else if (typeof value === 'string' && value.endsWith('s')) {
return parseFloat(value) * 1000;
}
return parseFloat(value);
};
function parseCubicBezier(input) {
const s = (input || "").trim();
const m = s.match(
/cubic-bezier\s*\(\s*([+-]?\d*\.?\d+)\s*,\s*([+-]?\d*\.?\d+)\s*,\s*([+-]?\d*\.?\d+)\s*,\s*([+-]?\d*\.?\d+)\s*\)\s*/i
);
return m ? [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])] : null;
}
// -----------------------------------------------------------------------------
// CSS Variable Groups (shared + component specific)
// -----------------------------------------------------------------------------
export const sharedVariables = {
text: "--kendo-chart-text",
chartBg: "--kendo-chart-bg",
fontWeight: "--kendo-font-weight",
fontFamily: "--kendo-font-family",
};
// Chart-specific variables (in addition to shared)
const chartVariables = {
majorLines: "--kendo-chart-major-lines",
minorLines: "--kendo-chart-minor-lines",
areaOpacity: "--kendo-chart-area-opacity",
surface: "--kendo-color-surface",
primaryBg: "--kendo-chart-primary-bg",
crosshairBg: "--kendo-chart-crosshair-bg",
axisLabelFontSize: "--kendo-chart-label-font-size",
axisLabelFontWeight: "--kendo-font-weight",
axisLabelFontFamily: "--kendo-font-family",
chartFontSize: "--kendo-chart-font-size", // used by defaultFont / legend labels
paneTitleFontSize: "--kendo-chart-pane-title-font-size",
paneTitleFontWeight: "--kendo-chart-pane-title-font-weight",
titleFontSize: "--kendo-chart-title-font-size",
titleFontWeight: "--kendo-font-weight",
errorBarsBg: "--kendo-chart-error-bars-bg",
areaInactiveOpacity: "--kendo-chart-area-inactive-opacity",
lineInactiveOpacity: "--kendo-chart-line-inactive-opacity",
bulletTarget: "--kendo-chart-bullet-target",
notesBg: "--kendo-chart-notes-bg",
notesBorder: "--kendo-chart-notes-border",
notesLines: "--kendo-chart-notes-lines",
inactive: "--kendo-chart-inactive",
initialAnimationDuration: '--kendo-duration-steady',
fadeInAnimationDuration: '--kendo-duration-rapid',
elasticEasing: '--kendo-easing-stretchy',
linearEasing: '--kendo-easing-linear',
swingEasing: '--kendo-easing-standard',
};
// Sankey-specific variables (in addition to shared)
const sankeyVariables = {
linksColor: "--kendo-color-subtle",
};
// Gauge-specific variables (in addition to shared)
const gaugeVariables = {
gaugePointer: "--kendo-chart-gauge-pointer",
gaugeTrack: "--kendo-chart-gauge-track",
};
// Font & color helpers using variable objects (avoid raw CSS strings)
const defaultFont = element => getFont(element, sharedVariables.fontWeight, chartVariables.chartFontSize, sharedVariables.fontFamily);
const paneTitleFont = (element) => getFont(element, chartVariables.paneTitleFontWeight, chartVariables.paneTitleFontSize, sharedVariables.fontFamily);
const normalTextColor = (element) => getColorProp(element, sharedVariables.text);
const title = (element) => ({
color: normalTextColor(element),
font: getFont(element, sharedVariables.fontWeight, chartVariables.titleFontSize, sharedVariables.fontFamily),
});
const sankeyLegend = (element) => {
const textColorNormal = normalTextColor(element);
return {
labels: {
color: textColorNormal,
font: defaultFont(element),
},
title: {
color: textColorNormal,
}
};
};
const chartLegend = (element) => {
const inactiveColor = getColorProp(element, chartVariables.inactive);
return Object.assign({}, {inactiveItems: {
labels: {
color: inactiveColor,
},
markers: {
color: inactiveColor,
},
}},
sankeyLegend(element));
};
// Export composed variable objects
export const chartCSSVariables = Object.assign({}, sharedVariables, chartVariables, seriesColorsVars);
export const sankeyCSSVariables = Object.assign({}, sharedVariables, sankeyVariables, seriesColorsVars);
export const gaugeCSSVariables = Object.assign({}, sharedVariables, gaugeVariables);
export const gaugeTheme = (element) => {
const vars = gaugeCSSVariables;
const textColorNormal = normalTextColor(element);
return {
pointer: {
color: getColorProp(element, vars.gaugePointer)
},
scale: {
labels: {
color: textColorNormal
},
rangePlaceholderColor: getColorProp(element, vars.gaugeTrack),
minorTicks: {
color: textColorNormal
},
majorTicks: {
color: textColorNormal
},
line: {
color: textColorNormal
}
}
};
};
export const sankeyTheme = (element) => {
const vars = sankeyCSSVariables;
return {
labels: {
color: normalTextColor(element),
font: defaultFont(element),
stroke: {
color: getColorProp(element, vars.chartBg),
},
},
links: {
color: getColorProp(element, vars.linksColor),
},
nodeColors: getSeriesColors(element),
title: title(element),
legend: sankeyLegend(element),
};
};
const notes = (element) => ({
icon: {
background: getColorProp(element, chartVariables.notesBg),
border: {
color: getColorProp(element, chartVariables.notesBorder),
},
},
line: {
color: getColorProp(element, chartVariables.notesLines),
},
label: {
font: defaultFont(element),
},
});
// (chartCSSVariables already exported above via composition)
export const chartTheme = (element) => {
const vars = chartCSSVariables;
const majorLines = getColorProp(element, vars.majorLines);
const normalTextColor = getColorProp(element, vars.text);
const axisLabelFont = getFont(element, vars.axisLabelFontWeight, vars.axisLabelFontSize, vars.axisLabelFontFamily);
const chartBg = getColorProp(element, vars.chartBg);
const notesProps = notes(element);
const areaOpacity = getNumberProp(element, vars.areaOpacity);
const surfaceColor = getColorProp(element, vars.surface);
const primaryBg = getColorProp(element, vars.primaryBg);
const boxPlot = () => ({
downColor: majorLines,
mean: {
color: surfaceColor,
},
median: {
color: surfaceColor,
},
whiskers: {
color: primaryBg,
},
});
const waterfall = () => ({
line: {
color: majorLines,
},
});
const area = () => ({
opacity: areaOpacity,
highlight: {
inactiveOpacity: getNumberProp(element, vars.areaInactiveOpacity),
},
});
const line = () => ({
highlight: {
inactiveOpacity: getNumberProp(element, vars.lineInactiveOpacity),
},
});
const bullet = () => ({
target: {
color: normalTextColor,
},
});
const motion = {
steady: parseToMS(getProp(element, vars.initialAnimationDuration)) || 600,
rapid: parseToMS(getProp(element, vars.fadeInAnimationDuration)) || 200,
easeOutElastic: parseCubicBezier(getProp(element, vars.elasticEasing)) || [0.07, 1.81, 0.3, 0.81],
linear: parseCubicBezier(getProp(element, vars.linearEasing)) || [0, 0, 1, 1],
swing: parseCubicBezier(getProp(element, vars.swingEasing)) || [0.42, 0, 0.58, 1]
};
return {
axisDefaults: {
crosshair: {
color: getColorProp(element, vars.crosshairBg),
},
labels: {
color: normalTextColor,
font: axisLabelFont,
},
line: {
color: majorLines,
},
majorGridLines: {
color: majorLines,
},
minorGridLines: {
color: getColorProp(element, vars.minorLines),
},
notes: structuredClone(notesProps),
title: {
color: normalTextColor,
font: defaultFont(element),
}
},
categoryAxis: {
highlight: {
color: "#8C9FD9",
opacity: 0.15
}
},
chartArea: {
background: chartBg,
},
legend: chartLegend(element),
seriesColors: getSeriesColors(element),
seriesDefaults: {
area: area(),
verticalArea: area(),
radarArea: area(),
boxPlot: boxPlot(),
verticalBoxPlot: boxPlot(),
bullet: bullet(),
verticalBullet: bullet(),
horizontalWaterfall: waterfall(),
waterfall: waterfall(),
line: line(),
verticalLine: line(),
candlestick: {
downColor: normalTextColor,
line: {
color: normalTextColor,
},
},
errorBars: {
color: getColorProp(element, vars.errorBarsBg),
},
icon: {
border: {
color: majorLines,
},
},
labels: {
background: chartBg,
color: normalTextColor,
font: axisLabelFont,
opacity: areaOpacity,
},
notes: structuredClone(notesProps),
},
subtitle: {
color: normalTextColor,
font: paneTitleFont(element),
},
title: title(element),
paneDefaults: {
title: {
font: paneTitleFont(element),
}
},
motion
};
};