@jupyter/web-components
Version:
A component library for building extensions in Jupyter frontends.
105 lines (104 loc) • 3.97 kB
JavaScript
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);
})));
}
}