diff --git a/src/App.tsx b/src/App.tsx index 74a01db..5b36444 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -53,7 +53,7 @@ const App: React.FC = () => { { { { = props => { +const Input: React.FC = (props) => { const { - labelText, placeHolder, onChange, colorType, incomingColor, incomingColorType + labelText, + placeHolder, + onChange, + colorType, + incomingColor, + incomingColorType, } = props; const initInputState = () => { // show the gamut warning on load if (colorType === colorTypes.lch && isLchOutOfRgbGamut(incomingColor)) { - return inputStates.outOfFocusOutOfGamut + return inputStates.outOfFocusOutOfGamut; } else { - return inputStates.outOfFocus + return inputStates.outOfFocus; } - } + }; const [value, setValue] = useState(incomingColor); const [inputState, setInputState] = useState(initInputState()); - const localChangeHandler = (e:ChangeEvent) => { + const localChangeHandler = (e: ChangeEvent) => { const changedValue = e.currentTarget.value; setValue(changedValue); - if (isValidColor(changedValue, colorType)) { + if (isValidColor(changedValue, colorType)) { if (colorType === colorTypes.lch && isLchOutOfRgbGamut(changedValue)) { setInputState(inputStates.inFocusValidValueOutOfGamut); } else { @@ -57,49 +62,75 @@ const Input: React.FC = props => { } else { setInputState(inputStates.inFocusInvalidValue); } - } + }; - const blurHandler = (e:ChangeEvent) => { + const blurHandler = (e: ChangeEvent) => { const changedValue = e.currentTarget.value; if (isValidColor(changedValue, colorType)) { setInputState(inputStates.outOfFocus); } else { setInputState(inputStates.onBlurInvalidValue); } - } + }; - const translatedIncomingColor = translatedColor(incomingColor, incomingColorType, colorType); + const translatedIncomingColor = translatedColor( + incomingColor, + incomingColorType, + colorType, + ); useEffect(() => { - if (inputState === inputStates.onBlurInvalidValue && - translatedIncomingColor !== colorTypes.none && - translatedIncomingColor !== value) { + if ( + inputState === inputStates.onBlurInvalidValue && + translatedIncomingColor !== colorTypes.none && + translatedIncomingColor !== value + ) { setValue(translatedIncomingColor); setInputState(inputStates.outOfFocus); } - // disabling this because we only want to update when - // translatedIncomingColor changes, but not value or inputState - // eslint-disable-next-line react-hooks/exhaustive-deps + // disabling this because we only want to update when + // translatedIncomingColor changes, but not value or inputState + // eslint-disable-next-line react-hooks/exhaustive-deps }, [translatedIncomingColor]); - if (inputState === inputStates.outOfFocus && + if ( + inputState === inputStates.outOfFocus && translatedIncomingColor !== colorTypes.none && - translatedIncomingColor !== value) { + translatedIncomingColor !== value + ) { setValue(translatedIncomingColor); } - const showGamutWarning = inputState === inputStates.inFocusValidValueOutOfGamut || inputState === inputStates.outOfFocusOutOfGamut + const showGamutWarning = + inputState === inputStates.inFocusValidValueOutOfGamut || + inputState === inputStates.outOfFocusOutOfGamut; if (colorType === colorTypes.picker) { return (
- -
- ); - } else { - return ( -
- - { showGamutWarning && - ⚠️ This lch value is outside the RGB gamut; translated values are approximated - } + + {showGamutWarning && ( + + ⚠️ This lch value is outside the RGB gamut; translated values are + approximated + + )}
); } diff --git a/src/constants.ts b/src/constants.ts index 6dc038e..a6a904a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,4 @@ -const DEFAULT_COLOR = `rgba(255, 0, 0, 1)`; // red +const DEFAULT_COLOR = `rgba(255 0 0 / 1)`; // red const ASSUMED_BACKGROUND_COLOR = [255, 255, 255]; // white export { DEFAULT_COLOR, ASSUMED_BACKGROUND_COLOR } diff --git a/src/utils/formatColor.test.ts b/src/utils/formatColor.test.ts index 557f48e..3ce33ca 100644 --- a/src/utils/formatColor.test.ts +++ b/src/utils/formatColor.test.ts @@ -3,9 +3,9 @@ import { formatColor } from './formatColor'; describe('formatColor()', () => { it('formatColor returns correct string', () => { - expect(formatColor([255, 0, 0, 1], colorTypes.rgba)).toEqual('rgba(255, 0, 0, 1)'); - expect(formatColor([255, 0, 0], colorTypes.rgba)).toEqual('rgba(255, 0, 0, 1)'); - expect(formatColor([255, 0, 0], colorTypes.rgb)).toEqual('rgb(255, 0, 0)'); - expect(formatColor([120, 60, 40], colorTypes.hsla)).toEqual('hsla(120, 60%, 40%, 1)'); + expect(formatColor([255, 0, 0, 1], colorTypes.rgba)).toEqual('rgba(255 0 0 / 1)'); + expect(formatColor([255, 0, 0], colorTypes.rgba)).toEqual('rgba(255 0 0 / 1)'); + expect(formatColor([255, 0, 0], colorTypes.rgb)).toEqual('rgb(255 0 0)'); + expect(formatColor([120, 60, 40], colorTypes.hsla)).toEqual('hsla(120 60% 40% / 1)'); }); }); diff --git a/src/utils/formatColor.ts b/src/utils/formatColor.ts index 3a21e1a..680ec3f 100644 --- a/src/utils/formatColor.ts +++ b/src/utils/formatColor.ts @@ -3,13 +3,13 @@ import { colorTypes } from "./colorTypes"; const formatColor = (arr: Array, formatColorType: colorTypes): string => { switch (true) { case formatColorType === colorTypes.rgb: - return `rgb(${arr[0]}, ${arr[1]}, ${arr[2]})`; + return `rgb(${arr[0]} ${arr[1]} ${arr[2]})`; case formatColorType === colorTypes.rgba: - return `rgba(${arr[0]}, ${arr[1]}, ${arr[2]}, ${arr[3] ? arr[3] : 1})`; + return `rgba(${arr[0]} ${arr[1]} ${arr[2]} / ${arr[3] ? arr[3] : 1})`; case formatColorType === colorTypes.hsl: - return `hsl(${arr[0]}, ${arr[1]}%, ${arr[2]}%)`; + return `hsl(${arr[0]} ${arr[1]}% ${arr[2]}%)`; case formatColorType === colorTypes.hsla: - return `hsla(${arr[0]}, ${arr[1]}%, ${arr[2]}%, ${arr[3] ? arr[3] : 1})`; + return `hsla(${arr[0]} ${arr[1]}% ${arr[2]}% / ${arr[3] ? arr[3] : 1})`; case formatColorType === colorTypes.lch: return `lch(${arr[0]}% ${arr[1]} ${arr[2]}${arr[3] && arr[3] !== 100 ? ` / ${arr[3]}%` : ''})`; default: diff --git a/src/utils/isValidColor.test.ts b/src/utils/isValidColor.test.ts index 8d97dbf..580cfd8 100644 --- a/src/utils/isValidColor.test.ts +++ b/src/utils/isValidColor.test.ts @@ -43,6 +43,12 @@ describe('isValidRgb', () => { expect(isValidRgb('rgb(255,255,255)')).toBe(true); }); + it('return true on valid modern syntax rgb values', () => { + expect(isValidRgb('rgb(255 255 255)')).toBe(true); + expect(isValidRgb('rgb(0 0 0)')).toBe(true); + expect(isValidRgb('rgb(100% 100% 100%)')).toBe(true); + }); + it('return false on invalid rgb values', () => { // expect(isValidRgb('rgb(200%, 200%, 200%)')).toBe(false); expect(isValidRgb('rgb(100%, 255, 100%)')).toBe(false); @@ -59,6 +65,12 @@ describe('isValidRgba', () => { expect(isValidRgba('rgba(255,255,255,0.5)')).toBe(true); }); + it('return true on valid modern syntax rgba values', () => { + expect(isValidRgba('rgba(255 255 255 / 0.5)')).toBe(true); + expect(isValidRgba('rgba(0 0 0 / 1)')).toBe(true); + expect(isValidRgba('rgba(100% 100% 100% / 0.5)')).toBe(true); + }); + it('return false on invalid rgba values', () => { // expect(isValidRgba('rgb(200%, 200%, 200%)')).toBe(false); expect(isValidRgba('rgb(100%, 100%, 100%)')).toBe(false); @@ -74,6 +86,11 @@ describe('isValidHsl', () => { expect(isValidHsl('hsl(100,100%,100%)')).toBe(true); }); + it('return true on valid modern syntax hsl values', () => { + expect(isValidHsl('hsl(100 100% 100%)')).toBe(true); + expect(isValidHsl('hsl(0 0% 0%)')).toBe(true); + }); + it('return false on invalid hsl values', () => { expect(isValidHsl('hsl(100%, 100%, 100%)')).toBe(false); expect(isValidHsl('hsl(100%, 255, 100%)')).toBe(false); @@ -86,6 +103,11 @@ describe('isValidHsla', () => { expect(isValidHsla('hsla(100,100%,100%, 1)')).toBe(true); }); + it('return true on valid modern syntax hsla values', () => { + expect(isValidHsla('hsla(100 100% 100% / 0.5)')).toBe(true); + expect(isValidHsla('hsla(0 0% 0% / 1)')).toBe(true); + }); + it('return false on invalid hsla values', () => { expect(isValidHsla('hsla(100%, 100%, 100%)')).toBe(false); expect(isValidHsla('hsla (100%, 255, 100%, 1)')).toBe(false); diff --git a/src/utils/isValidColor.ts b/src/utils/isValidColor.ts index 378b15e..67f5caf 100644 --- a/src/utils/isValidColor.ts +++ b/src/utils/isValidColor.ts @@ -1,76 +1,97 @@ -import { colorTypes } from './colorTypes'; -import { lowerCaseNamedColors } from './namedColors' +import { colorTypes } from "./colorTypes"; +import { lowerCaseNamedColors } from "./namedColors"; const isValidHex6 = (color: string): boolean => { // https://stackoverflow.com/a/8027444/1173898 - if (/^(#)?[0-9A-F]{3}$/i.test(color) || /^(#)?[0-9A-F]{6}$/i.test(color)) { - return true; - } else { - return false; - } -} + return /^(#)?[0-9A-F]{3}$/i.test(color) || /^(#)?[0-9A-F]{6}$/i.test(color); +}; const isValidHex8 = (color: string): boolean => { // https://stackoverflow.com/a/8027444/1173898 - if (/^(#)?[0-9A-F]{4}$/i.test(color) || /^(#)?[0-9A-F]{8}$/i.test(color)) { + return /^(#)?[0-9A-F]{4}$/i.test(color) || /^(#)?[0-9A-F]{8}$/i.test(color); +}; + +const isValidRgb = (color: string): boolean => { + // https://rgxdb.com/r/4LS1LCA + // @todo make sure values are 0-100 for % or 0-255 for unitless + // Modern space syntax: rgb(255 255 255) + if ( + /rgb\(\s*(-?\d+|-?\d*\.\d+)(%?)\s+(-?\d+|-?\d*\.\d+)(\2)\s+(-?\d+|-?\d*\.\d+)(\2)\s*\)/.test(color) + ) { return true; - } else { - return false; } -} + // Legacy comma syntax: rgb(255, 255, 255) + if ( + /rgb\(\s*(-?\d+|-?\d*\.\d+(?=%))(%?)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*\)/.test(color) + ) { + return true; + } + return false; +}; const isValidRgba = (color: string): boolean => { // https://rgxdb.com/r/GFYPX74 // @todo make sure values are 0-100 for % or 0-255 for unitless - if (/rgba\(\s*(-?\d+|-?\d*\.\d+(?=%))(%?)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/.test(color)) { + // Modern space syntax: rgba(255 255 255 / 0.5) + if ( + /rgba\(\s*(-?\d+|-?\d*\.\d+)(%?)\s+(-?\d+|-?\d*\.\d+)(\2)\s+(-?\d+|-?\d*\.\d+)(\2)\s*\/\s*(-?\d+|-?\d*\.\d+)\s*\)/.test(color) + ) { return true; - } else { - return false; } -} - -const isValidRgb = (color: string): boolean => { - // https://rgxdb.com/r/4LS1LCA - // @todo make sure values are 0-100 for % or 0-255 for unitless - if (/rgb\(\s*(-?\d+|-?\d*\.\d+(?=%))(%?)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*\)/.test(color)) { + // Legacy comma syntax: rgba(255, 255, 255, 0.5) + if ( + /rgba\(\s*(-?\d+|-?\d*\.\d+(?=%))(%?)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*,\s*(-?\d+|-?\d*\.\d+(?=%))(\2)\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/.test(color) + ) { return true; - } else { - return false; } -} + return false; +}; -const isValidHsla = (color: string): boolean => { +const isValidHsl = (color: string): boolean => { // https://rgxdb.com/r/6KT5NBF // @todo make sure % are 0-100, // add support for `deg` and `rad` and `turn` - if (/hsla\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/.test(color)) { + // Modern space syntax: hsl(120 100% 50%) + if ( + /hsl\(\s*(-?\d+|-?\d*\.\d+)\s+(-?\d+|-?\d*\.\d+)%\s+(-?\d+|-?\d*\.\d+)%\s*\)/.test(color) + ) { + return true; + } + // Legacy comma syntax: hsl(120, 100%, 50%) + if ( + /hsl\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*\)/.test(color) + ) { return true; - } else { - return false; } -} + return false; +}; -const isValidHsl = (color: string): boolean => { +const isValidHsla = (color: string): boolean => { // https://rgxdb.com/r/6KT5NBF // @todo make sure % are 0-100, // add support for `deg` and `rad` and `turn` - if (/hsl\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*\)/.test(color)) { + // Modern space syntax: hsla(120 100% 50% / 0.5) + if ( + /hsla\(\s*(-?\d+|-?\d*\.\d+)\s+(-?\d+|-?\d*\.\d+)%\s+(-?\d+|-?\d*\.\d+)%\s*\/\s*(-?\d+|-?\d*\.\d+)\s*\)/.test(color) + ) { return true; - } else { - return false; } -} + // Legacy comma syntax: hsla(120, 100%, 50%, 0.5) + if ( + /hsla\(\s*(-?\d+|-?\d*.\d+)\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%\s*,\s*(-?\d+|-?\d*.\d+)\s*\)/.test(color) + ) { + return true; + } + return false; +}; const isValidLch = (color: string): boolean => { // @todo make sure % are 0-100, // add support for `deg` and `rad` and `turn` - let regex = /lch\(((?=\.\d|\d)(?:\d+)?(?:\.?\d*))?%\s+((?=\.\d|\d)(?:\d+)?(?:\.?\d*))(?:(\d+))?\s+((?=\.\d|\d)(?:\d+)?(?:\.?\d*))(?:(\d+))?(\s+(\/\s+((?=\.\d|\d)(?:\d+)?(?:\.?\d*))(\d+)))?%?\)/i; - if (regex.test(color)) { - return true; - } else { - return false; - } -} + const regex = + /lch\(((?=\.\d|\d)(?:\d+)?(?:\.?\d*))?%\s+((?=\.\d|\d)(?:\d+)?(?:\.?\d*))(?:(\d+))?\s+((?=\.\d|\d)(?:\d+)?(?:\.?\d*))(?:(\d+))?(\s+(\/\s+((?=\.\d|\d)(?:\d+)?(?:\.?\d*))(\d+)))?%?\)/i; + return regex.test(color); +}; const isValidColor = (color: string, colorType: colorTypes): boolean => { switch (true) { @@ -78,11 +99,11 @@ const isValidColor = (color: string, colorType: colorTypes): boolean => { case colorType === colorTypes.hex6 && isValidHex6(color): case colorType === colorTypes.picker && isValidHex6(color): case colorType === colorTypes.hex8 && isValidHex8(color): + case colorType === colorTypes.rgb && isValidRgb(color): case colorType === colorTypes.rgba && isValidRgba(color): - case colorType === colorTypes.rgb && isValidRgb(color): + case colorType === colorTypes.hsl && isValidHsl(color): case colorType === colorTypes.hsla && isValidHsla(color): - case colorType === colorTypes.hsl && isValidHsl(color): - case colorType === colorTypes.lch && isValidLch(color): + case colorType === colorTypes.lch && isValidLch(color): case colorType === colorTypes.named && lowerCaseNamedColors.includes(color.toLowerCase()): return true; @@ -91,4 +112,13 @@ const isValidColor = (color: string, colorType: colorTypes): boolean => { } }; -export { isValidColor, isValidHex6, isValidHex8, isValidRgb, isValidRgba, isValidHsl, isValidHsla, isValidLch } +export { + isValidColor, + isValidHex6, + isValidHex8, + isValidRgb, + isValidRgba, + isValidHsl, + isValidHsla, + isValidLch, +}; diff --git a/src/utils/toHex8.ts b/src/utils/toHex8.ts index f293f7e..3615352 100644 --- a/src/utils/toHex8.ts +++ b/src/utils/toHex8.ts @@ -1,5 +1,5 @@ import { colorStringToArray } from './colorStringToArray'; -import { hslaToRgba } from './toRgba'; +import { hslaToRgba, normalizeModernSyntax } from './toRgba'; const rgbaArrayToHex8 = (rgbaArray: Array): string => { let r = (+rgbaArray[0]).toString(16); @@ -20,7 +20,7 @@ const rgbaArrayToHex8 = (rgbaArray: Array): string => { } const rgbaToHex8 = (rgba: string) => { - const rgbaArray: Array = colorStringToArray(rgba, false, 5) as Array; + const rgbaArray: Array = colorStringToArray(normalizeModernSyntax(rgba), false, 5) as Array; const rgbaNumArray: Array = [ parseInt(rgbaArray[0]), diff --git a/src/utils/toHsla.ts b/src/utils/toHsla.ts index 5bce531..f132754 100644 --- a/src/utils/toHsla.ts +++ b/src/utils/toHsla.ts @@ -1,5 +1,6 @@ import { colorStringToArray } from './colorStringToArray'; import { rgbArrayToHsl } from './toHsl'; +import { normalizeModernSyntax } from './toRgba'; const rgbaArrayToHsla = (rgba: Array): Array => { const rgbNumberArray = [rgba[0], rgba[1], rgba[2]] @@ -13,7 +14,7 @@ const rgbaArrayToHsla = (rgba: Array): Array => { } const rgbaToHsla = (rgba: string): Array => { - const rgbaArray = colorStringToArray(rgba, false, 5) as Array + const rgbaArray = colorStringToArray(normalizeModernSyntax(rgba), false, 5) as Array const rgbNumberArray = [parseInt(rgbaArray[0]), parseInt(rgbaArray[1]), parseInt(rgbaArray[2]), parseFloat(rgbaArray[3])] diff --git a/src/utils/toLch.ts b/src/utils/toLch.ts index 875ad53..8353aab 100644 --- a/src/utils/toLch.ts +++ b/src/utils/toLch.ts @@ -1,7 +1,7 @@ import { rgb_array_to_LCH } from './w3conversions'; import { hexToRgb, hslToRgb } from './toRgb'; -import { hex8ToRgba, hslaToRgba } from './toRgba'; +import { hex8ToRgba, hslaToRgba, normalizeModernSyntax } from './toRgba'; import { colorStringToArray } from './colorStringToArray'; const hex6ToLch = (hex6: string): Array => { @@ -36,7 +36,7 @@ const rgbToLch = (rgb: string): Array => { } const rgbaToLch = (rgba: string): Array => { - const rgbaArray = colorStringToArray(rgba, false, 5) as Array; + const rgbaArray = colorStringToArray(normalizeModernSyntax(rgba), false, 5) as Array; const rgbNumberArray = [parseInt(rgbaArray[0]), parseInt(rgbaArray[1]), parseInt(rgbaArray[2])] diff --git a/src/utils/toRgba.test.ts b/src/utils/toRgba.test.ts index bcf001a..4ef0c92 100644 --- a/src/utils/toRgba.test.ts +++ b/src/utils/toRgba.test.ts @@ -69,6 +69,12 @@ describe("HSLa to RGBa conversion", () => { expect(hslaToRgba("hsla(0, 0%, 100%, 1)")[2]).toBe(255); expect(hslaToRgba("hsla(0, 0%, 100%, 1)")[3]).toBe(1); }); + + it("correct rgba for modern space syntax", () => { + expect(hslaToRgba("hsla(0 0% 0% / 1)")).toEqual([0, 0, 0, 1]); + expect(hslaToRgba("hsla(0 0% 100% / 1)")).toEqual([255, 255, 255, 1]); + expect(hslaToRgba("hsla(0 0% 0% / 0.5)")[3]).toBe(0.5); + }); }); describe("rgba to RGBA conversion", () => { @@ -85,6 +91,12 @@ describe("rgba to RGBA conversion", () => { expect(rgbaToRgba("rgba(255, 255, 255, 1)")[2]).toBe(255); expect(rgbaToRgba("rgba(255, 255, 255, 1)")[3]).toBe(1); }); + + it("correct rgba for modern space syntax", () => { + expect(rgbaToRgba("rgba(0 0 0 / 1)")).toEqual([0, 0, 0, 1]); + expect(rgbaToRgba("rgba(255 255 255 / 1)")).toEqual([255, 255, 255, 1]); + expect(rgbaToRgba("rgba(255 0 0 / 0.5)")).toEqual([255, 0, 0, 0.5]); + }); }); // integration diff --git a/src/utils/toRgba.ts b/src/utils/toRgba.ts index 5b45607..157726f 100644 --- a/src/utils/toRgba.ts +++ b/src/utils/toRgba.ts @@ -42,6 +42,20 @@ type Rad = number; type Turn = number; type Hue = Deg | Rad | Turn; +// Converts modern space syntax to legacy comma syntax before parsing. +// e.g. rgba(255 255 255 / 0.6) → rgba(255, 255, 255, 0.6) +// hsla(120 100% 50% / 0.8) → hsla(120, 100%, 50%, 0.8) +const normalizeModernSyntax = (color: string): string => { + if (color.indexOf(',') > -1) return color; + const match = color.match(/^(\w+)\((.+)\)$/); + if (!match) return color; + const [, fn, content] = match; + const normalized = content + .replace(/\s+/g, ', ') + .replace(/,\s*\/\s*,\s*/, ', '); + return `${fn}(${normalized})`; +}; + const stringToHue = (input: string): Hue => { const inputAsNum = Number(input.substr(0, input.length - 3)); @@ -57,6 +71,7 @@ const stringToHue = (input: string): Hue => { }; const hslaToRgba = (hslaArg: string): number[] => { + hslaArg = normalizeModernSyntax(hslaArg); const sep: string = hslaArg.indexOf(",") > -1 ? "," : " "; const hsla: any = hslaArg @@ -130,7 +145,7 @@ const lchToRgba = (lch: string): Array => { } const rgbaToRgba = (color: string): Array => { - return colorStringToArray(color, true, 5) as Array; + return colorStringToArray(normalizeModernSyntax(color), true, 5) as Array; }; const toRgba = (color: string, colorType: colorTypes) => { @@ -156,4 +171,4 @@ const toRgba = (color: string, colorType: colorTypes) => { } }; -export { hex8ToRgba, hslaToRgba, lchToRgba, rgbaToRgba, toRgba }; +export { hex8ToRgba, hslaToRgba, lchToRgba, normalizeModernSyntax, rgbaToRgba, toRgba }; diff --git a/src/utils/translatedColor.test.ts b/src/utils/translatedColor.test.ts index b2a31ea..2b741ec 100644 --- a/src/utils/translatedColor.test.ts +++ b/src/utils/translatedColor.test.ts @@ -3,13 +3,29 @@ import { colorTypes } from './colorTypes'; describe('translatedColor', () => { + it('Should translate modern rgba syntax to all targets', () => { + expect(translatedColor(`rgba(255 0 0 / 0.5)`, colorTypes.rgba, colorTypes.hex6)).toBe('#ff7f7f'); + expect(translatedColor(`rgba(255 0 0 / 0.5)`, colorTypes.rgba, colorTypes.rgb)).toBe('rgb(255 127 127)'); + expect(translatedColor(`rgba(255 0 0 / 0.5)`, colorTypes.rgba, colorTypes.hex8)).toBe('#ff000080'); + expect(translatedColor(`rgba(255 0 0 / 0.5)`, colorTypes.rgba, colorTypes.hsla)).toBeTruthy(); + expect(translatedColor(`rgba(255 0 0 / 0.5)`, colorTypes.rgba, colorTypes.lch)).toBeTruthy(); + }); + + it('Should translate modern hsla syntax to all targets', () => { + expect(translatedColor(`hsla(0 100% 50% / 0.5)`, colorTypes.hsla, colorTypes.hex6)).toBe('#ff7f7f'); + expect(translatedColor(`hsla(0 100% 50% / 0.5)`, colorTypes.hsla, colorTypes.rgb)).toBe('rgb(255 127 127)'); + expect(translatedColor(`hsla(0 100% 50% / 0.5)`, colorTypes.hsla, colorTypes.hex8)).toBeTruthy(); + expect(translatedColor(`hsla(0 100% 50% / 0.5)`, colorTypes.hsla, colorTypes.rgba)).toBeTruthy(); + expect(translatedColor(`hsla(0 100% 50% / 0.5)`, colorTypes.hsla, colorTypes.lch)).toBeTruthy(); + }); + it('Should return correct translated colors', () => { - expect(translatedColor(`#fff`, colorTypes.hex6, colorTypes.rgb)).toBe(`rgb(255, 255, 255)`); - expect(translatedColor(`#fff`, colorTypes.hex6, colorTypes.rgba)).toBe(`rgba(255, 255, 255, 1)`); + expect(translatedColor(`#fff`, colorTypes.hex6, colorTypes.rgb)).toBe(`rgb(255 255 255)`); + expect(translatedColor(`#fff`, colorTypes.hex6, colorTypes.rgba)).toBe(`rgba(255 255 255 / 1)`); expect(translatedColor(`#fff`, colorTypes.hex6, colorTypes.named)).toBe(`White`); expect(translatedColor(`#ff00ff`, colorTypes.hex6, colorTypes.named)).toBe(`Fuchsia`); expect(translatedColor(`rgb(255, 0, 0)`, colorTypes.rgb, colorTypes.named)).toBe(`Red`); - expect(translatedColor(`rgb(255, 0, 0)`, colorTypes.rgb, colorTypes.hsl)).toBe('hsl(0, 100%, 50%)'); + expect(translatedColor(`rgb(255, 0, 0)`, colorTypes.rgb, colorTypes.hsl)).toBe('hsl(0 100% 50%)'); expect(translatedColor(`hsl(200, 66%, 75%)`, colorTypes.hsl, colorTypes.hex6)).toBe('#95cde9'); expect(translatedColor(`lch(54.291% 106.837 40.858)`, colorTypes.lch, colorTypes.hex6)).toBe('#ff0000'); expect(translatedColor(`lch(54.291% 106.837 40.858 / 50%)`, colorTypes.lch, colorTypes.hex8)).toBe('#ff000080'); diff --git a/src/utils/translatedColor.ts b/src/utils/translatedColor.ts index 5ff417d..cf90a92 100644 --- a/src/utils/translatedColor.ts +++ b/src/utils/translatedColor.ts @@ -98,6 +98,8 @@ const translatedColor = ( case targetColorType === colorTypes.hex6: case targetColorType === colorTypes.picker: return rgbArrayToHex(rgbaOverlay); + case targetColorType === colorTypes.rgba: + return formatColor(rgbaAsRgbaArray, colorTypes.rgba); case targetColorType === colorTypes.hex8: return rgbaToHex8(color); case targetColorType === colorTypes.rgb: