UNPKG

@jupyter/web-components

Version:

A component library for building extensions in Jupyter frontends.

105 lines (104 loc) 3.97 kB
import { clamp, ColorRGBA64, ComponentStateColorPalette, parseColorHexRGB } from '@microsoft/fast-colors'; import { isSwatchRGB, SwatchRGB } from './swatch.js'; import { binarySearch } from './utilities/binary-search.js'; import { directionByIsDark } from './utilities/direction-by-is-dark.js'; import { contrast } from './utilities/relative-luminance.js'; function create(rOrSource, g, b) { if (typeof rOrSource === 'number') { /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ return PaletteRGB.from(SwatchRGB.create(rOrSource, g, b)); } else { return PaletteRGB.from(rOrSource); } } function from(source) { return isSwatchRGB(source) ? PaletteRGBImpl.from(source) : PaletteRGBImpl.from(SwatchRGB.create(source.r, source.g, source.b)); } /** @public */ export const PaletteRGB = Object.freeze({ create, from }); /** * A {@link Palette} representing RGB swatch values. * @public */ class PaletteRGBImpl { /** * * @param source - The source color for the palette * @param swatches - All swatches in the palette */ constructor(source, swatches) { this.closestIndexCache = new Map(); this.source = source; this.swatches = swatches; this.reversedSwatches = Object.freeze([...this.swatches].reverse()); this.lastIndex = this.swatches.length - 1; } /** * {@inheritdoc Palette.colorContrast} */ colorContrast(reference, contrastTarget, initialSearchIndex, direction) { if (initialSearchIndex === undefined) { initialSearchIndex = this.closestIndexOf(reference); } let source = this.swatches; const endSearchIndex = this.lastIndex; let startSearchIndex = initialSearchIndex; if (direction === undefined) { direction = directionByIsDark(reference); } const condition = (value) => contrast(reference, value) >= contrastTarget; if (direction === -1) { source = this.reversedSwatches; startSearchIndex = endSearchIndex - startSearchIndex; } return binarySearch(source, condition, startSearchIndex, endSearchIndex); } /** * {@inheritdoc Palette.get} */ get(index) { return (this.swatches[index] || this.swatches[clamp(index, 0, this.lastIndex)]); } /** * {@inheritdoc Palette.closestIndexOf} */ closestIndexOf(reference) { if (this.closestIndexCache.has(reference.relativeLuminance)) { /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ return this.closestIndexCache.get(reference.relativeLuminance); } let index = this.swatches.indexOf(reference); if (index !== -1) { this.closestIndexCache.set(reference.relativeLuminance, index); return index; } const closest = this.swatches.reduce((previous, next) => Math.abs(next.relativeLuminance - reference.relativeLuminance) < Math.abs(previous.relativeLuminance - reference.relativeLuminance) ? next : previous); index = this.swatches.indexOf(closest); this.closestIndexCache.set(reference.relativeLuminance, index); return index; } /** * Create a color palette from a provided swatch * @param source - The source swatch to create a palette from * @returns */ static from(source) { return new PaletteRGBImpl(source, Object.freeze(new ComponentStateColorPalette({ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ baseColor: ColorRGBA64.fromObject(source) }).palette.map(x => { /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ const _x = parseColorHexRGB(x.toStringHexRGB()); return SwatchRGB.create(_x.r, _x.g, _x.b); }))); } }