UNPKG

@atlaskit/adf-schema

Version:

Shared package that contains the ADF-schema (json) and ProseMirror node/mark specs

128 lines (124 loc) 3.79 kB
// https://en.wikipedia.org/wiki/HCL_color_space // https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space // https://en.wikipedia.org/wiki/CIELAB_color_space const clamp = (i, min, max) => Math.round(Math.min(Math.max(i, min), max)); const expandShorthandHex = input => input.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => r + r + g + g + b + b); const rgbFromHex = input => { const fullHex = expandShorthandHex(input); const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(fullHex); return result === null ? null : { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) }; }; const rgbToHex = ({ r, g, b }) => { const convertComponent = c => { const cBase16 = c.toString(16); return cBase16.length === 1 ? `0${cBase16}` : cBase16; }; return `#${convertComponent(r)}${convertComponent(g)}${convertComponent(b)}`; }; const rgbToXyz = rgb => { const convertRgbComponent = c => c > 0.04045 ? Math.pow((c + 0.055) / 1.055, 2.4) : c / 12.92; const convertXyzComponent = c => c > 0.008856452 ? Math.pow(c, 1 / 3) : c / 0.12841855 + 0.137931034; const r = convertRgbComponent(rgb.r / 255); const g = convertRgbComponent(rgb.g / 255); const b = convertRgbComponent(rgb.b / 255); return { x: convertXyzComponent((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / 0.95047), y: convertXyzComponent(0.2126729 * r + 0.7151522 * g + 0.072175 * b), z: convertXyzComponent((0.0193339 * r + 0.119192 * g + 0.9503041 * b) / 1.08883) }; }; const xyzToLab = ({ x, y, z }) => ({ l: Math.max(116 * y - 16, 0), a: 500 * (x - y), b: 200 * (y - z) }); const labToLch = ({ l, a, b }) => { let h = (Math.atan2(b, a) * (180 / Math.PI) + 360) % 360; const c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); if (Math.round(c * 10000) === 0) h = Number.NaN; return { l, c, h }; }; const lchToLab = ({ l, c, h }) => { const convertH = Number.isNaN(h) ? 0 : h * (Math.PI / 180); return { l, a: Math.cos(convertH) * c, b: Math.sin(convertH) * c }; }; const labToXyz = ({ l, a, b }) => { const convertComponent = c => c > 0.206896552 ? Math.pow(c, 3) : 0.12841855 * (c - 0.137931034); const y = (l + 16) / 116; const x = a / 500 + y; const z = y - b / 200; return { x: convertComponent(x) * 0.95047, y: convertComponent(y), z: convertComponent(z) * 1.08883 }; }; const xyzToRgb = ({ x, y, z }) => { const convertComponent = c => 255 * (c <= 0.00304 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055); return { r: clamp(convertComponent(3.2404542 * x - 1.5371385 * y - 0.4985314 * z), 0, 255), g: clamp(convertComponent(-0.969266 * x + 1.8760108 * y + 0.041556 * z), 0, 255), b: clamp(convertComponent(0.0556434 * x - 0.2040259 * y + 1.0572252 * z), 0, 255) }; }; const rgbToLch = rgb => labToLch(xyzToLab(rgbToXyz(rgb))); const lchToRgb = lch => xyzToRgb(labToXyz(lchToLab(lch))); export const clampLightness = (color, newPercent) => { const rgb = rgbFromHex(color); if (rgb === null) { return color; } // LCH (rather than HSL) gives the best results here as the L component in LCH is based on human color perception const lch = rgbToLch(rgb); lch.l = clamp(newPercent, 0, 100); return rgbToHex(lchToRgb(lch)); }; const getLightness = color => { const rgb = rgbFromHex(color); if (rgb === null) { return 0; } const lch = rgbToLch(rgb); return lch.l; }; export const getDarkModeLCHColor = currentBackgroundColor => { const lightness = getLightness(currentBackgroundColor); const newLightness = Math.abs(100 - lightness); return clampLightness(currentBackgroundColor, newLightness).toUpperCase(); };