UNPKG

@ctrl/tinycolor

Version:

Fast, small color manipulation and conversion for JavaScript

214 lines (213 loc) 8.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isValidCSSUnit = exports.stringInputToObject = exports.inputToRGB = void 0; const conversion_js_1 = require("./conversion.js"); const css_color_names_js_1 = require("./css-color-names.js"); const util_js_1 = require("./util.js"); /** * Given a string or object, convert that input to RGB * * Possible string inputs: * ``` * "red" * "#f00" or "f00" * "#ff0000" or "ff0000" * "#ff000000" or "ff000000" * "rgb 255 0 0" or "rgb (255, 0, 0)" * "rgb 1.0 0 0" or "rgb (1, 0, 0)" * "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" * "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" * "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" * "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" * "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" * "cmyk(0, 20, 0, 0)" or "cmyk 0 20 0 0" * ``` */ function inputToRGB(color) { let rgb = { r: 0, g: 0, b: 0 }; let a = 1; let s = null; let v = null; let l = null; let ok = false; let format = false; if (typeof color === 'string') { color = stringInputToObject(color); } if (typeof color === 'object') { if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) { rgb = (0, conversion_js_1.rgbToRgb)(color.r, color.g, color.b); ok = true; format = String(color.r).substr(-1) === '%' ? 'prgb' : 'rgb'; } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) { s = (0, util_js_1.convertToPercentage)(color.s); v = (0, util_js_1.convertToPercentage)(color.v); rgb = (0, conversion_js_1.hsvToRgb)(color.h, s, v); ok = true; format = 'hsv'; } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) { s = (0, util_js_1.convertToPercentage)(color.s); l = (0, util_js_1.convertToPercentage)(color.l); rgb = (0, conversion_js_1.hslToRgb)(color.h, s, l); ok = true; format = 'hsl'; } else if (isValidCSSUnit(color.c) && isValidCSSUnit(color.m) && isValidCSSUnit(color.y) && isValidCSSUnit(color.k)) { rgb = (0, conversion_js_1.cmykToRgb)(color.c, color.m, color.y, color.k); ok = true; format = 'cmyk'; } if (Object.prototype.hasOwnProperty.call(color, 'a')) { a = color.a; } } a = (0, util_js_1.boundAlpha)(a); return { ok, format: color.format || format, r: Math.min(255, Math.max(rgb.r, 0)), g: Math.min(255, Math.max(rgb.g, 0)), b: Math.min(255, Math.max(rgb.b, 0)), a, }; } exports.inputToRGB = inputToRGB; // <http://www.w3.org/TR/css3-values/#integers> const CSS_INTEGER = '[-\\+]?\\d+%?'; // <http://www.w3.org/TR/css3-values/#number-value> const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?'; // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. const CSS_UNIT = '(?:' + CSS_NUMBER + ')|(?:' + CSS_INTEGER + ')'; // Actual matching. // Parentheses and commas are optional, but not required. // Whitespace can take the place of commas or opening paren // eslint-disable-next-line prettier/prettier const PERMISSIVE_MATCH3 = '[\\s|\\(]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')\\s*\\)?'; const PERMISSIVE_MATCH4 = // eslint-disable-next-line prettier/prettier '[\\s|\\(]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')[,|\\s]+(' + CSS_UNIT + ')\\s*\\)?'; const matchers = { CSS_UNIT: new RegExp(CSS_UNIT), rgb: new RegExp('rgb' + PERMISSIVE_MATCH3), rgba: new RegExp('rgba' + PERMISSIVE_MATCH4), hsl: new RegExp('hsl' + PERMISSIVE_MATCH3), hsla: new RegExp('hsla' + PERMISSIVE_MATCH4), hsv: new RegExp('hsv' + PERMISSIVE_MATCH3), hsva: new RegExp('hsva' + PERMISSIVE_MATCH4), cmyk: new RegExp('cmyk' + PERMISSIVE_MATCH4), hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, }; /** * Permissive string parsing. Take in a number of formats, and output an object * based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` or `{c, m, y, k}` or `{c, m, y, k, a}` */ function stringInputToObject(color) { color = color.trim().toLowerCase(); if (color.length === 0) { return false; } let named = false; if (css_color_names_js_1.names[color]) { color = css_color_names_js_1.names[color]; named = true; } else if (color === 'transparent') { return { r: 0, g: 0, b: 0, a: 0, format: 'name' }; } // Try to match string input using regular expressions. // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] // Just return an object and let the conversion functions handle that. // This way the result will be the same whether the tinycolor is initialized with string or object. let match = matchers.rgb.exec(color); if (match) { return { r: match[1], g: match[2], b: match[3] }; } match = matchers.rgba.exec(color); if (match) { return { r: match[1], g: match[2], b: match[3], a: match[4] }; } match = matchers.hsl.exec(color); if (match) { return { h: match[1], s: match[2], l: match[3] }; } match = matchers.hsla.exec(color); if (match) { return { h: match[1], s: match[2], l: match[3], a: match[4] }; } match = matchers.hsv.exec(color); if (match) { return { h: match[1], s: match[2], v: match[3] }; } match = matchers.hsva.exec(color); if (match) { return { h: match[1], s: match[2], v: match[3], a: match[4] }; } match = matchers.cmyk.exec(color); if (match) { return { c: match[1], m: match[2], y: match[3], k: match[4], }; } match = matchers.hex8.exec(color); if (match) { return { r: (0, conversion_js_1.parseIntFromHex)(match[1]), g: (0, conversion_js_1.parseIntFromHex)(match[2]), b: (0, conversion_js_1.parseIntFromHex)(match[3]), a: (0, conversion_js_1.convertHexToDecimal)(match[4]), format: named ? 'name' : 'hex8', }; } match = matchers.hex6.exec(color); if (match) { return { r: (0, conversion_js_1.parseIntFromHex)(match[1]), g: (0, conversion_js_1.parseIntFromHex)(match[2]), b: (0, conversion_js_1.parseIntFromHex)(match[3]), format: named ? 'name' : 'hex', }; } match = matchers.hex4.exec(color); if (match) { return { r: (0, conversion_js_1.parseIntFromHex)(match[1] + match[1]), g: (0, conversion_js_1.parseIntFromHex)(match[2] + match[2]), b: (0, conversion_js_1.parseIntFromHex)(match[3] + match[3]), a: (0, conversion_js_1.convertHexToDecimal)(match[4] + match[4]), format: named ? 'name' : 'hex8', }; } match = matchers.hex3.exec(color); if (match) { return { r: (0, conversion_js_1.parseIntFromHex)(match[1] + match[1]), g: (0, conversion_js_1.parseIntFromHex)(match[2] + match[2]), b: (0, conversion_js_1.parseIntFromHex)(match[3] + match[3]), format: named ? 'name' : 'hex', }; } return false; } exports.stringInputToObject = stringInputToObject; /** * Check to see if it looks like a CSS unit * (see `matchers` above for definition). */ function isValidCSSUnit(color) { if (typeof color === 'number') { return !Number.isNaN(color); } return matchers.CSS_UNIT.test(color); } exports.isValidCSSUnit = isValidCSSUnit;