UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

334 lines (295 loc) • 9.04 kB
// (C) 2007-2020 GoodData Corporation import isEmpty = require("lodash/isEmpty"); import isEqual = require("lodash/isEqual"); import isString = require("lodash/isString"); import range = require("lodash/range"); import { AFM, Execution } from "@gooddata/typings"; import { IColor, IColorItem } from "@gooddata/gooddata-js"; import { IColorPalette, IColorPaletteItem, IColorMapping } from "../../../interfaces/Config"; import { IHeaderPredicate, IHeaderPredicateContext } from "../../../interfaces/HeaderPredicate"; import { IMappingHeader, isMappingHeaderAttributeItem, isMappingHeaderAttribute, } from "../../../interfaces/MappingHeader"; import { getMappingHeaderLocalIdentifier } from "../../../helpers/mappingHeader"; export const WHITE = "rgb(255, 255, 255)"; export const BLACK = "rgb(0, 0, 0)"; export const GRAY = "rgb(201, 213, 223)"; export const AXIS_LINE_COLOR = "#d5d5d5"; export const TRANSPARENT = "transparent"; export const DEFAULT_COLORS = [ "rgb(20,178,226)", "rgb(0,193,141)", "rgb(229,77,66)", "rgb(241,134,0)", "rgb(171,85,163)", "rgb(244,213,33)", "rgb(148,161,174)", "rgb(107,191,216)", "rgb(181,136,177)", "rgb(238,135,128)", "rgb(241,171,84)", "rgb(133,209,188)", "rgb(41,117,170)", "rgb(4,140,103)", "rgb(181,60,51)", "rgb(163,101,46)", "rgb(140,57,132)", "rgb(136,219,244)", "rgb(189,234,222)", "rgb(239,197,194)", ]; export const DEFAULT_COLOR_PALETTE = [ { guid: "1", fill: { r: 20, g: 178, b: 226 }, }, { guid: "2", fill: { r: 0, g: 193, b: 141 }, }, { guid: "3", fill: { r: 229, g: 77, b: 66 }, }, { guid: "4", fill: { r: 241, g: 134, b: 0 }, }, { guid: "5", fill: { r: 171, g: 85, b: 163 }, }, { guid: "6", fill: { r: 244, g: 213, b: 33 }, }, { guid: "7", fill: { r: 148, g: 161, b: 174 }, }, { guid: "8", fill: { r: 107, g: 191, b: 216 }, }, { guid: "9", fill: { r: 181, g: 136, b: 177 }, }, { guid: "10", fill: { r: 238, g: 135, b: 128 }, }, { guid: "11", fill: { r: 241, g: 171, b: 84 }, }, { guid: "12", fill: { r: 133, g: 209, b: 188 }, }, { guid: "13", fill: { r: 41, g: 117, b: 170 }, }, { guid: "14", fill: { r: 4, g: 140, b: 103 }, }, { guid: "15", fill: { r: 181, g: 60, b: 51 }, }, { guid: "16", fill: { r: 163, g: 101, b: 46 }, }, { guid: "17", fill: { r: 140, g: 57, b: 132 }, }, { guid: "18", fill: { r: 136, g: 219, b: 244 }, }, { guid: "19", fill: { r: 189, g: 234, b: 222 }, }, { guid: "20", fill: { r: 239, g: 197, b: 194 }, }, ]; export const HEATMAP_BLUE_COLOR_PALETTE = [ "rgb(255,255,255)", "rgb(197,236,248)", "rgb(138,217,241)", "rgb(79,198,234)", "rgb(20,178,226)", "rgb(22,151,192)", "rgb(0,110,145)", ]; export const DEFAULT_HEATMAP_BLUE_COLOR: IColor = { r: 0, g: 110, b: 145, }; export const DEFAULT_BULLET_GRAY_COLOR: IColor = { r: 217, g: 220, b: 226, }; function lighter(color: number, percent: number) { const t = percent < 0 ? 0 : 255; const p = Math.abs(percent); return Math.round((t - color) * p) + color; } function formatColor(red: number, green: number, blue: number, opacity: number = 1): string { if (opacity === 1) { return `rgb(${red},${green},${blue})`; } return `rgba(${red},${green},${blue},${opacity})`; } export function parseRGBColorCode(color: string) { const f = color.split(","); const R = parseInt(f[0].slice(4), 10); const G = parseInt(f[1], 10); const B = parseInt(f[2], 10); return { R, G, B }; } /** * Source: * http://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors */ export function getLighterColor(color: string, percent: number) { const { R, G, B } = parseRGBColorCode(color); return formatColor(lighter(R, percent), lighter(G, percent), lighter(B, percent)); } export function getLighterColorFromRGB(color: IColor, percent: number) { const { r, g, b } = color; return { r: lighter(r, percent), g: lighter(g, percent), b: lighter(b, percent), }; } export function normalizeColorToRGB(color: string) { const hexPattern = /#([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})/i; return color.replace(hexPattern, (_prefix: string, r: string, g: string, b: string) => { return `rgb(${[r, g, b].map(value => parseInt(value, 16).toString(10)).join(", ")})`; }); } export function getColorPaletteFromColors(colors: string[]): IColorPalette { try { return colors.map((color: string, index: number) => { const { R, G, B } = parseRGBColorCode(normalizeColorToRGB(color)); if (isNaN(R) || isNaN(G) || isNaN(B)) { throw Error; } return { guid: String(index), fill: { r: R, g: G, b: B, }, }; }); } catch (_ignored) { return DEFAULT_COLOR_PALETTE; } } export function getRgbString(color: IColorPaletteItem): string { return `rgb(${color.fill.r},${color.fill.g},${color.fill.b})`; } export function getValidColorPalette(colors?: string[], colorPalette?: IColorPalette) { return isEmpty(colorPalette) ? isEmpty(colors) ? DEFAULT_COLOR_PALETTE : getColorPaletteFromColors(colors) : colorPalette; } export function isCustomPalette(palette: IColorPalette) { return !isEqual(palette, DEFAULT_COLOR_PALETTE); } export function getColorFromMapping( mappingHeader: IMappingHeader, colorMapping: IColorMapping[], executionResponse: Execution.IExecutionResponse, afm: AFM.IAfm, ): IColorItem { if (!colorMapping) { return undefined; } const mapping = colorMapping.find(item => item.predicate(mappingHeader, { afm, executionResponse })); return mapping && mapping.color; } export function getColorByGuid(colorPalette: IColorPalette, guid: string, index: number) { const inPalette = colorPalette.find((item: any) => item.guid === guid); return inPalette ? inPalette.fill : colorPalette[index % colorPalette.length].fill; } export function getRgbStringFromRGB(color: IColor) { return `rgb(${color.r},${color.g},${color.b})`; } export function getColorMappingPredicate(idOrUri: string): IHeaderPredicate { return (header: IMappingHeader, _context: IHeaderPredicateContext): boolean => { if (isMappingHeaderAttributeItem(header)) { return idOrUri ? idOrUri === header.attributeHeaderItem.uri : false; } if (isMappingHeaderAttribute(header)) { return idOrUri ? idOrUri === header.attributeHeader.uri : false; } const headerLocalIdentifier = getMappingHeaderLocalIdentifier(header); return headerLocalIdentifier ? headerLocalIdentifier === idOrUri : false; }; } function getCalculatedChannel(channel: number, index: number, step: number): number { return Math.trunc(channel + index * step); } function getCalculatedColors( count: number, channels: number[], steps: number[], opacity: number = 1, ): string[] { return range(1, count).map( (index: number): string => formatColor( getCalculatedChannel(channels[0], index, steps[0]), getCalculatedChannel(channels[1], index, steps[1]), getCalculatedChannel(channels[2], index, steps[2]), opacity, ), ); } function getRGBColorCode(color: string | IColor): IColor { if (isString(color)) { const { R: r, G: g, B: b } = parseRGBColorCode(color); return { r, g, b, }; } return color; } export function getColorPalette(baseColor: string | IColor, opacity: number = 1): string[] { const colorItemsCount = 6; const { r, g, b } = getRGBColorCode(baseColor); const channels = [r, g, b]; const steps = channels.map(channel => (255 - channel) / colorItemsCount); const generatedColors = getCalculatedColors(colorItemsCount, channels, steps, opacity); return [...generatedColors.reverse(), formatColor(r, g, b, opacity)]; } export function rgbToRgba(color: string, opacity: number = 1): string { const { R, G, B } = parseRGBColorCode(color); return formatColor(R, G, B, opacity); } // For re-exporting in index.ts // Create object here since TSC can't reexport external types used by getColorMappingPredicate export default { getColorByGuid, getColorMappingPredicate, };