// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ Copyright (c) 2017, the Perspective Authors. ┃ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ // ┃ This file is part of the Perspective library, distributed under the terms ┃ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ import * as d3 from "d3"; import { groupFromKey } from "./seriesKey"; import { getValuesByColumn } from "../data/utils"; import { Settings } from "../types"; export function seriesColors( settings: Settings, ): d3.ScaleOrdinal | null { const col = settings.data && settings.data.length > 0 ? settings.data[0] : {}; const domain = Object.keys(col).filter((k) => k !== "__ROW_PATH__"); return colorScale().settings(settings).domain(domain)(); } export function seriesColorsFromColumn( settings: Settings, column, ): d3.ScaleOrdinal | null { const data = getValuesByColumn(settings, column); const domain = [...new Set(data)].sort(); return colorScale().settings(settings).domain(domain)(); } export function seriesColorsFromDistinct( settings: Settings, data, ): d3.ScaleOrdinal | null { let domain = [...new Set(data)]; return colorScale().settings(settings).domain(domain)(); } export function seriesColorsFromGroups( settings: Settings, ): d3.ScaleOrdinal | null { const col = settings.data[0] ?? {}; const inner = Object.keys(col) .filter((k) => k !== "__ROW_PATH__") .map((k) => groupFromKey(k)); const domain = [...new Set(inner)]; return colorScale().settings(settings).domain(domain)(); } type ColorScaleSettings = { colorStyles?: any; }; type ColorScaleMapFunc = (d: any) => string; interface ColorScale { (): d3.ScaleOrdinal | null; settings(): Settings; settings(nextSettings: Settings): ColorScale; domain(): any[]; domain(nextDomain: any[]): ColorScale; defaultColors(): string[]; defaultColors(nextDefaultColors: string[]): ColorScale; mapFunction(): ColorScaleMapFunc; mapFunction(nextMapFunction: ColorScaleMapFunc): ColorScale; } export function colorScale(): ColorScale { let domain = null; let defaultColors: string[] | null = null; let settings: ColorScaleSettings = {}; let mapFunction = (d) => withOpacity(d, settings.colorStyles && settings.colorStyles.opacity); const colors: ColorScale = (): d3.ScaleOrdinal | null => { const styles = settings.colorStyles; const defaults = defaultColors || [styles.series]; if (defaults || domain.length > 1) { const range = domain.length > 1 ? styles.scheme : defaults; return d3.scaleOrdinal(range.map(mapFunction)).domain(domain); // ordinal.js } return null; }; colors.domain = (...args: any[][]): any => { if (args.length === 0) { return domain; } domain = args[0]; return colors; }; colors.defaultColors = (...args: string[][]): any => { if (!args.length) { return defaultColors; } defaultColors = args[0]; return colors; }; colors.mapFunction = (...args: ColorScaleMapFunc[]): any => { if (!args.length) { return mapFunction; } mapFunction = args[0]; return colors; }; colors.settings = (...args: Settings[]): any => { if (!args.length) { return settings; } settings = args[0]; return colors; }; return colors as ColorScale; } export function withoutOpacity(color) { return setOpacity(1)(color); } export function withOpacity(color, opacity = 0.5) { return setOpacity(opacity)(color); } export function setOpacity(opacity) { return (color) => { const decoded = d3.color(color); if (decoded !== null && decoded !== undefined) { decoded.opacity = opacity; } return decoded + ""; }; }