UNPKG

@exabytellc/utils

Version:

EB react utils to make everything a little easier!

427 lines (417 loc) 12 kB
import ClassHelper from "../_helpers/ClassHelper"; import { validParam } from "../_helpers/handleError"; import { mapToObject } from "../list"; import { clamp, round, numLinearTo } from "../math"; import { padStart } from "../str"; import { isNull, isNum } from "../types"; import { Colors, HSLtoRGB, RGBToHSL } from "../colors"; export default class Color extends ClassHelper { //========< variable & constructor #r; get r() { return this.#r } #g; get g() { return this.#g } #b; get b() { return this.#b } #o; get o() { return this.#o } #s = 0; get shade() { return this.#s; } get avg() { return ((this.r + this.g + this.b) / 3) } //========< variable & constructor #hsl = null; get hsl() { if (!this.#hsl) this.#hsl = RGBToHSL(this.#r, this.#g, this.#b); return this.#hsl; } //========< variable & constructor constructor(...args) { super(); let r = 0, g = 0, b = 0, o = 1, s = null; // Default values if (args.length === 1) { const c = Color.#parse(args[0]); if (c) { ({ r, g, b, o, shade: s } = c); // Destructuring assignment } } else if (args.length >= 3) { // Ensure at least RGB values are provided [r, g, b, o] = args; // Destructuring assignment } else { validParam(true, args); } // Perform any additional validation if necessary this.#r = r ?? 255; this.#g = g ?? 255; this.#b = b ?? 255; this.#o = o ?? 1; this.#s = s ?? (((Math.max(this.#r, this.#g, this.#b) / 255) + (Math.min(this.#r, this.#g, this.#b) / 255)) / 2); } //========< parse static #parseHex(hex, hasAlpha = false) { const step = hasAlpha ? 2 : 1; const components = []; for (let i = 0; i < hex.length; i += step) { const component = parseInt(hex.substring(i, i + step), 16); components.push(component); } return new Color(...components); } static #parse(color) { return this.handleTryCatch({ n: "#parse", e: () => { // Check if color is already a Color object if (Color.is(color)) return color; // Check if color is a named color if (Colors?.[color]) { return Colors?.[color]; } // Parse hexadecimal color codes (6-digit) if (color?.match(/^#([a-f0-9]{6})$/i)) { // #ff00ff return Color.#parseHex(color.substring(1)); } // Parse hexadecimal color codes (3-digit) if (color?.match(/^#([a-f0-9]{3})$/i)) { // #f0f const hex = color.substring(1).split('').map(c => c + c).join(''); return Color.#parseHex(hex); } // Parse hexadecimal color codes with alpha (8-digit) if (color?.match(/^#([a-f0-9]{8})$/i)) { // #ff00ff00 return Color.#parseHex(color.substring(1), true); } // Parse hexadecimal color codes with alpha (4-digit) if (color?.match(/^#([a-f0-9]{4})$/i)) { // #f00f const hex = color.substring(1).split('').map(c => c + c).join(''); return Color.#parseHex(hex, true); } // Parse RGB and RGBA color codes const rgbMatch = color.match(/^rgba?\(([^)]+)\)$/i); if (rgbMatch) { const [r, g, b, a] = rgbMatch[1].split(',').map(val => parseFloat(val.trim())); if (isNaN(r) || isNaN(g) || isNaN(b) || r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) { throw new Error('Invalid RGB values'); } const alpha = isNaN(a) ? 1 : Math.min(Math.max(a, 0), 1); return new Color(r, g, b, alpha); } // Parse HSL and HSLA color codes const hslMatch = color.match(/^hsla?\(([^)]+)\)$/i); if (hslMatch) { const [h, s, l, a] = hslMatch[1].split(',').map(val => parseFloat(val.trim().replace('%', '')) ); if (isNaN(h) || isNaN(s) || isNaN(l) || h < 0 || h > 360 || s < 0 || s > 100 || l < 0 || l > 100) { throw new Error('Invalid HSL values'); } const hue = h / 360; const saturation = s / 100; const lightness = l / 100; const alpha = isNaN(a) ? 1 : clamp(a, 0, 1); return new Color(...Object.values(HSLtoRGB(hue, saturation, lightness)), alpha); } throw new Error('Unknown color format'); } }); } //========< parse static is(value, canBeNull = false) { return ( canBeNull && isNull(value) || !isNull(value) && typeof value === "object" && value instanceof Color ); } //========< brightness lighten(perc) { return this.handleTryCatch({ n: "lighten", c: !isNum(perc), p: { perc }, e: () => { return new Color( clamp(this.#r + ((255 - this.#r) * perc), 0, 255), clamp(this.#g + ((255 - this.#g) * perc), 0, 255), clamp(this.#b + ((255 - this.#b) * perc), 0, 255), this.#o ); } }); } darken(perc) { return this.handleTryCatch({ n: "darken", c: !isNum(perc), p: { perc }, e: () => { return new Color( clamp(this.#r - (this.#r * perc), 0, 255), clamp(this.#g - (this.#g * perc), 0, 255), clamp(this.#b - (this.#b * perc), 0, 255), this.#o ); } }); } //========< opacity fadeOut(perc) { return this.handleTryCatch({ n: "fadeOut", c: !isNum(perc), p: { perc }, e: () => { return new Color( this.#r, this.#g, this.#b, clamp(this.#o - perc, 0, 1) ); } }); } fadeIn(perc) { return this.handleTryCatch({ n: "fadeIn", c: !isNum(perc), p: { perc }, e: () => { return new Color( this.#r, this.#g, this.#b, clamp(this.#o + perc, 0, 1) ); } }); } //========< setShade(perc) { return this.handleTryCatch({ n: "setShade", c: !isNum(perc), p: { perc }, e: () => { var { h, s } = RGBToHSL(this.#r, this.#g, this.#b); var { r, g, b } = HSLtoRGB(h, s, perc * 100); // Return a new Color instance with adjusted RGB values and same opacity return new Color(r, g, b, this.#o); } }); } addShade(perc) { return this.handleTryCatch({ n: "addShade", c: !isNum(perc), p: { perc }, e: () => { var { h, s, l } = RGBToHSL(this.#r, this.#g, this.#b); var { r, g, b } = HSLtoRGB(h, s, (l + (perc * 100))); // Return a new Color instance with adjusted RGB values and same opacity return new Color(r, g, b, this.#o); } }); } //========< opacity setFade(o = 1) { return this.handleTryCatch({ n: "setFade", c: !isNum(o), p: { o }, e: () => { return new Color( this.#r, this.#g, this.#b, round(clamp(o, 0, 1), 2) ); } }); } addFade(o = 1) { return this.handleTryCatch({ n: "addFade", c: !isNum(o), p: { o }, e: () => { return new Color( this.#r, this.#g, this.#b, round(clamp(this.#o + o, 0, 1), 2) ); } }); } //========< check neigbors isNeighborColor(color, tolerance = 0.12) { color = new Color(color); return this.handleTryCatch({ n: "isNeighborColor", c: !Color.is(color) || !isNum(tolerance), p: { color, tolerance }, e: () => { color = new Color(color); tolerance = tolerance * 255; return ( Math.abs(this.#r - color.rgba[0]) <= tolerance && Math.abs(this.#g - color.rgba[1]) <= tolerance && Math.abs(this.#b - color.rgba[2]) <= tolerance ); } }); } //========< to string format get HEX() { return this.handleTryCatch({ n: "HEX", e: () => { return `#${padStart(this.#r.toString(16), 2)}${padStart( this.#g.toString(16), 2 )}${padStart(this.#b.toString(16), 2)}`; } }); } get HEXA() { return this.handleTryCatch({ n: "HEXA", e: () => { return `#${padStart(this.#r.toString(16), 2)}${padStart( this.#g.toString(16), 2)}${padStart(this.#b.toString(16), 2)}${padStart( Math.round(this.#o * 255).toString(16), 2)}`; } }); } get RGB() { return this.handleTryCatch({ n: "RGB", e: () => { return `rgb(${round(this.#r, 1)},${round(this.#g, 1)},${round(this.#b, 1)})`; } }); } get RGBA() { return this.handleTryCatch({ n: "RGBA", e: () => { return `rgba(${round(this.#r, 1)},${round(this.#g, 1)},${round(this.#b, 1)},${round(this.#o, 2)})`; } }); } //========< get contrast get contrastHueColor() { return new Color( 255 - this.#r, 255 - this.#g, 255 - this.#b, this.#o ); } get contrastShadeColor() { return this.addShade(this.#s < 0.5 ? 0.7 : -0.7); } get contrastBlackWhiteColor() { let c = this.#s < 0.5 ? 255 : 0; return new Color(c, c, c, this.#o); } //========< get contrast get shades() { const o = { 900: 0.1, 800: 0.2, 700: 0.3, 600: 0.4, 500: 0.5, 400: 0.6, 300: 0.7, 200: 0.8, 100: 0.9, 50: 0.95, } return mapToObject(o, (k, v) => { return { [k]: this.setShade(v) } }) } get fades() { const o = { 5: 0.05, 10: 0.1, 20: 0.2, 30: 0.3, 40: 0.4, 50: 0.5, 60: 0.6, 70: 0.7, 80: 0.8, 90: 0.9, 95: 0.95, } return mapToObject(o, (k, v) => { return { [k]: this.setFade(v) } }) } //========< hue to color hueToColor(perc, color) { color = new Color(color); return this.handleTryCatch({ n: "hueToColor", c: !isNum(perc) || !Color.is(color), p: { perc, color }, f: null, e: () => { let mHSL = this.hsl; let cHSL = color.hsl; let dH = Math.abs(cHSL.h - mHSL.h) * perc; let dS = Math.abs(cHSL.s - mHSL.s) * perc; let dL = Math.abs(cHSL.l - mHSL.l) * perc; let rgb = HSLtoRGB( numLinearTo(mHSL.h, dH, cHSL.h), numLinearTo(mHSL.s, dS, cHSL.s), numLinearTo(mHSL.l, dL, cHSL.l) ); return new Color(rgb.r, rgb.g, rgb.b, this.#o); } }); } //========< get text scaleToColor(perc, color) { color = new Color(color); return this.handleTryCatch({ n: "scaleToColor", c: !isNum(perc) || !Color.is(color), p: { perc, color }, f: null, e: () => { return new Color( Math.round(this.r + (perc * (color.r - this.r))), Math.round(this.g + (perc * (color.g - this.g))), Math.round(this.b + (perc * (color.b - this.b))), this.o ); } }); } //========< get text mixColors(color, mixAmount = 0.5) { function mixC(cA, cB) { var nCA = cA * mixAmount; var nCB = cB * (1 - mixAmount); return parseInt(nCA + nCB); } color = new Color(color); return this.handleTryCatch({ n: "mixColors", c: !Color.is(color) || !isNum(mixAmount), p: { color, mixAmount }, e: () => { return new Color( mixC(this.#r, color.r), mixC(this.#g, color.g), mixC(this.#b, color.b), mixC(this.#o, color.o), ); } }); } }