UNPKG

highcharts

Version:
202 lines (201 loc) 6.7 kB
/* * * * (c) 2010-2026 Highsoft AS * Author: Torstein Honsi * * A commercial license may be required depending on use. * See www.highcharts.com/license * * * */ 'use strict'; import Color from './Color.js'; import H from '../Globals.js'; const { charts } = H; import PaletteDefaults from './PaletteDefaults.js'; import { diffObjects, extend, isString, objectEach, merge } from '../../Shared/Utilities.js'; /* * * * Constants * * */ const defaultColors = [ '#2caffe', '#544fc5', '#00e272', '#fe6a35', '#6b8abc', '#d568fb', '#2ee0ca', '#fa4b42', '#feb56a', '#91e8e1' ]; const defaultDarkOverrideColors = [ null, // Use the same color for the first item in dark mode '#00e272', '#efdf00' ]; /* * * Build the text content for the style tag */ const getStyles = (specifier, cssVars) => { const reduceToCSS = ( // For browsers that don't support color-scheme and light-dark, we need // to first set the CSS variables as the light scheme. supportsColorScheme) => { let css = ''; objectEach(cssVars.light, (value, key) => { css += supportsColorScheme && value !== cssVars.dark[key] ? ` ${key}: light-dark(${value}, ${cssVars.dark[key]});\n` : ` ${key}: ${value};\n`; }); return css; }; const legacy = reduceToCSS(), lightDark = reduceToCSS(true); const css = `${specifier || ':root'} { ${legacy} } @supports (color: light-dark(#fff, #000)) { ${specifier || ':root'} { ${lightDark} } } .highcharts-container { color-scheme: light dark; } .highcharts-light .highcharts-container { color-scheme: light; } .highcharts-dark .highcharts-container { color-scheme: dark; }`; return css; }; /* * * * Class * * */ /* eslint-disable valid-jsdoc */ /** * A Palette class holding the palette colors and lifecycle methods for each * chart. * * @class * @name Highcharts.Palette * * @param {Highcharts.Chart} chart * The chart instance * @param {Highcharts.PaletteOptions} options * Palette options */ export default class Palette { /* * * * Constructor * * */ constructor(renderer, options) { this.defaultOptions = PaletteDefaults; this.options = merge(PaletteDefaults); this.cssVars = { light: {}, dark: {} }; this.renderer = renderer; this.update(options); } /** * Inject the CSS for the palette into the document head or chart * container. * * @internal * * @param {Highcharts.PaletteOptions} options * Palette options */ injectCSS(options) { const { cssVars, renderer } = this, hasSpecificPalette = Object.keys(diffObjects(options, this.defaultOptions)).length > 0, colorCount = Math.max(options.colors?.length || 0, defaultColors.length); let colorScheme = 'light'; const addKebab = (color, key) => { if (isString(color)) { // Kebab-case the key. Sequences of numbers should be kept // but with a preceding dash. key = key .replace(/([0-9]+)/g, '-$1') .replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`); cssVars[colorScheme][`--highcharts-${key}`] = color; } }; for (const cScheme of ['light', 'dark']) { const paletteColors = options[cScheme] || {}, interpolated = {}, neutralColor = new Color(paletteColors?.neutralColor || ''), backgroundColor = new Color(paletteColors?.backgroundColor || ''), highlightColor = new Color(paletteColors?.highlightColor || ''); colorScheme = cScheme; // Interpolate keys [3, 5, 10, 20, 40, 60, 80, 100].forEach((fraction) => { interpolated[`neutralColor${fraction}`] = backgroundColor .tweenTo(neutralColor, fraction / 100); interpolated[`highlightColor${fraction}`] = backgroundColor .tweenTo(highlightColor, fraction / 100); }); // Extended data colors per scheme for (let i = 0; i < colorCount; i++) { const color = // 1. priority to scheme-specific colors paletteColors?.colors?.[i] || // 2. then fall back to palette-level colors options.colors?.[i] || // 3. then fall back to dark default colors (cScheme === 'dark' ? defaultDarkOverrideColors : [])?.[i] || // 4. finally, use default colors defaultColors[i]; addKebab(color || '#888a', `color${i}`); } // The rest are stored as named properties objectEach(paletteColors, addKebab); objectEach(interpolated, addKebab); // Default to the light scheme, the dark scheme doesn't define // all colors. if (cScheme === 'light') { extend(cssVars.dark, cssVars.light); } } // Add a style tag to the chart renderer box const defs = renderer.defs.element, specifier = hasSpecificPalette ? `*[data-highcharts-chart="${renderer.chartIndex}"]` : '', style = defs .querySelector('style.highcharts-palette') || renderer.box.ownerDocument.createElement('style'); if (!style.parentNode) { style.nonce = 'highcharts'; style.className = 'highcharts-palette'; defs.appendChild(style); } style.textContent = getStyles(specifier, cssVars); } /** * Update the palette with new options. May be called directly as * `chart.palette.update()` or indirectly from `chart.update({ palette })`. * * @function Highcharts.Palette#update * * @param {Highcharts.PaletteOptions} options * New palette options */ update(options) { const renderTo = this.renderer.box.parentElement, chart = charts?.[this.renderer.chartIndex]; options = merge(true, this.options, options); if (chart) { chart.options.palette = options; } if (options.injectCSS !== false) { this.injectCSS(options); } if (renderTo) { if (isString(options.colorScheme) && ['light', 'dark', 'inherit'].includes(options.colorScheme)) { renderTo.style.colorScheme = options.colorScheme; } else { renderTo.style.removeProperty('color-scheme'); } } } }