UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

422 lines (421 loc) 12.2 kB
import { _ as _define_property } from '@swc/helpers/_/_define_property'; import { getBoundedValue } from '../functions/numbers.js'; let scratchBytes = new Uint8Array(4); let scratchUInt32 = new Uint32Array(scratchBytes.buffer); export const isRgbColor = (value) => 'string' != typeof value && 'r' in value && 'g' in value && 'b' in value; export const isHslColor = (value) => 'string' != typeof value && 'h' in value && 's' in value && 'l' in value; export const isHsvColor = (value) => 'string' != typeof value && 'h' in value && 's' in value && 'v' in value; export class ColorValue { static create(val) { if (!val) return ColorValue.fromTbgr(0); if (isRgbColor(val)) return ColorValue.fromRGB(val); if (isHslColor(val)) return ColorValue.fromHSL(val); if (isHsvColor(val)) return ColorValue.fromHSV(val); if ('string' == typeof val) return ColorValue.fromString(val, ColorValue.fromTbgr(0)); return ColorValue.fromTbgr(0); } toTbgr() { return this._tbgr; } static fromTbgr(tbgr) { return new ColorValue(tbgr); } static fromRgbt(red, green, blue, transparency) { return this.fromTbgr( this.computeTbgrFromComponents(red, green, blue, transparency), ); } static computeTbgrFromComponents(red, green, blue, transparency) { scratchBytes[0] = red; scratchBytes[1] = green; scratchBytes[2] = blue; scratchBytes[3] = transparency || 0; return scratchUInt32[0]; } static fromString(val, defaultColorIfNotParsed) { let [tbgr, hue] = this.computeTbgrFromString( val, defaultColorIfNotParsed?.toTbgr(), ); return new ColorValue(tbgr, hue); } static fromHSL(hsl) { let alpha = hsl.a ?? 1; return new ColorValue( this.computeTbgrFromHSL( hsl.h / 360, hsl.s / 100, hsl.l / 100, Math.round((1 - alpha) * 255), ), hsl.h, ); } static fromRGB(rgb) { let alpha = rgb.a ?? 1; return ColorValue.fromRgbt( rgb.r, rgb.g, rgb.b, Math.round((1 - alpha) * 255), ); } static fromHSV(hsv) { let alpha = hsv.a ?? 1; let transparency = Math.round((1 - alpha) * 255); if (!hsv.s || -1 === hsv.h) { let white = 0xff & Math.floor((255.0 * hsv.v) / 100.0 + 0.5 + 3.0e-14); return ColorValue.fromRgbt(white, white, white, 0); } let dhue = hsv.h, dsaturation = hsv.s, dvalue = hsv.v; if (360 === dhue) dhue = 0.0; dhue /= 60; let hueIntpart = Math.floor(dhue); let hueFractpart = dhue - hueIntpart; dvalue /= 100; dsaturation /= 100; let p = 0xff & Math.floor(dvalue * (1.0 - dsaturation) * 255.0 + 0.5); let q = 0xff & Math.floor(dvalue * (1.0 - dsaturation * hueFractpart) * 255.0 + 0.5); let t = 0xff & Math.floor( dvalue * (1.0 - dsaturation * (1.0 - hueFractpart)) * 255.0 + 0.5, ); let v = 0xff & Math.floor(255 * dvalue + 0.5); let r = 0, b = 0, g = 0; switch (hueIntpart) { case 0: r = v; g = t; b = p; break; case 1: (r = q), (g = v); b = p; break; case 2: (r = p), (g = v); b = t; break; case 3: (r = p), (g = q); b = v; break; case 4: (r = t), (g = p); b = v; break; case 5: (r = v), (g = p); b = q; break; } return new ColorValue( ColorValue.computeTbgrFromComponents(r, g, b, transparency), hsv.h, ); } static computeTbgrFromString(val, defaultColorIfNotParsed) { val = val.toLowerCase(); let m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec(val); if (m) { let color; let name = m[1]; let components = m[2]; let hasPercent = (str) => '%' === str[str.length - 1]; let floatOrPercent = (str) => { let v = parseFloat(str); return 255 * getBoundedValue(hasPercent(str) ? v / 100 : v, 0, 1); }; let intOrPercent = (str) => { let v = hasPercent(str) ? (parseFloat(str) / 100) * 255 : parseInt(str, 10); return getBoundedValue(v, 0, 255); }; switch (name) { case 'rgb': case 'rgba': color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components, ); if (color) return [ this.computeTbgrFromComponents( intOrPercent(color[1]), intOrPercent(color[2]), intOrPercent(color[3]), 'string' == typeof color[4] ? 255 - floatOrPercent(color[4]) : 0, ), void 0, ]; break; case 'hsl': case 'hsla': color = /^(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components, ); if (color) { let h = parseFloat(color[1]); let s = parseInt(color[2], 10) / 100; let l = parseInt(color[3], 10) / 100; let t = 'string' == typeof color[4] ? 255 - floatOrPercent(color[4]) : 0; return [this.computeTbgrFromHSL(h / 360, s, l, t), h]; } break; } } else if ((m = /^\#([A-Fa-f\d]+)$/.exec(val))) { let hex = m[1]; let size = hex.length; if (3 === size) return [ this.computeTbgrFromComponents( parseInt(hex.charAt(0) + hex.charAt(0), 16), parseInt(hex.charAt(1) + hex.charAt(1), 16), parseInt(hex.charAt(2) + hex.charAt(2), 16), 0, ), void 0, ]; if (6 === size) return [ this.computeTbgrFromComponents( parseInt(hex.charAt(0) + hex.charAt(1), 16), parseInt(hex.charAt(2) + hex.charAt(3), 16), parseInt(hex.charAt(4) + hex.charAt(5), 16), 0, ), void 0, ]; if (8 === size) return [ this.computeTbgrFromComponents( parseInt(hex.charAt(0) + hex.charAt(1), 16), parseInt(hex.charAt(2) + hex.charAt(3), 16), parseInt(hex.charAt(4) + hex.charAt(5), 16), 255 - parseInt(hex.charAt(6) + hex.charAt(7), 16), ), void 0, ]; } if (defaultColorIfNotParsed) return [defaultColorIfNotParsed, void 0]; throw new Error('unable to parse string into ColorValue'); } static getColors(tbgr) { scratchUInt32[0] = tbgr; return { b: scratchBytes[2], g: scratchBytes[1], r: scratchBytes[0], t: scratchBytes[3], }; } getRgb(includeAlpha) { scratchUInt32[0] = this._tbgr; if (includeAlpha) return ( (scratchBytes[0] << 24) + (scratchBytes[1] << 16) + (scratchBytes[2] << 8) + (255 - scratchBytes[3]) ); return (scratchBytes[0] << 16) + (scratchBytes[1] << 8) + scratchBytes[2]; } getAlpha() { return ColorValue.getAlpha(this._tbgr); } static getAlpha(tbgr) { scratchUInt32[0] = tbgr; return 255 - scratchBytes[3]; } toHexString(includeAlpha) { if (includeAlpha) { let value = this.getRgb(includeAlpha); if (value < 0) value = 0xffffffff + value + 1; return `#${`00000000${value.toString(16)}`.slice(-8)}`; } return `#${`000000${this.getRgb().toString(16)}`.slice(-6)}`; } static computeTbgrFromHSL(h, s, l, transparency = 0) { let torgb = (p1, q1, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p1 + (q1 - p1) * 6 * t; if (t < 0.5) return q1; if (t < 2 / 3) return p1 + (q1 - p1) * 6 * (2 / 3 - t); return p1; }; let hue2rgb = (p1, q1, t) => Math.round(255 * torgb(p1, q1, t)); let modulo = (n, m) => ((n % m) + m) % m; h = modulo(h, 1); s = getBoundedValue(s, 0, 1); l = getBoundedValue(l, 0, 1); if (0 === s) { l *= 255; return this.computeTbgrFromComponents(l, l, l, transparency); } let p = l <= 0.5 ? l * (1 + s) : l + s - l * s; let q = 2 * l - p; return this.computeTbgrFromComponents( hue2rgb(q, p, h + 1 / 3), hue2rgb(q, p, h), hue2rgb(q, p, h - 1 / 3), transparency, ); } toHslColor() { return { ...ColorValue.toHsl(this._tbgr), ...(void 0 != this._hue && { h: this._hue, }), }; } static toHsl(tbgr) { let { r, g, b } = ColorValue.getColors(tbgr); let red = r / 255; let green = g / 255; let blue = b / 255; let cMin = Math.min(red, green, blue); let cMax = Math.max(red, green, blue); let delta = cMax - cMin; let hue = 0; let saturation = 0; hue = 0 === delta ? 0 : red === cMax ? ((green - blue) / delta) % 6 : green === cMax ? (blue - red) / delta + 2 : (red - green) / delta + 4; hue = Math.round(60 * hue); if (hue < 0) hue += 360; let lightness = (cMax + cMin) / 2; saturation = 0 === delta ? 0 : delta / (1 - Math.abs(2 * lightness - 1)); saturation = Number((100 * saturation).toFixed(1)); lightness = Number((100 * lightness).toFixed(1)); return { h: hue, s: saturation, l: lightness, a: this.getAlpha(tbgr) / 255, }; } toRgbColor() { let { r, g, b } = ColorValue.getColors(this._tbgr); return { r, g, b, a: this.getAlpha() / 255, }; } toHsvColor() { return { ...ColorValue.toHsv(this._tbgr), ...(void 0 != this._hue && { h: this._hue, }), }; } static toHsv(tbgr) { let { r, g, b } = ColorValue.getColors(tbgr); let red = r / 255; let green = g / 255; let blue = b / 255; let cMin = Math.min(red, green, blue); let cMax = Math.max(red, green, blue); let delta = cMax - cMin; let hue = 0; hue = 0 === delta ? 0 : red === cMax ? ((green - blue) / delta) % 6 : green === cMax ? (blue - red) / delta + 2 : (red - green) / delta + 4; hue = Math.round(60 * hue); if (hue < 0) hue += 360; let brightness = cMax; let saturation = 0 === cMax ? 0 : delta / cMax; saturation = Number((100 * saturation).toFixed(1)); brightness = Number((100 * brightness).toFixed(1)); return { h: hue, s: saturation, v: brightness, a: this.getAlpha(tbgr) / 255, }; } equals(other) { return this._tbgr === other._tbgr; } static getFormattedColorNumber(value, precision = 1) { if (0 === precision) Math.round(value).toString(); return Number(value.toFixed(precision)).toString(); } toRgbString(includeAlpha) { let rgb = this.toRgbColor(); let rgbString = `${rgb.r}, ${rgb.g}, ${rgb.b}`; if (includeAlpha) { let alpha = rgb.a ?? 1; return `rgba(${rgbString}, ${ColorValue.getFormattedColorNumber( alpha, 2, )})`; } return `rgb(${rgbString})`; } toHslString(includeAlpha) { let hsl = this.toHslColor(); let hslString = `${ColorValue.getFormattedColorNumber( this._hue ?? hsl.h, )}, ${ColorValue.getFormattedColorNumber( hsl.s, )}%, ${ColorValue.getFormattedColorNumber(hsl.l)}%`; if (includeAlpha) { let alpha = hsl.a ?? 1; return `hsla(${hslString}, ${ColorValue.getFormattedColorNumber( alpha, 2, )})`; } return `hsl(${hslString})`; } toHsvString(includeAlpha) { let hsv = this.toHsvColor(); let hsvString = `${this._hue ?? hsv.h}, ${hsv.s}%, ${hsv.v}%`; if (includeAlpha) { let alpha = hsv.a ?? 1; return `hsva(${hsvString}, ${ColorValue.getFormattedColorNumber( alpha, 2, )})`; } return `hsv(${hsvString})`; } constructor(tbgr, hue) { _define_property(this, '_tbgr', void 0); _define_property(this, '_hue', void 0); scratchUInt32[0] = tbgr; this._tbgr = scratchUInt32[0]; this._hue = hue; } }