UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.

253 lines (252 loc) 9.72 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.Colour = exports.Color = void 0; const primitives_1 = require("../guards/primitives"); const convert_1 = require("./convert"); const css_colors_1 = require("./css-colors"); const guards_1 = require("./guards"); const random_1 = require("./random"); const utils_1 = require("./utils"); class Color { hex; hex8; rgb; rgba; hsl; hsla; constructor(color) { if (color) { const $color = color.trim(); if (_a.isCSSColor($color)) { const { hex, hex8, rgb, rgba, hsl, hsla } = new _a(css_colors_1.CSS_COLORS[$color]); this.hex = hex; this.hex8 = hex8; this.rgb = rgb; this.rgba = rgba; this.hsl = hsl; this.hsla = hsla; } else { const colors = this.#convertColorToOthers($color); if ('hex8' in colors) { const [r, g, b] = (0, utils_1.extractAlphaColorValues)(colors.rgba); const [h, s, l] = (0, utils_1.extractAlphaColorValues)(colors.hsla); this.hex = colors.hex8.toUpperCase().slice(0, 7); this.hex8 = colors.hex8.toUpperCase(); this.rgb = `rgb(${r}, ${g}, ${b})`; this.rgba = colors.rgba; this.hsl = `hsl(${h}, ${s}%, ${l}%)`; this.hsla = colors.hsla; } else { this.hex = colors.hex.toUpperCase(); this.hex8 = this.#hex6ToHex8(colors.hex); this.rgb = colors.rgb; this.rgba = this.#rgbToRGBA(colors.rgb); this.hsl = colors.hsl; this.hsla = this.#hslToHSLA(colors.hsl); } } } else { const hsl = (0, random_1.generateRandomHSLColor)(); const { hex, rgb } = (0, convert_1.convertColorCode)(hsl); this.hex = hex.toUpperCase(); this.hex8 = this.#hex6ToHex8(hex); this.rgb = rgb; this.rgba = this.#rgbToRGBA(rgb); this.hsl = hsl; this.hsla = this.#hslToHSLA(hsl); } } *[Symbol.iterator]() { yield this.hex; yield this.hex8; yield this.rgb; yield this.rgba; yield this.hsl; yield this.hsla; } [Symbol.toPrimitive](hint) { if (hint === 'string') { return this.hex8; } else if (hint === 'number') { const hex = this.hex8.endsWith('FF') ? this.hex : this.hex8; return parseInt(hex.replace('#', '0x'), 16); } return this; } #hex6ToHex8(hex) { return `${hex.toUpperCase()}${(0, utils_1.percentToHex)(100)}`; } #rgbToRGBA(rgb) { const [r, g, b] = (0, utils_1.extractSolidColorValues)(rgb); return `rgba(${r}, ${g}, ${b}, 1)`; } #hslToHSLA(hsl) { const [h, s, l] = (0, utils_1.extractSolidColorValues)(hsl); return `hsla(${h}, ${s}%, ${l}%, 1)`; } toString() { return this.hex8; } toJSON() { return this.hex8; } applyOpacity(opacity) { const hex8 = `${this.hex.slice(0, 7)}${(0, utils_1.percentToHex)(opacity)}`.toUpperCase(); return new _a(hex8); } applyDarkness(percent) { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const newL = Math.max(0, l - percent); const newHSL = `hsl(${h}, ${s}%, ${newL}%)`; return new _a(newHSL).applyOpacity((a * 100)); } applyBrightness(percent) { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const newL = Math.min(100, l + percent); const newHSL = `hsl(${h}, ${s}%, ${newL}%)`; return new _a(newHSL).applyOpacity((a * 100)); } applyDullness(percent) { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const newS = Math.max(0, s - percent); const newHSL = `hsl(${h}, ${newS}%, ${l}%)`; return new _a(newHSL).applyOpacity((a * 100)); } applyWhiteShade(percent) { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const newS = Math.max(0, s - (s * percent) / 100); const newL = Math.min(100, l + ((100 - l) * percent) / 100); const newHSL = `hsl(${h}, ${newS}%, ${newL}%)`; return new _a(newHSL).applyOpacity((a * 100)); } blendWith(other, weight = 0.5) { const w = Math.max(0, Math.min(1, weight)); const otherColor = new _a(other); const [r1, g1, b1, a1] = (0, utils_1.extractAlphaColorValues)(this.rgba); const [r2, g2, b2, a2] = (0, utils_1.extractAlphaColorValues)(otherColor.rgba); const alpha = Math.round((a1 * (1 - w) + a2 * w) * 100) / 100; const _blendChannel = (c1, c2) => { return Math.round((c1 * a1 * (1 - w) + c2 * a2 * w) / alpha); }; const r = _blendChannel(r1, r2); const g = _blendChannel(g1, g2); const b = _blendChannel(b1, b2); const blended = `rgba(${r}, ${g}, ${b}, ${alpha})`; return new _a(blended); } contrastRatio(other) { const otherColor = new _a(other); const _luminance = (rgb) => { const [r, g, b] = (0, utils_1.extractSolidColorValues)(rgb).map((v) => { const c = v / 255; return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; }); return 0.2126 * r + 0.7152 * g + 0.0722 * b; }; const lum1 = _luminance(this.rgb); const lum2 = _luminance(otherColor.rgb); const brighter = Math.max(lum1, lum2); const darker = Math.min(lum1, lum2); const ratio = (brighter + 0.05) / (darker + 0.05); return Math.round(ratio * 100) / 100; } getComplementaryColor() { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const newHue = (h + 180) % 360; const newHSL = `hsl(${newHue}, ${s}%, ${l}%)`; return new _a(newHSL).applyOpacity((a * 100)); } getAnalogousColors() { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const left = `hsl(${(h + 330) % 360}, ${s}%, ${l}%)`; const right = `hsl(${(h + 30) % 360}, ${s}%, ${l}%)`; const analogous = [this, new _a(left), new _a(right)]; return analogous.map((c) => c.applyOpacity((a * 100))); } getTriadColors() { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const c1 = `hsl(${(h + 120) % 360}, ${s}%, ${l}%)`; const c2 = `hsl(${(h + 240) % 360}, ${s}%, ${l}%)`; const triad = [this, new _a(c1), new _a(c2)]; return triad.map((c) => c.applyOpacity((a * 100))); } getTetradColors() { const [h, s, l, a] = (0, utils_1.extractAlphaColorValues)(this.hsla); const c1 = `hsl(${(h + 90) % 360}, ${s}%, ${l}%)`; const c2 = `hsl(${(h + 180) % 360}, ${s}%, ${l}%)`; const c3 = `hsl(${(h + 270) % 360}, ${s}%, ${l}%)`; const tetrad = [this, new _a(c1), new _a(c2), new _a(c3)]; return tetrad.map((c) => c.applyOpacity((a * 100))); } getWCAGRating(other) { const ratio = this.contrastRatio(other); if (ratio >= 7) return 'AAA'; if (ratio >= 4.5) return 'AA'; return 'Fail'; } isLightColor(threshold = 127.5) { const [r, g, b] = (0, utils_1.extractSolidColorValues)(this.rgb); const brightness = (r * 299 + g * 587 + b * 114) / 1000; return brightness > Math.min(255, Math.max(0, threshold)); } static isHex6(color) { return /^#[0-9A-Fa-f]{6}$/.test(color?.trim()); } static isHex8(color) { return /^#[0-9A-Fa-f]{8}$/.test(color?.trim()); } static isRGB(color) { return (0, guards_1.isRGB)(color); } static isRGBA(color) { return (0, guards_1.isRGBA)(color); } static isHSL(color) { return (0, guards_1.isHSL)(color); } static isHSLA(color) { return (0, guards_1.isHSLA)(color); } static isCSSColor(color) { return (0, primitives_1.isNonEmptyString)(color) && color in css_colors_1.CSS_COLORS; } #convertColorToOthers(color) { if (_a.isHex6(color)) { const { rgb, hsl } = (0, convert_1.convertColorCode)(color); return { hex: color, rgb, hsl }; } else if (_a.isRGB(color)) { const { hex, hsl } = (0, convert_1.convertColorCode)(color); return { hex, rgb: color, hsl }; } else if (_a.isHSL(color)) { const { hex, rgb } = (0, convert_1.convertColorCode)(color); return { hex, rgb, hsl: color }; } else if (_a.isHex8(color)) { const { rgba, hsla } = (0, convert_1.convertColorCode)(color); return { hex8: color, rgba, hsla }; } else if (_a.isRGBA(color)) { const { hex8, hsla } = (0, convert_1.convertColorCode)(color); return { hex8, rgba: color, hsla }; } else if (_a.isHSLA(color)) { const { hex8, rgba } = (0, convert_1.convertColorCode)(color); return { hex8, rgba, hsla: color }; } throw new TypeError(`${color} is not a valid color!`, { cause: 'Unrecognized Color Format', }); } } exports.Color = Color; exports.Colour = Color; _a = Color;