-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from caoimghgin/insert-isValid
Insert isValid function as an extention for colorjs.io
- Loading branch information
Showing
6 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# colorsjs.io/isValid | ||
|
||
Lifts portions of code from colorjs.io npm package that was private so we could expose add an isValid function. I have submitted | ||
a [Pull Request](https://github.com/color-js/color.js/pull/633) to [colorjs](https://nextjs.org/) to add this feature. In the meantime, | ||
this extension does the job until it's approved and merged. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* eslint-disable curly */ | ||
import KEYWORDS from './keywords.js'; | ||
import { parseFunction } from './parse.js'; | ||
|
||
/** | ||
* Verify string can be parsed into a color object | ||
* @param {String} str | ||
* @returns boolean | ||
*/ | ||
export default function isValid(str) { | ||
if (!str) return false; | ||
if (isColorHex(str)) return true; | ||
if (isTransparent(str)) return true; | ||
return validateParsedValue(str, parseFunction(str)); | ||
} | ||
|
||
/** | ||
* Return if parsed is valid and not a rgb function with angles | ||
* @param {String} str | ||
* @param {any} parsed | ||
* @returns boolean | ||
*/ | ||
const validateParsedValue = (str, parsed) => { | ||
return !(!isParsedValid(parsed, str) || isParsedRGBWithAngles(parsed)); | ||
}; | ||
|
||
/** | ||
* Return if string is "transparent" | ||
* @param {String} str | ||
* @returns boolean | ||
*/ | ||
const isTransparent = (str) => { | ||
return str === 'transparent'; | ||
}; | ||
|
||
/** | ||
* Return if parsed is valid | ||
* @param {any} parsed | ||
* @param {String} str | ||
* @returns boolean | ||
*/ | ||
const isParsedValid = (parsed, str) => { | ||
if ( | ||
!parsed || | ||
!parsed.name || | ||
parsed.argMeta.filter((item) => isTypeNumberPercentageOrAngle(item.type)).length < 3 || | ||
parsed.argMeta.filter((item) => isTypeNumberPercentageOrAngle(item.type)).length > 4 | ||
) { | ||
if (!KEYWORDS[str.toLowerCase()]) return false; | ||
} | ||
return true; | ||
}; | ||
|
||
/** | ||
* Return if parsed is a rgb function with angles | ||
* @param {any} parsed | ||
* @returns boolean | ||
*/ | ||
const isParsedRGBWithAngles = (parsed) => { | ||
if ( | ||
parsed && | ||
parsed.name === 'rgb' && | ||
parsed.argMeta.filter((item) => item.type === '<angle>').length | ||
) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
|
||
/** | ||
* Return if string is a number, percentage or angle | ||
* @param {String} type | ||
* @returns boolean | ||
*/ | ||
const isTypeNumberPercentageOrAngle = (type) => { | ||
return type === '<number>' || type === '<percentage>' || type === '<angle>' || !type; | ||
}; | ||
|
||
/** | ||
* Verify string is valid hex | ||
* @param {String} str | ||
* @returns boolean | ||
*/ | ||
function isColorHex(str) { | ||
const isString = (color) => color && typeof color === 'string'; | ||
if (isString(str)) { | ||
const regex = /^#([\da-f]{3}){1,2}$|^#([\da-f]{4}){1,2}$/i; | ||
return str && regex.test(str); | ||
} | ||
return false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// To produce: Visit https://www.w3.org/TR/css-color-4/#named-colors | ||
// and run in the console: | ||
// copy($$("tr", $(".named-color-table tbody")).map(tr => `"${tr.cells[2].textContent.trim()}": [${tr.cells[4].textContent.trim().split(/\s+/).map(c => c === "0"? "0" : c === "255"? "1" : c + " / 255").join(", ")}]`).join(",\n")) | ||
|
||
/** List of CSS color keywords | ||
* Note that this does not include currentColor, transparent, | ||
* or system colors | ||
* | ||
* @type {Record<string, [number, number, number]>} | ||
*/ | ||
export default { | ||
aliceblue: [240 / 255, 248 / 255, 1], | ||
antiquewhite: [250 / 255, 235 / 255, 215 / 255], | ||
aqua: [0, 1, 1], | ||
aquamarine: [127 / 255, 1, 212 / 255], | ||
azure: [240 / 255, 1, 1], | ||
beige: [245 / 255, 245 / 255, 220 / 255], | ||
bisque: [1, 228 / 255, 196 / 255], | ||
black: [0, 0, 0], | ||
blanchedalmond: [1, 235 / 255, 205 / 255], | ||
blue: [0, 0, 1], | ||
blueviolet: [138 / 255, 43 / 255, 226 / 255], | ||
brown: [165 / 255, 42 / 255, 42 / 255], | ||
burlywood: [222 / 255, 184 / 255, 135 / 255], | ||
cadetblue: [95 / 255, 158 / 255, 160 / 255], | ||
chartreuse: [127 / 255, 1, 0], | ||
chocolate: [210 / 255, 105 / 255, 30 / 255], | ||
coral: [1, 127 / 255, 80 / 255], | ||
cornflowerblue: [100 / 255, 149 / 255, 237 / 255], | ||
cornsilk: [1, 248 / 255, 220 / 255], | ||
crimson: [220 / 255, 20 / 255, 60 / 255], | ||
cyan: [0, 1, 1], | ||
darkblue: [0, 0, 139 / 255], | ||
darkcyan: [0, 139 / 255, 139 / 255], | ||
darkgoldenrod: [184 / 255, 134 / 255, 11 / 255], | ||
darkgray: [169 / 255, 169 / 255, 169 / 255], | ||
darkgreen: [0, 100 / 255, 0], | ||
darkgrey: [169 / 255, 169 / 255, 169 / 255], | ||
darkkhaki: [189 / 255, 183 / 255, 107 / 255], | ||
darkmagenta: [139 / 255, 0, 139 / 255], | ||
darkolivegreen: [85 / 255, 107 / 255, 47 / 255], | ||
darkorange: [1, 140 / 255, 0], | ||
darkorchid: [153 / 255, 50 / 255, 204 / 255], | ||
darkred: [139 / 255, 0, 0], | ||
darksalmon: [233 / 255, 150 / 255, 122 / 255], | ||
darkseagreen: [143 / 255, 188 / 255, 143 / 255], | ||
darkslateblue: [72 / 255, 61 / 255, 139 / 255], | ||
darkslategray: [47 / 255, 79 / 255, 79 / 255], | ||
darkslategrey: [47 / 255, 79 / 255, 79 / 255], | ||
darkturquoise: [0, 206 / 255, 209 / 255], | ||
darkviolet: [148 / 255, 0, 211 / 255], | ||
deeppink: [1, 20 / 255, 147 / 255], | ||
deepskyblue: [0, 191 / 255, 1], | ||
dimgray: [105 / 255, 105 / 255, 105 / 255], | ||
dimgrey: [105 / 255, 105 / 255, 105 / 255], | ||
dodgerblue: [30 / 255, 144 / 255, 1], | ||
firebrick: [178 / 255, 34 / 255, 34 / 255], | ||
floralwhite: [1, 250 / 255, 240 / 255], | ||
forestgreen: [34 / 255, 139 / 255, 34 / 255], | ||
fuchsia: [1, 0, 1], | ||
gainsboro: [220 / 255, 220 / 255, 220 / 255], | ||
ghostwhite: [248 / 255, 248 / 255, 1], | ||
gold: [1, 215 / 255, 0], | ||
goldenrod: [218 / 255, 165 / 255, 32 / 255], | ||
gray: [128 / 255, 128 / 255, 128 / 255], | ||
green: [0, 128 / 255, 0], | ||
greenyellow: [173 / 255, 1, 47 / 255], | ||
grey: [128 / 255, 128 / 255, 128 / 255], | ||
honeydew: [240 / 255, 1, 240 / 255], | ||
hotpink: [1, 105 / 255, 180 / 255], | ||
indianred: [205 / 255, 92 / 255, 92 / 255], | ||
indigo: [75 / 255, 0, 130 / 255], | ||
ivory: [1, 1, 240 / 255], | ||
khaki: [240 / 255, 230 / 255, 140 / 255], | ||
lavender: [230 / 255, 230 / 255, 250 / 255], | ||
lavenderblush: [1, 240 / 255, 245 / 255], | ||
lawngreen: [124 / 255, 252 / 255, 0], | ||
lemonchiffon: [1, 250 / 255, 205 / 255], | ||
lightblue: [173 / 255, 216 / 255, 230 / 255], | ||
lightcoral: [240 / 255, 128 / 255, 128 / 255], | ||
lightcyan: [224 / 255, 1, 1], | ||
lightgoldenrodyellow: [250 / 255, 250 / 255, 210 / 255], | ||
lightgray: [211 / 255, 211 / 255, 211 / 255], | ||
lightgreen: [144 / 255, 238 / 255, 144 / 255], | ||
lightgrey: [211 / 255, 211 / 255, 211 / 255], | ||
lightpink: [1, 182 / 255, 193 / 255], | ||
lightsalmon: [1, 160 / 255, 122 / 255], | ||
lightseagreen: [32 / 255, 178 / 255, 170 / 255], | ||
lightskyblue: [135 / 255, 206 / 255, 250 / 255], | ||
lightslategray: [119 / 255, 136 / 255, 153 / 255], | ||
lightslategrey: [119 / 255, 136 / 255, 153 / 255], | ||
lightsteelblue: [176 / 255, 196 / 255, 222 / 255], | ||
lightyellow: [1, 1, 224 / 255], | ||
lime: [0, 1, 0], | ||
limegreen: [50 / 255, 205 / 255, 50 / 255], | ||
linen: [250 / 255, 240 / 255, 230 / 255], | ||
magenta: [1, 0, 1], | ||
maroon: [128 / 255, 0, 0], | ||
mediumaquamarine: [102 / 255, 205 / 255, 170 / 255], | ||
mediumblue: [0, 0, 205 / 255], | ||
mediumorchid: [186 / 255, 85 / 255, 211 / 255], | ||
mediumpurple: [147 / 255, 112 / 255, 219 / 255], | ||
mediumseagreen: [60 / 255, 179 / 255, 113 / 255], | ||
mediumslateblue: [123 / 255, 104 / 255, 238 / 255], | ||
mediumspringgreen: [0, 250 / 255, 154 / 255], | ||
mediumturquoise: [72 / 255, 209 / 255, 204 / 255], | ||
mediumvioletred: [199 / 255, 21 / 255, 133 / 255], | ||
midnightblue: [25 / 255, 25 / 255, 112 / 255], | ||
mintcream: [245 / 255, 1, 250 / 255], | ||
mistyrose: [1, 228 / 255, 225 / 255], | ||
moccasin: [1, 228 / 255, 181 / 255], | ||
navajowhite: [1, 222 / 255, 173 / 255], | ||
navy: [0, 0, 128 / 255], | ||
oldlace: [253 / 255, 245 / 255, 230 / 255], | ||
olive: [128 / 255, 128 / 255, 0], | ||
olivedrab: [107 / 255, 142 / 255, 35 / 255], | ||
orange: [1, 165 / 255, 0], | ||
orangered: [1, 69 / 255, 0], | ||
orchid: [218 / 255, 112 / 255, 214 / 255], | ||
palegoldenrod: [238 / 255, 232 / 255, 170 / 255], | ||
palegreen: [152 / 255, 251 / 255, 152 / 255], | ||
paleturquoise: [175 / 255, 238 / 255, 238 / 255], | ||
palevioletred: [219 / 255, 112 / 255, 147 / 255], | ||
papayawhip: [1, 239 / 255, 213 / 255], | ||
peachpuff: [1, 218 / 255, 185 / 255], | ||
peru: [205 / 255, 133 / 255, 63 / 255], | ||
pink: [1, 192 / 255, 203 / 255], | ||
plum: [221 / 255, 160 / 255, 221 / 255], | ||
powderblue: [176 / 255, 224 / 255, 230 / 255], | ||
purple: [128 / 255, 0, 128 / 255], | ||
rebeccapurple: [102 / 255, 51 / 255, 153 / 255], | ||
red: [1, 0, 0], | ||
rosybrown: [188 / 255, 143 / 255, 143 / 255], | ||
royalblue: [65 / 255, 105 / 255, 225 / 255], | ||
saddlebrown: [139 / 255, 69 / 255, 19 / 255], | ||
salmon: [250 / 255, 128 / 255, 114 / 255], | ||
sandybrown: [244 / 255, 164 / 255, 96 / 255], | ||
seagreen: [46 / 255, 139 / 255, 87 / 255], | ||
seashell: [1, 245 / 255, 238 / 255], | ||
sienna: [160 / 255, 82 / 255, 45 / 255], | ||
silver: [192 / 255, 192 / 255, 192 / 255], | ||
skyblue: [135 / 255, 206 / 255, 235 / 255], | ||
slateblue: [106 / 255, 90 / 255, 205 / 255], | ||
slategray: [112 / 255, 128 / 255, 144 / 255], | ||
slategrey: [112 / 255, 128 / 255, 144 / 255], | ||
snow: [1, 250 / 255, 250 / 255], | ||
springgreen: [0, 1, 127 / 255], | ||
steelblue: [70 / 255, 130 / 255, 180 / 255], | ||
tan: [210 / 255, 180 / 255, 140 / 255], | ||
teal: [0, 128 / 255, 128 / 255], | ||
thistle: [216 / 255, 191 / 255, 216 / 255], | ||
tomato: [1, 99 / 255, 71 / 255], | ||
turquoise: [64 / 255, 224 / 255, 208 / 255], | ||
violet: [238 / 255, 130 / 255, 238 / 255], | ||
wheat: [245 / 255, 222 / 255, 179 / 255], | ||
white: [1, 1, 1], | ||
whitesmoke: [245 / 255, 245 / 255, 245 / 255], | ||
yellow: [1, 1, 0], | ||
yellowgreen: [154 / 255, 205 / 255, 50 / 255], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* eslint-disable no-multi-assign */ | ||
/* eslint-disable object-shorthand */ | ||
/* eslint-disable prefer-const */ | ||
/* eslint-disable no-param-reassign */ | ||
/** | ||
* Parse a CSS function, regardless of its name and arguments | ||
* @param {string} str String to parse | ||
* @return {ParseFunctionReturn | void} | ||
*/ | ||
export function parseFunction(str) { | ||
if (!str) { | ||
return; | ||
} | ||
|
||
str = str.trim(); | ||
|
||
let parts = str.match(regex.function); | ||
|
||
if (parts) { | ||
// It is a function, parse args | ||
let args = []; | ||
let argMeta = []; | ||
let lastAlpha = false; | ||
|
||
let separators = parts[2].replace(regex.singleArgument, ($0, rawArg) => { | ||
let { value, meta } = parseArgument(rawArg); | ||
|
||
if ($0.startsWith('/')) { | ||
// It's alpha | ||
lastAlpha = true; | ||
} | ||
|
||
args.push(value); | ||
argMeta.push(meta); | ||
return ''; | ||
}); | ||
|
||
return { | ||
name: parts[1].toLowerCase(), | ||
args, | ||
argMeta, | ||
lastAlpha, | ||
commas: separators.includes(','), | ||
rawName: parts[1], | ||
rawArgs: parts[2], | ||
}; | ||
} | ||
} | ||
|
||
/** | ||
* Parse a single function argument | ||
* @param {string} rawArg | ||
* @returns {{value: number, meta: ArgumentMeta}} | ||
*/ | ||
export function parseArgument(rawArg) { | ||
/** @type {Partial<ArgumentMeta>} */ | ||
let meta = {}; | ||
let unit = rawArg.match(regex.unitValue)?.[0]; | ||
/** @type {string | number} */ | ||
let value = (meta.raw = rawArg); | ||
|
||
if (unit) { | ||
// It’s a dimension token | ||
meta.type = unit === '%' ? '<percentage>' : '<angle>'; | ||
meta.unit = unit; | ||
meta.unitless = Number(value.slice(0, -unit.length)); // unitless number | ||
|
||
value = meta.unitless * units[unit]; | ||
} else if (regex.number.test(value)) { | ||
// It's a number | ||
// Convert numerical args to numbers | ||
value = Number(value); | ||
meta.type = '<number>'; | ||
} else if (value === 'none') { | ||
value = null; | ||
} else if (value === 'NaN' || value === 'calc(NaN)') { | ||
value = NaN; | ||
meta.type = '<number>'; | ||
} else { | ||
meta.type = '<ident>'; | ||
} | ||
|
||
return { value: /** @type {number} */ (value), meta: /** @type {ArgumentMeta} */ (meta) }; | ||
} | ||
|
||
/** | ||
* Units and multiplication factors for the internally stored numbers | ||
*/ | ||
export const units = { | ||
'%': 0.01, | ||
deg: 1, | ||
grad: 0.9, | ||
rad: 180 / Math.PI, | ||
turn: 360, | ||
}; | ||
|
||
export const regex = { | ||
// Need to list calc(NaN) explicitly as otherwise its ending paren would terminate the function call | ||
function: /^([a-z]+)\(((?:calc\(NaN\)|.)+?)\)$/i, | ||
number: /^([-+]?(?:[0-9]*\.)?[0-9]+(e[-+]?[0-9]+)?)$/i, | ||
unitValue: RegExp(`(${Object.keys(units).join('|')})$`), | ||
|
||
// NOTE The -+ are not just for prefix, but also for idents, and e+N notation! | ||
singleArgument: /\/?\s*(none|NaN|calc\(NaN\)|[-+\w.]+(?:%|deg|g?rad|turn)?)/g, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import isValid from './utilities/validate-color/isValid.js'; | ||
|
||
function main() { | ||
console.log('Hello, World!'); | ||
console.log(isValid('transparent')); | ||
console.log('IS VALID?', isValid('#0055')); | ||
} | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* eslint-disable no-console */ | ||
// https://wiki.c2.com/?TracerBullets | ||
|
||
import isValid from '../extensions/colorjs.io/isValid.js'; | ||
|
||
function main() { | ||
console.log('Hello, World!'); | ||
console.log('IS transparent VALID?', isValid('transparent')); | ||
console.log('IS #0055 VALID?', isValid('#0055')); | ||
} | ||
|
||
main(); |