pax_global_header00006660000000000000000000000064142236246630014522gustar00rootroot0000000000000052 comment=f998e9b67936dbe4d35e43cc0772717bd9ce8ea2 fabiospampinato-khroma-f998e9b/000077500000000000000000000000001422362466300166165ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/.editorconfig000066400000000000000000000002241422362466300212710ustar00rootroot00000000000000 root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true fabiospampinato-khroma-f998e9b/.gitignore000066400000000000000000000006421422362466300206100ustar00rootroot00000000000000 # Numerous always-ignore extensions *.diff *.err *.log *.orig *.rej *.swo *.swp *.vi *.zip *~ *.sass-cache *.ruby-version *.rbenv-version # OS or Editor folders ._* .cache .DS_Store .idea .project .settings .tmproj *.esproj *.sublime-project *.sublime-workspace nbproject Thumbs.db .fseventsd .DocumentRevisions* .TemporaryItems .Trashes # Other paths to ignore bower_components node_modules package-lock.json dist fabiospampinato-khroma-f998e9b/LICENSE000066400000000000000000000021211422362466300176170ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2019-present Fabio Spampinato, Andrew Maney Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fabiospampinato-khroma-f998e9b/README.md000066400000000000000000000264531422362466300201070ustar00rootroot00000000000000# [Khrôma](https://en.wiktionary.org/wiki/%CF%87%CF%81%E1%BF%B6%CE%BC%CE%B1#Noun) A collection of functions for manipulating CSS colors, inspired by SASS. ## Features - **Small**: the entire library weighs ~5kb (min + gzip) and has no dependencies. - **Fast**: in our benchmark suite functions are executed at ~0.0025ms per call on a mid-2014 MBP. - **Flexible**: All valid CSS colors are supported. - **SASS-like**: if you're familiar with SASS you should feel right at home. ## Install ```sh npm install --save khroma ``` ## Usage ```ts import {red, isDark, darken, change} from 'khroma'; red ( '#ffffff' ); // => 255 isDark ( 'white' ); // => false darken ( 'hsl(0, 5%, 100%)', 50 ); // => 'hsl(0, 5%, 50%)' change ( 'rgb(255, 255, 255)', { a: 0.5 } ); // => 'rgba(255, 255, 255, 0.5)' ``` ## Functions These are all the provided functions, for each of them below you can find a short description, its interface and examples. | Create | Convert | Get channel | Get more | Edit channel | Edit more | | ------------- | ----------------------- | ------------------------- | ----------------------- | --------------------------------- | -------------------- | | [hex](#hex) | [toKeyword](#tokeyword) | [channel](#channel) | [contrast](#contrast) | [saturate](#saturate) | [adjust](#adjust) | | [rgb](#rgb) | [toHex](#tohex) | [red](#red) | [luminance](#luminance) | [desaturate](#desaturate) | [change](#change) | | [rgba](#rgba) | [toRgba](#torgba) | [green](#green) | [isDark](#isdark) | [lighten](#lighten) | [invert](#invert) | | [hsl](#hsl) | [toHsla](#tohsla) | [blue](#blue) | [isLight](#islight) | [darken](#darken) | [mix](#mix) | | [hsla](#hsla) | | [hue](#hue) | [isValid](#isvalid) | [opacify](#opacify) | [scale](#scale) | | | | [saturation](#saturation) | | [fadeIn](#fadein) | | | | | [lightness](#lightness) | | [transparentize](#transparentize) | | | | | [alpha](#alpha) | | [fadeOut](#fadeout) | | | | | [opacity](#opacity) | | [rgba](#rgba-alt) (alt) | | | | | | | [complement](#complement) | | | | | | | [grayscale](#grayscale) | | ### Create These functions create a new color given the provided channels. #### `hex` Alias for [`rgba`](#rgba). #### `rgb` Alias for [`rgba`](#rgba). #### `rgba` Creates a new color given its rgba channels, the alpha channel is optional. ```ts function rgba ( r: string, g: number, b: number, a: number = 1 ): string; ``` ```ts rgba ( 255, 204, 0 ); // => '#ffcc00' rgba ( 255, 204, 0, 0.5 ); // => 'rgba(255, 204, 0, 0.5)' ``` #### `hsl` Alias for [`hsla`](#hsla). #### `hsla` Creates a new color given its hsla channels, the alpha channel is optional. ```ts function hsla ( h: number, s: number, l: number, a: number = 1 ): string; ``` ```ts hsla ( 0, 50, 100 ); // => 'hsl(0, 50%, 100%)' hsla ( 10, 50, 100, 0.5 ); // => 'hsla(10, 50%, 100%, 0.5)' ``` ### Convert These functions convert supported colors to a specific format. #### `toKeyword` Convert a color to the keyword format, when possible. ```ts function toKeyword ( color: string ): string | undefined; ``` ```ts toKeyword ( '#ff0000' ); // => 'red' toKeyword ( '#ffcc00' ); // => undefined ``` #### `toHex` Convert a color to the HEX format. ```ts function toHex ( color: string ): string; ``` ```ts toHex ( 'red' ); // => '#ff0000' toHex ( '#ff0000' ); // => '#ff0000' ``` #### `toRgba` Convert a color to the RGBA format. ```ts function toRgba ( color: string ): string; ``` ```ts toRgba ( 'red' ); // => 'rgb(255, 0, 0)' toRgba ( '#ff0000' ); // => 'rgb(255, 0, 0)' toRgba ( '#00000088' ); // => 'rgba(0, 0, 0, 0.5333333333)' ``` #### `toHsla` Convert a color to the HSLA format. ```ts function toHsla ( color: string ): string; ``` ```ts toHsla ( 'red' ); // => 'hsl(0, 100%, 50%)' toHsla ( '#ff0000' ); // => 'hsl(0, 100%, 50%)' toHsla ( 'rgb(255, 0, 0)' ); // => 'hsl(0, 100%, 50%)' ``` ### Get channel These functions get a single channel from the provided color. #### `channel` Gets any single channel of the color. ```ts function channel ( color: string, channel: 'r' | 'g' | 'b' | 'h' | 's' | 'l' | 'a' ): number; ``` ```ts channel ( '#ffcc00', 'r' ); // => 255 channel ( '#ffcc00', 'h' ); // => 48 channel ( '#ffcc00', 'a' ); // => 1 ``` #### `red` Gets the red channel of the color. ```ts function red ( color: string ): number; ``` ```ts red ( '#ffcc00' ); // => 255 ``` #### `green` Gets the green channel of the color. ```ts function green ( color: string ): number; ``` ```ts green ( '#ffcc00' ); // => 204 ``` #### `blue` Gets the blue channel of the color. ```ts function blue ( color: string ): number; ``` ```ts blue ( '#ffcc00' ); // => 0 ``` #### `hue` Gets the hue channel of the color. ```ts function hue ( color: string ): number; ``` ```ts hue ( 'hsl(0, 50%, 100%)' ); // => 0 ``` #### `saturation` Gets the saturation channel of the color. ```ts function saturation ( color: string ): number; ``` ```ts saturation ( 'hsl(0, 50%, 100%)' ); // => 50 ``` #### `lightness` Gets the lightness channel of the color. ```ts function lightness ( color: string ): number; ``` ```ts lightness ( 'hsl(0, 50%, 100%)' ); // => 100 ``` #### `alpha` Gets the alpha channel of the color. ```ts function alpha ( color: string ): number; ``` ```ts alpha ( '#ffcc00' ); // => 1 alpha ( 'rgba(255, 205, 0, 0.5)' ); // => 0.5 ``` #### `opacity` Alias for [`alpha`](#alpha). ### Get more These functions get some other information from the provided color. #### `contrast` Gets the contrast in luminance between two colors. Contrast values go between 1 and 10. 1 means same color, >= 4 means decent contrast, >= 7 means great contrast, 10 means great contrast. ```ts function contrast ( color1: string, color2: string ): number; ``` ```ts contrast ( '#000000', '#000000' ); // => 1 contrast ( '#000000', '#ffffff' ); // => 10 contrast ( '#888888', '#ffffff' ); // => 4.0617165366 ``` #### `luminance` Gets the [relative luminance](https://en.wikipedia.org/wiki/Relative_luminance) of the color. ```ts function luminance ( color: string ): number; ``` ```ts luminance ( 'black' ); // => 0 luminance ( 'white' ); // => 1 luminance ( '#ffcc00' ); // => 0.6444573127 ``` #### `isDark` Checks if the provided color is a dark color. ```ts function isDark ( color: string ): number; ``` ```ts isDark ( 'black' ); // => true isDark ( 'white' ); // => false isDark ( '#ffcc00' ); // => false ``` #### `isLight` Checks if the provided color is a light color. ```ts function isLight ( color: string ): number; ``` ```ts isLight ( 'black' ); // => false isLight ( 'white' ); // => true isLight ( '#ffcc00' ); // => true ``` #### `isValid` Checks if the provided color is a valid color. ```ts function isLight ( color: string ): boolean; ``` ```ts isValid ( 'black' ); // => true isValid ( '#ffcc00' ); // => true isValid ( '#wtf' ); // => false ``` ### Edit channel These functions change a single channel of the provided color. #### `saturate` Increases the saturation channel of the color. ```ts function saturate ( color: string, amount: number ): string; ``` ```ts saturate ( 'hsl(0, 50%, 50%)', 25 ); // => 'hsl(0, 75%, 50%)' ``` #### `desaturate` Decreases the saturation channel of the color. ```ts function desaturate ( color: string, amount: number ): string; ``` ```ts desaturate ( 'hsl(0, 50%, 50%)', 25 ); // => 'hsl(0, 25%, 50%)' ``` #### `lighten` Increases the lightness channel of the color. ```ts function lighten ( color: string, amount: number ): string; ``` ```ts lighten ( 'hsl(0, 50%, 50%)', 25 ); // => 'hsl(0, 50%, 75%)' ``` #### `darken` Decreases the lightness channel of the color. ```ts function darken ( color: string, amount: number ): string; ``` ```ts darken ( 'hsl(0, 50%, 50%)', 25 ); // => 'hsl(0, 50%, 25%)' ``` #### `opacify` Increases the opacity channel of the color. ```ts function opacify ( color: string, amount: number ): string; ``` ```ts opacify ( 'rgba(255, 204, 0, 0.5)', 0.25 ); // => 'rgba(255, 204, 0, 0.75)' ``` #### `fadeIn` Alias for [`opacify`](#opacify). #### `transparentize` Decreases the opacity channel of the color. ```ts function transparentize ( color: string, amount: number ): string; ``` ```ts transparentize ( 'rgba(255, 204, 0, 0.5)', 0.25 ); // => 'rgba(255, 204, 0, 0.25)' ``` #### `fadeOut` Alias for [`transparentize`](#transparentize). #### `rgba` (alt) Sets a new value for the opacity channel. ```ts function rgba ( color: string, amount: number ): string; ``` ```ts rgba ( 'rgba(255, 204, 0, 0.5)', 0.1 ); // => 'rgba(255, 204, 0, 0.1)' ``` #### `complement` Gets the complement of the color, rotating its hue channel by 180 degrees. ```ts function complement ( color: string ): string; ``` ```ts complement ( '#ffcc00' ); // => 'hsl(228, 100%, 50%)' ``` #### `grayscale` Gets the grayscale version of the color, setting its saturation to 0. ```ts function grayscale ( color: string ): string; ``` ```ts grayscale ( '#ffcc00' ); // => 'hsl(48, 0%, 50%)' ``` ### Edit more These functions can/will change more than a single channel at once of the provided color. #### `adjust` Increases or decreases the value of any channel of the color. ```ts function adjust ( color: string, channels: Record<'r' | 'g' | 'b' | 'h' | 's' | 'l' | 'a', number> ): string; ``` ```ts adjust ( '#ffcc00', { r: -10, g: 200 } ); // => '#f5ff00' adjust ( '#ffcc00', { a: -0.5 } ); // => 'rgba(255, 204, 0, 0.5)' adjust ( '#ffcc00', { h: 50, l: -30 } ); // => 'hsl(98, 100%, 20%)' ``` #### `change` Sets a new value for any channel of the color. ```ts function change ( color: string, channels: Record<'r' | 'g' | 'b' | 'h' | 's' | 'l' | 'a', number> ): string; ``` ```ts change ( '#ffcc00', { r: 10, g: 200 } ); // => '#0ac800' change ( '#ffcc00', { a: 0.5 } ); // => 'rgba(255, 204, 0, 0.5)' change ( '#ffcc00', { h: 50, l: 30 } ); // => 'hsl(50, 100%, 30%)' ``` #### `invert` Gets the inverse of the color. ```ts function invert ( color: string, weight: number = 100 ): string; ``` ```ts invert ( '#ffcc00' ); // => '#0033ff' invert ( '#ffcc00', 50 ); // => '#808080' ``` #### `mix` Mixes two colors together. ```ts function mix ( color1: string, color2: string, weight: number = 50 ): string; ``` ```ts mix ( 'red', 'blue' ); // => '#800080' mix ( 'red', 'blue', 15 ); // => '#2600d9' ``` #### `scale` Scales any channel of the color. ```ts function scale ( color: string, channels: Record<'r' | 'g' | 'b' | 'h' | 's' | 'l' | 'a', number> ): string; ``` ```ts scale ( '#ffcc00', { r: -50, b: 10 } ); // => '#80cc1a' ``` ## License MIT © Fabio Spampinato, Andrew Maney fabiospampinato-khroma-f998e9b/package.json000077500000000000000000000016271422362466300211150ustar00rootroot00000000000000{ "name": "khroma", "repository": "github:fabiospampinato/khroma", "description": "A collection of functions for manipulating CSS colors, inspired by SASS.", "version": "2.0.0", "type": "module", "sideEffects": false, "main": "dist/index.js", "exports": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { "benchmark": "tsex benchmark", "benchmark:watch": "tsex benchmark --watch", "clean": "tsex clean", "compile": "tsex compile", "compile:watch": "tsex compile --watch", "test": "tsex test", "test:watch": "tsex test --watch", "prepublishOnly": "npm run clean && npm run compile && npm run test" }, "keywords": [ "sass", "color", "manipulation", "manipulate", "css", "hex", "rgb", "hsl" ], "devDependencies": { "benchloop": "^1.3.2", "fava": "^0.0.6", "tsex": "^1.1.0", "typescript": "^4.6.3" } } fabiospampinato-khroma-f998e9b/src/000077500000000000000000000000001422362466300174055ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/src/channels/000077500000000000000000000000001422362466300212005ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/src/channels/index.ts000066400000000000000000000066661422362466300226750ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Type from '~/channels/type'; import {TYPE} from '~/constants'; import type {RGBA, HSLA, CHANNELS} from '~/types'; /* MAIN */ class Channels { /* VARIABLES */ color?: string; changed: boolean; data: CHANNELS; //TSC: It should really be "Partial", but TS gets excessively noisy type: Type; /* CONSTRUCTOR */ constructor ( data: RGBA | HSLA | CHANNELS, color?: string ) { this.color = color; this.changed = false; this.data = data as CHANNELS; //TSC this.type = new Type (); } /* API */ set ( data: RGBA | HSLA | CHANNELS, color?: string ): this { this.color = color; this.changed = false; this.data = data as CHANNELS; //TSC this.type.type = TYPE.ALL; return this; } /* HELPERS */ _ensureHSL (): void { const data = this.data; const {h, s, l} = data; if ( h === undefined ) data.h = _.channel.rgb2hsl ( data, 'h' ); if ( s === undefined ) data.s = _.channel.rgb2hsl ( data, 's' ); if ( l === undefined ) data.l = _.channel.rgb2hsl ( data, 'l' ); } _ensureRGB (): void { const data = this.data; const {r, g, b} = data; if ( r === undefined ) data.r = _.channel.hsl2rgb ( data, 'r' ); if ( g === undefined ) data.g = _.channel.hsl2rgb ( data, 'g' ); if ( b === undefined ) data.b = _.channel.hsl2rgb ( data, 'b' ); } /* GETTERS */ get r (): number { const data = this.data; const r = data.r; if ( !this.type.is ( TYPE.HSL ) && r !== undefined ) return r; this._ensureHSL (); return _.channel.hsl2rgb ( data, 'r' ); } get g (): number { const data = this.data; const g = data.g; if ( !this.type.is ( TYPE.HSL ) && g !== undefined ) return g; this._ensureHSL (); return _.channel.hsl2rgb ( data, 'g' ); } get b (): number { const data = this.data; const b = data.b; if ( !this.type.is ( TYPE.HSL ) && b !== undefined ) return b; this._ensureHSL (); return _.channel.hsl2rgb ( data, 'b' ); } get h (): number { const data = this.data; const h = data.h; if ( !this.type.is ( TYPE.RGB ) && h !== undefined ) return h; this._ensureRGB (); return _.channel.rgb2hsl ( data, 'h' ); } get s (): number { const data = this.data; const s = data.s; if ( !this.type.is ( TYPE.RGB ) && s !== undefined ) return s; this._ensureRGB (); return _.channel.rgb2hsl ( data, 's' ); } get l (): number { const data = this.data; const l = data.l; if ( !this.type.is ( TYPE.RGB ) && l !== undefined ) return l; this._ensureRGB (); return _.channel.rgb2hsl ( data, 'l' ); } get a (): number { return this.data.a; } /* SETTERS */ set r ( r: number ) { this.type.set ( TYPE.RGB ); this.changed = true; this.data.r = r; } set g ( g: number ) { this.type.set ( TYPE.RGB ); this.changed = true; this.data.g = g; } set b ( b: number ) { this.type.set ( TYPE.RGB ); this.changed = true; this.data.b = b; } set h ( h: number ) { this.type.set ( TYPE.HSL ); this.changed = true; this.data.h = h; } set s ( s: number ) { this.type.set ( TYPE.HSL ); this.changed = true; this.data.s = s; } set l ( l: number ) { this.type.set ( TYPE.HSL ); this.changed = true; this.data.l = l; } set a ( a: number ) { this.changed = true; this.data.a = a; } } /* EXPORT */ export default Channels; fabiospampinato-khroma-f998e9b/src/channels/reusable.ts000066400000000000000000000002641422362466300233540ustar00rootroot00000000000000 /* IMPORT */ import Channels from '~/channels'; /* MAIN */ const channels = new Channels ( { r: 0, g: 0, b: 0, a: 0 }, 'transparent' ); /* EXPORT */ export default channels; fabiospampinato-khroma-f998e9b/src/channels/type.ts000066400000000000000000000010211422362466300225230ustar00rootroot00000000000000 /* IMPORT */ import {TYPE} from '~/constants'; /* MAIN */ class Type { /* VARIABLES */ type: number = TYPE.ALL; /* API */ get (): number { return this.type; } set ( type: number ): void { if ( this.type && this.type !== type ) throw new Error ( 'Cannot change both RGB and HSL channels at the same time' ); this.type = type; } reset (): void { this.type = TYPE.ALL; } is ( type: number ): boolean { return this.type === type; } } /* EXPORT */ export default Type; fabiospampinato-khroma-f998e9b/src/color/000077500000000000000000000000001422362466300205235ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/src/color/hex.ts000066400000000000000000000030421422362466300216560ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import ChannelsReusable from '~/channels/reusable'; import {DEC2HEX} from '~/constants'; import type {Channels} from '~/types'; /* MAIN */ const Hex = { /* VARIABLES */ re: /^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i, /* API */ parse: ( color: string ): Channels | void => { if ( color.charCodeAt ( 0 ) !== 35 ) return; // '#' const match = color.match ( Hex.re ); if ( !match ) return; const hex = match[1]; const dec = parseInt ( hex, 16 ); const length = hex.length; const hasAlpha = length % 4 === 0; const isFullLength = length > 4; const multiplier = isFullLength ? 1 : 17; const bits = isFullLength ? 8 : 4; const bitsOffset = hasAlpha ? 0 : -1; const mask = isFullLength ? 255 : 15; return ChannelsReusable.set ({ r: ( ( dec >> ( bits * ( bitsOffset + 3 ) ) ) & mask ) * multiplier, g: ( ( dec >> ( bits * ( bitsOffset + 2 ) ) ) & mask ) * multiplier, b: ( ( dec >> ( bits * ( bitsOffset + 1 ) ) ) & mask ) * multiplier, a: hasAlpha ? ( dec & mask ) * multiplier / 255 : 1 }, color ); }, stringify: ( channels: Channels ): string => { const {r, g, b, a} = channels; if ( a < 1 ) { // #RRGGBBAA return `#${DEC2HEX[Math.round ( r )]}${DEC2HEX[Math.round ( g )]}${DEC2HEX[Math.round ( b )]}${DEC2HEX[Math.round ( a * 255 )]}`; } else { // #RRGGBB return `#${DEC2HEX[Math.round ( r )]}${DEC2HEX[Math.round ( g )]}${DEC2HEX[Math.round ( b )]}`; } } }; /* EXPORT */ export default Hex; fabiospampinato-khroma-f998e9b/src/color/hsl.ts000066400000000000000000000036621422362466300216700ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import ChannelsReusable from '~/channels/reusable'; import type {Channels} from '~/types'; /* MAIN */ const HSL = { /* VARIABLES */ re: /^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i, hueRe: /^(.+?)(deg|grad|rad|turn)$/i, /* HELPERS */ _hue2deg: ( hue: string ): number => { const match = hue.match ( HSL.hueRe ); if ( match ) { const [, number, unit] = match; switch ( unit ) { case 'grad': return _.channel.clamp.h ( parseFloat ( number ) * .9 ); case 'rad': return _.channel.clamp.h ( parseFloat ( number ) * 180 / Math.PI ); case 'turn': return _.channel.clamp.h ( parseFloat ( number ) * 360 ); } } return _.channel.clamp.h ( parseFloat ( hue ) ); }, /* API */ parse: ( color: string ): Channels | void => { const charCode = color.charCodeAt ( 0 ); if ( charCode !== 104 && charCode !== 72 ) return; // 'h'/'H' const match = color.match ( HSL.re ); if ( !match ) return; const [, h, s, l, a, isAlphaPercentage] = match; return ChannelsReusable.set ({ h: HSL._hue2deg ( h ), s: _.channel.clamp.s ( parseFloat ( s ) ), l: _.channel.clamp.l ( parseFloat ( l ) ), a: a ? _.channel.clamp.a ( isAlphaPercentage ? parseFloat ( a ) / 100 : parseFloat ( a ) ) : 1 }, color ); }, stringify: ( channels: Channels ): string => { const {h, s, l, a} = channels; if ( a < 1 ) { // HSLA return `hsla(${_.lang.round ( h )}, ${_.lang.round ( s )}%, ${_.lang.round ( l )}%, ${a})`; } else { // HSL return `hsl(${_.lang.round ( h )}, ${_.lang.round ( s )}%, ${_.lang.round ( l )}%)`; } } }; /* EXPORT */ export default HSL; fabiospampinato-khroma-f998e9b/src/color/index.ts000066400000000000000000000026531422362466300222100ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Hex from '~/color/hex'; import HSL from '~/color/hsl'; import Keyword from '~/color/keyword'; import RGB from '~/color/rgb'; import {TYPE} from '~/constants'; import type {Channels} from '~/types'; /* MAIN */ const Color = { /* VARIABLES */ format: { keyword: Keyword, hex: Hex, rgb: RGB, rgba: RGB, hsl: HSL, hsla: HSL }, /* API */ parse: ( color: string | Channels ): Channels => { if ( typeof color !== 'string' ) return color; const channels = Hex.parse ( color ) || RGB.parse ( color ) || HSL.parse ( color ) || Keyword.parse ( color ); // Color providers ordered with performance in mind if ( channels ) return channels; throw new Error ( `Unsupported color format: "${color}"` ); }, stringify: ( channels: Channels ): string => { // SASS returns a keyword if possible, but we avoid doing that as it's slower and doesn't really add any value if ( !channels.changed && channels.color ) return channels.color; if ( channels.type.is ( TYPE.HSL ) || channels.data.r === undefined ) { return HSL.stringify ( channels ); } else if ( channels.a < 1 || !Number.isInteger ( channels.r ) || !Number.isInteger ( channels.g ) || !Number.isInteger ( channels.b ) ) { return RGB.stringify ( channels ); } else { return Hex.stringify ( channels ); } } }; /* EXPORT */ export default Color; fabiospampinato-khroma-f998e9b/src/color/keyword.ts000066400000000000000000000105421422362466300225610ustar00rootroot00000000000000 /* IMPORT */ import Hex from '~/color/hex'; import type {Channels} from '~/types'; /* MAIN */ const Keyword = { /* VARIABLES */ colors: { aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c', cyanaqua: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9', darkgreen: '#006400', darkgrey: '#a9a9a9', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkslategrey: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dimgrey: '#696969', dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080', green: '#008000', greenyellow: '#adff2f', grey: '#808080', honeydew: '#f0fff0', hotpink: '#ff69b4', indianred: '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgreen: '#90ee90', lightgrey: '#d3d3d3', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslategray: '#778899', lightslategrey: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370db', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee', palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', rebeccapurple: '#663399', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', slategrey: '#708090', snow: '#fffafa', springgreen: '#00ff7f', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8', transparent: '#00000000', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32' }, /* API */ parse: ( color: string ): Channels | void => { color = color.toLowerCase (); const hex = Keyword.colors[color]; if ( !hex ) return; return Hex.parse ( hex ); }, stringify: ( channels: Channels ): string | undefined => { const hex = Hex.stringify ( channels ); for ( const name in Keyword.colors ) { if ( Keyword.colors[name] === hex ) return name; } return; } }; /* EXPORT */ export default Keyword; fabiospampinato-khroma-f998e9b/src/color/rgb.ts000066400000000000000000000031311422362466300216430ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import ChannelsReusable from '~/channels/reusable'; import type {Channels} from '~/types'; /* MAIN */ const RGB = { /* VARIABLES */ re: /^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i, /* API */ parse: ( color: string ): Channels | void => { const charCode = color.charCodeAt ( 0 ); if ( charCode !== 114 && charCode !== 82 ) return; // 'r'/'R' const match = color.match ( RGB.re ); if ( !match ) return; const [, r, isRedPercentage, g, isGreenPercentage, b, isBluePercentage, a, isAlphaPercentage] = match; return ChannelsReusable.set ({ r: _.channel.clamp.r ( isRedPercentage ? parseFloat ( r ) * 2.55 : parseFloat ( r ) ), g: _.channel.clamp.g ( isGreenPercentage ? parseFloat ( g ) * 2.55 : parseFloat ( g ) ), b: _.channel.clamp.b ( isBluePercentage ? parseFloat ( b ) * 2.55 : parseFloat ( b ) ), a: a ? _.channel.clamp.a ( isAlphaPercentage ? parseFloat ( a ) / 100 : parseFloat ( a ) ) : 1 }, color ); }, stringify: ( channels: Channels ): string => { const {r, g, b, a} = channels; if ( a < 1 ) { // RGBA return `rgba(${_.lang.round ( r )}, ${_.lang.round ( g )}, ${_.lang.round ( b )}, ${_.lang.round ( a )})`; } else { // RGB return `rgb(${_.lang.round ( r )}, ${_.lang.round ( g )}, ${_.lang.round ( b )})`; } } }; /* EXPORT */ export default RGB; fabiospampinato-khroma-f998e9b/src/constants.ts000066400000000000000000000005251422362466300217730ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; /* MAIN */ const DEC2HEX: Record = {}; for ( let i = 0; i <= 255; i++ ) DEC2HEX[i] = _.unit.dec2hex ( i ); // Populating dynamically, striking a balance between code size and performance const TYPE = { ALL: 0, RGB: 1, HSL: 2 }; /* EXPORT */ export {DEC2HEX, TYPE}; fabiospampinato-khroma-f998e9b/src/index.ts000077500000000000000000000000521422362466300210640ustar00rootroot00000000000000 /* EXPORT */ export * from '~/methods'; fabiospampinato-khroma-f998e9b/src/methods/000077500000000000000000000000001422362466300210505ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/src/methods/adjust.ts000066400000000000000000000007641422362466300227210ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; import change from '~/methods/change'; import type {CHANNELS, Channels} from '~/types'; /* MAIN */ const adjust = ( color: string | Channels, channels: Partial ): string => { const ch = Color.parse ( color ); const changes: Partial = {}; for ( const c in channels ) { if ( !channels[c] ) continue; changes[c] = ch[c] + channels[c]; } return change ( color, changes ); }; /* EXPORT */ export default adjust; fabiospampinato-khroma-f998e9b/src/methods/adjust_channel.ts000066400000000000000000000010431422362466300244000ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Color from '~/color'; import type {CHANNEL, Channels} from '~/types'; /* MAIN */ const adjustChannel = ( color: string | Channels, channel: CHANNEL, amount: number ): string => { const channels = Color.parse ( color ); const amountCurrent = channels[channel]; const amountNext = _.channel.clamp[channel]( amountCurrent + amount ); if ( amountCurrent !== amountNext ) channels[channel] = amountNext; return Color.stringify ( channels ); }; /* EXPORT */ export default adjustChannel; fabiospampinato-khroma-f998e9b/src/methods/alpha.ts000066400000000000000000000003571422362466300225120ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const alpha = ( color: string | Channels ): number => { return channel ( color, 'a' ); }; /* EXPORT */ export default alpha; fabiospampinato-khroma-f998e9b/src/methods/blue.ts000066400000000000000000000003551422362466300223520ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const blue = ( color: string | Channels ): number => { return channel ( color, 'b' ); }; /* EXPORT */ export default blue; fabiospampinato-khroma-f998e9b/src/methods/change.ts000066400000000000000000000006401422362466300226450ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Color from '~/color'; import type {CHANNELS, Channels} from '~/types'; /* MAIN */ const change = ( color: string | Channels, channels: Partial ): string => { const ch = Color.parse ( color ); for ( const c in channels ) { ch[c] = _.channel.clamp[c]( channels[c] ); } return Color.stringify ( ch ); }; /* EXPORT */ export default change; fabiospampinato-khroma-f998e9b/src/methods/channel.ts000066400000000000000000000004641422362466300230340ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Color from '~/color'; import type {CHANNEL, Channels} from '~/types'; /* MAIN */ const channel = ( color: string | Channels, channel: CHANNEL ): number => { return _.lang.round ( Color.parse ( color )[channel] ); }; /* EXPORT */ export default channel; fabiospampinato-khroma-f998e9b/src/methods/complement.ts000066400000000000000000000004211422362466300235600ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const complement = ( color: string | Channels ): string => { return adjustChannel ( color, 'h', 180 ); }; /* EXPORT */ export default complement; fabiospampinato-khroma-f998e9b/src/methods/contrast.ts000066400000000000000000000010131422362466300232500ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import luminance from '~/methods/luminance'; /* MAIN */ const contrast = ( color1: string, color2: string ): number => { const luminance1 = luminance ( color1 ); const luminance2 = luminance ( color2 ); const max = Math.max ( luminance1, luminance2 ); const min = Math.min ( luminance1, luminance2 ); const ratio = ( max + Number.EPSILON ) / ( min + Number.EPSILON ); return _.lang.round ( _.lang.clamp ( ratio, 1, 10 ) ); }; /* EXPORT */ export default contrast; fabiospampinato-khroma-f998e9b/src/methods/darken.ts000066400000000000000000000004351422362466300226660ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const darken = ( color: string | Channels, amount: number ): string => { return adjustChannel ( color, 'l', -amount ); }; /* EXPORT */ export default darken; fabiospampinato-khroma-f998e9b/src/methods/desaturate.ts000066400000000000000000000004451422362466300235640ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const desaturate = ( color: string | Channels, amount: number ): string => { return adjustChannel ( color, 's', -amount ); }; /* EXPORT */ export default desaturate; fabiospampinato-khroma-f998e9b/src/methods/grayscale.ts000066400000000000000000000003711422362466300233730ustar00rootroot00000000000000 /* IMPORT */ import change from '~/methods/change'; import type {Channels} from '~/types'; /* MAIN */ const grayscale = ( color: string | Channels ): string => { return change ( color, { s: 0 } ); }; /* EXPORT */ export default grayscale; fabiospampinato-khroma-f998e9b/src/methods/green.ts000066400000000000000000000003571422362466300225250ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const green = ( color: string | Channels ): number => { return channel ( color, 'g' ); }; /* EXPORT */ export default green; fabiospampinato-khroma-f998e9b/src/methods/hsla.ts000066400000000000000000000007241422362466300223520ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import ChannelsReusable from '~/channels/reusable'; import Color from '~/color'; /* MAIN */ const hsla = ( h: number, s: number, l: number, a: number = 1 ): string => { const channels = ChannelsReusable.set ({ h: _.channel.clamp.h ( h ), s: _.channel.clamp.s ( s ), l: _.channel.clamp.l ( l ), a: _.channel.clamp.a ( a ) }); return Color.stringify ( channels ); }; /* EXPORT */ export default hsla; fabiospampinato-khroma-f998e9b/src/methods/hue.ts000066400000000000000000000003531422362466300222020ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const hue = ( color: string | Channels ): number => { return channel ( color, 'h' ); }; /* EXPORT */ export default hue; fabiospampinato-khroma-f998e9b/src/methods/index.ts000066400000000000000000000041251422362466300225310ustar00rootroot00000000000000 /* IMPORT */ import hex from '~/methods/rgba'; // Alias import rgb from '~/methods/rgba'; // Alias import rgba from '~/methods/rgba'; import hsl from '~/methods/hsla'; // Alias import hsla from '~/methods/hsla'; import toKeyword from '~/methods/to_keyword'; import toHex from '~/methods/to_hex'; import toRgba from '~/methods/to_rgba'; import toHsla from '~/methods/to_hsla'; import channel from '~/methods/channel'; import red from '~/methods/red'; import green from '~/methods/green'; import blue from '~/methods/blue'; import hue from '~/methods/hue'; import saturation from '~/methods/saturation'; import lightness from '~/methods/lightness'; import alpha from '~/methods/alpha'; import opacity from '~/methods/alpha'; // Alias import contrast from '~/methods/contrast'; import luminance from '~/methods/luminance'; import isDark from '~/methods/is_dark'; import isLight from '~/methods/is_light'; import isValid from '~/methods/is_valid'; import saturate from '~/methods/saturate'; import desaturate from '~/methods/desaturate'; import lighten from '~/methods/lighten'; import darken from '~/methods/darken'; import opacify from '~/methods/opacify'; import fadeIn from '~/methods/opacify'; // Alias import transparentize from '~/methods/transparentize'; import fadeOut from '~/methods/transparentize'; // Alias import complement from '~/methods/complement'; import grayscale from '~/methods/grayscale'; import adjust from '~/methods/adjust'; import change from '~/methods/change'; import invert from '~/methods/invert'; import mix from '~/methods/mix'; import scale from '~/methods/scale'; /* EXPORT */ export { /* CREATE */ hex, rgb, rgba, hsl, hsla, /* CONVERT */ toKeyword, toHex, toRgba, toHsla, /* GET - CHANNEL */ channel, red, green, blue, hue, saturation, lightness, alpha, opacity, /* GET - MORE */ contrast, luminance, isDark, isLight, isValid, /* EDIT - CHANNEL */ saturate, desaturate, lighten, darken, opacify, fadeIn, transparentize, fadeOut, complement, grayscale, /* EDIT - MORE */ adjust, change, invert, mix, scale }; fabiospampinato-khroma-f998e9b/src/methods/invert.ts000066400000000000000000000006541422362466300227340ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; import mix from '~/methods/mix'; import type {Channels} from '~/types'; /* MAIN */ const invert = ( color: string | Channels, weight: number = 100 ): string => { const inverse = Color.parse ( color ); inverse.r = 255 - inverse.r; inverse.g = 255 - inverse.g; inverse.b = 255 - inverse.b; return mix ( inverse, color, weight ); }; /* EXPORT */ export default invert; fabiospampinato-khroma-f998e9b/src/methods/is_dark.ts000066400000000000000000000003571422362466300230410ustar00rootroot00000000000000 /* IMPORT */ import isLight from '~/methods/is_light'; import type {Channels} from '~/types'; /* MAIN */ const isDark = ( color: string | Channels ): boolean => { return !isLight ( color ); }; /* EXPORT */ export default isDark; fabiospampinato-khroma-f998e9b/src/methods/is_light.ts000066400000000000000000000003731422362466300232250ustar00rootroot00000000000000 /* IMPORT */ import luminance from '~/methods/luminance'; import type {Channels} from '~/types'; /* MAIN */ const isLight = ( color: string | Channels ): boolean => { return luminance ( color ) >= .5; }; /* EXPORT */ export default isLight; fabiospampinato-khroma-f998e9b/src/methods/is_valid.ts000066400000000000000000000003601422362466300232110ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; /* MAIN */ const isValid = ( color: string ): boolean => { try { Color.parse ( color ); return true; } catch { return false; } }; /* EXPORT */ export default isValid; fabiospampinato-khroma-f998e9b/src/methods/lighten.ts000066400000000000000000000004361422362466300230550ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const lighten = ( color: string | Channels, amount: number ): string => { return adjustChannel ( color, 'l', amount ); }; /* EXPORT */ export default lighten; fabiospampinato-khroma-f998e9b/src/methods/lightness.ts000066400000000000000000000003671422362466300234260ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const lightness = ( color: string | Channels ): number => { return channel ( color, 'l' ); }; /* EXPORT */ export default lightness; fabiospampinato-khroma-f998e9b/src/methods/luminance.ts000066400000000000000000000007271422362466300234010ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Color from '~/color'; import type {Channels} from '~/types'; /* MAIN */ //SOURCE: https://planetcalc.com/7779 const luminance = ( color: string | Channels ): number => { const {r, g, b} = Color.parse ( color ); const luminance = .2126 * _.channel.toLinear ( r ) + .7152 * _.channel.toLinear ( g ) + .0722 * _.channel.toLinear ( b ); return _.lang.round ( luminance ); }; /* EXPORT */ export default luminance; fabiospampinato-khroma-f998e9b/src/methods/mix.ts000066400000000000000000000021601422362466300222140ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; import rgba from '~/methods/rgba'; import type {Channels} from '~/types'; /* MAIN */ //SOURCE: https://github.com/sass/dart-sass/blob/7457d2e9e7e623d9844ffd037a070cf32d39c348/lib/src/functions/color.dart#L718-L756 const mix = ( color1: string | Channels, color2: string | Channels, weight: number = 50 ): string => { const {r: r1, g: g1, b: b1, a: a1} = Color.parse ( color1 ); const {r: r2, g: g2, b: b2, a: a2} = Color.parse ( color2 ); const weightScale = weight / 100; const weightNormalized = ( weightScale * 2 ) - 1; const alphaDelta = a1 - a2; const weight1combined = ( ( weightNormalized * alphaDelta ) === -1 ) ? weightNormalized : ( weightNormalized + alphaDelta ) / ( 1 + weightNormalized * alphaDelta ); const weight1 = ( weight1combined + 1 ) / 2; const weight2 = 1 - weight1; const r = ( r1 * weight1 ) + ( r2 * weight2 ); const g = ( g1 * weight1 ) + ( g2 * weight2 ); const b = ( b1 * weight1 ) + ( b2 * weight2 ); const a = ( a1 * weightScale ) + ( a2 * ( 1 - weightScale ) ); return rgba ( r, g, b, a ); }; /* EXPORT */ export default mix; fabiospampinato-khroma-f998e9b/src/methods/opacify.ts000066400000000000000000000004361422362466300230550ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const opacify = ( color: string | Channels, amount: number ): string => { return adjustChannel ( color, 'a', amount ); }; /* EXPORT */ export default opacify; fabiospampinato-khroma-f998e9b/src/methods/red.ts000066400000000000000000000003531422362466300221730ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const red = ( color: string | Channels ): number => { return channel ( color, 'r' ); }; /* EXPORT */ export default red; fabiospampinato-khroma-f998e9b/src/methods/rgba.ts000066400000000000000000000014751422362466300223420ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import ChannelsReusable from '~/channels/reusable'; import Color from '~/color'; import change from '~/methods/change'; import type {Channels} from '~/types'; /* TYPES */ type IRgba = { ( color: string | Channels, opacity: number ): string, ( r: number, g: number, b: number, a?: number ): string }; /* MAIN */ const rgba: IRgba = ( r: string | Channels | number, g: number, b: number = 0, a: number = 1 ): string => { //TSC: `b` shouldn't have a default value if ( typeof r !== 'number' ) return change ( r, { a: g } ); const channels = ChannelsReusable.set ({ r: _.channel.clamp.r ( r ), g: _.channel.clamp.g ( g ), b: _.channel.clamp.b ( b ), a: _.channel.clamp.a ( a ) }); return Color.stringify ( channels ); }; /* EXPORT */ export default rgba; fabiospampinato-khroma-f998e9b/src/methods/saturate.ts000066400000000000000000000004401422362466300232460ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const saturate = ( color: string | Channels, amount: number ): string => { return adjustChannel ( color, 's', amount ); }; /* EXPORT */ export default saturate; fabiospampinato-khroma-f998e9b/src/methods/saturation.ts000066400000000000000000000003711422362466300236120ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/methods/channel'; import type {Channels} from '~/types'; /* MAIN */ const saturation = ( color: string | Channels ): number => { return channel ( color, 's' ); }; /* EXPORT */ export default saturation; fabiospampinato-khroma-f998e9b/src/methods/scale.ts000066400000000000000000000012301422362466300225030ustar00rootroot00000000000000 /* IMPORT */ import _ from '~/utils'; import Color from '~/color'; import adjust from '~/methods/adjust'; import type {CHANNELS, Channels} from '~/types'; /* MAIN */ const scale = ( color: string | Channels, channels: Partial ): string => { const ch = Color.parse ( color ); const adjustments: Partial = {}; const delta = ( amount: number, weight: number, max: number ) => weight > 0 ? ( max - amount ) * weight / 100 : amount * weight / 100; for ( const c in channels ) { adjustments[c] = delta ( ch[c], channels[c], _.channel.max[c] ); } return adjust ( color, adjustments ); }; /* EXPORT */ export default scale; fabiospampinato-khroma-f998e9b/src/methods/to_hex.ts000066400000000000000000000003171422362466300227070ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; /* MAIN */ const toHex = ( color: string ): string => { return Color.format.hex.stringify ( Color.parse ( color ) ); }; /* EXPORT */ export default toHex; fabiospampinato-khroma-f998e9b/src/methods/to_hsla.ts000066400000000000000000000003221422362466300230460ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; /* MAIN */ const toHsla = ( color: string ): string => { return Color.format.hsla.stringify ( Color.parse ( color ) ); }; /* EXPORT */ export default toHsla; fabiospampinato-khroma-f998e9b/src/methods/to_keyword.ts000066400000000000000000000003471422362466300236120ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; /* MAIN */ const toKeyword = ( color: string ): string | undefined => { return Color.format.keyword.stringify ( Color.parse ( color ) ); }; /* EXPORT */ export default toKeyword; fabiospampinato-khroma-f998e9b/src/methods/to_rgba.ts000066400000000000000000000003221422362466300230320ustar00rootroot00000000000000 /* IMPORT */ import Color from '~/color'; /* MAIN */ const toRgba = ( color: string ): string => { return Color.format.rgba.stringify ( Color.parse ( color ) ); }; /* EXPORT */ export default toRgba; fabiospampinato-khroma-f998e9b/src/methods/transparentize.ts000066400000000000000000000004551422362466300244750ustar00rootroot00000000000000 /* IMPORT */ import adjustChannel from '~/methods/adjust_channel'; import type {Channels} from '~/types'; /* MAIN */ const transparentize = ( color: string | Channels, amount: number ): string => { return adjustChannel ( color, 'a', -amount ); }; /* EXPORT */ export default transparentize; fabiospampinato-khroma-f998e9b/src/types.ts000066400000000000000000000011051422362466300211160ustar00rootroot00000000000000 /* IMPORT */ import type Channels from './channels'; /* MAIN */ type ALPHA = { a: number // Alpha (0~1) }; type RGB = { r: number, // Red (0~255) g: number, // Green (0~255) b: number // Blue (0~255) }; type RGBA = RGB & ALPHA; type HSL = { h: number, // Hue (0~360) s: number, // Saturation (0~100) l: number // Lightness (0~100) }; type HSLA = HSL & ALPHA; type CHANNEL = 'r' | 'g' | 'b' | 'h' | 's' | 'l' | 'a'; type CHANNELS = Record; /* EXPORT */ export type {Channels}; export type {ALPHA, RGB, RGBA, HSL, HSLA, CHANNEL, CHANNELS}; fabiospampinato-khroma-f998e9b/src/utils/000077500000000000000000000000001422362466300205455ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/src/utils/channel.ts000066400000000000000000000046721422362466300225360ustar00rootroot00000000000000 /* IMPORT */ import type {RGB, HSL} from '~/types'; /* MAIN */ const Channel = { /* CLAMP */ min: { r: 0, g: 0, b: 0, s: 0, l: 0, a: 0 }, max: { r: 255, g: 255, b: 255, h: 360, s: 100, l: 100, a: 1 }, clamp: { r: ( r: number ) => r >= 255 ? 255 : ( r < 0 ? 0 : r ), g: ( g: number ) => g >= 255 ? 255 : ( g < 0 ? 0 : g ), b: ( b: number ) => b >= 255 ? 255 : ( b < 0 ? 0 : b ), h: ( h: number ) => h % 360, s: ( s: number ) => s >= 100 ? 100 : ( s < 0 ? 0 : s ), l: ( l: number ) => l >= 100 ? 100 : ( l < 0 ? 0 : l ), a: ( a: number ) => a >= 1 ? 1 : ( a < 0 ? 0 : a ) }, /* CONVERSION */ //SOURCE: https://planetcalc.com/7779 toLinear: ( c: number ): number => { const n = c / 255; return c > .03928 ? Math.pow ( ( ( n + .055 ) / 1.055 ), 2.4 ) : n / 12.92; }, //SOURCE: https://gist.github.com/mjackson/5311256 hue2rgb: ( p: number, q: number, t: number ): number => { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1/6 ) return p + ( q - p ) * 6 * t; if ( t < 1/2 ) return q; if ( t < 2/3 ) return p + ( q - p ) * ( 2/3 - t ) * 6; return p; }, hsl2rgb: ( { h, s, l }: HSL, channel: keyof RGB ): number => { if ( !s ) return l * 2.55; // Achromatic h /= 360; s /= 100; l /= 100; const q = ( l < .5 ) ? l * ( 1 + s ) : ( l + s ) - ( l * s ); const p = 2 * l - q; switch ( channel ) { case 'r': return Channel.hue2rgb ( p, q, h + 1/3 ) * 255; case 'g': return Channel.hue2rgb ( p, q, h ) * 255; case 'b': return Channel.hue2rgb ( p, q, h - 1/3 ) * 255; } }, rgb2hsl: ( { r, g, b }: RGB, channel: keyof HSL ): number => { r /= 255; g /= 255; b /= 255; const max = Math.max ( r, g, b ); const min = Math.min ( r, g, b ); const l = ( max + min ) / 2; if ( channel === 'l' ) return l * 100; if ( max === min ) return 0; // Achromatic const d = max - min; const s = ( l > .5 ) ? d / ( 2 - max - min ) : d / ( max + min ); if ( channel === 's' ) return s * 100; switch ( max ) { case r: return ( ( g - b ) / d + ( g < b ? 6 : 0 ) ) * 60; case g: return ( ( b - r ) / d + 2 ) * 60; case b: return ( ( r - g ) / d + 4 ) * 60; default: return -1; //TSC: TypeScript is stupid and complains if there isn't this useless default statement } } }; /* EXPORT */ export default Channel; fabiospampinato-khroma-f998e9b/src/utils/index.ts000066400000000000000000000003271422362466300222260ustar00rootroot00000000000000 /* IMPORT */ import channel from '~/utils/channel'; import lang from '~/utils/lang'; import unit from '~/utils/unit'; /* MAIN */ const Utils = { channel, lang, unit }; /* EXPORT */ export default Utils; fabiospampinato-khroma-f998e9b/src/utils/lang.ts000066400000000000000000000006601422362466300220400ustar00rootroot00000000000000 /* MAIN */ const Lang = { /* API */ clamp: ( number: number, lower: number, upper: number ): number => { if ( lower > upper ) return Math.min ( lower, Math.max ( upper, number ) ); return Math.min ( upper, Math.max ( lower, number ) ); }, round: ( number: number ): number => { // 10 digits rounding return Math.round ( number * 10000000000 ) / 10000000000; } }; /* EXPORT */ export default Lang; fabiospampinato-khroma-f998e9b/src/utils/unit.ts000066400000000000000000000003421422362466300220730ustar00rootroot00000000000000 /* MAIN */ const Unit = { /* API */ dec2hex: ( dec: number ): string => { const hex = Math.round ( dec ).toString ( 16 ); return hex.length > 1 ? hex : `0${hex}`; } }; /* EXPORT */ export default Unit; fabiospampinato-khroma-f998e9b/tasks/000077500000000000000000000000001422362466300177435ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/tasks/benchmark.js000066400000000000000000000143051422362466300222360ustar00rootroot00000000000000 /* IMPORT */ import benchmark from 'benchloop'; import Color from '../dist/color/index.js'; import {hex, rgb, rgba, hsl, hsla, channel, red, green, blue, alpha, hue, saturation, lightness, darken, lighten, opacify, transparentize, saturate, desaturate, grayscale, invert, complement, scale, adjust, change, mix, contrast, luminance, isDark, isLight, toKeyword, toHex, toRgba, toHsla} from '../dist/index.js'; /* MAIN */ benchmark.defaultOptions = Object.assign ( benchmark.defaultOptions, { log: 'compact', iterations: 2500 }); benchmark.group ( 'parse', () => { benchmark ({ name: 'keyword', fn: () => { Color.parse ( 'blue' ); } }); benchmark.group ( 'hex', () => { benchmark ({ name: 'rgb', fn: () => { Color.parse ( '#fc0' ); } }); benchmark ({ name: 'rgba', fn: () => { Color.parse ( '#fc08' ); } }); benchmark ({ name: 'rrggbb', fn: () => { Color.parse ( '#ffcc00' ); } }); benchmark ({ name: 'rrggbbaa', fn: () => { Color.parse ( '#ffcc0088' ); } }); }); benchmark.group ( 'rgb', () => { benchmark ({ name: 'rgb', fn: () => { Color.parse ( 'rgb(255, 204, 0)' ); } }); benchmark ({ name: 'rgba', fn: () => { Color.parse ( 'rgb(255, 204, 0, .5)' ); } }); benchmark ({ name: 'rgba:percentage', fn: () => { Color.parse ( 'rgb(100%, 80%, 0%, .5)' ); } }); benchmark ({ name: 'rgba:scientific', fn: () => { Color.parse ( 'rgba(1e2, .5e1, .5e0, +.25e2%)' ); } }); }); benchmark.group ( 'hsl', () => { benchmark ({ name: 'hsl', fn: () => { Color.parse ( 'hsl(150, 50%, 50%)' ); } }); benchmark ({ name: 'hsla', fn: () => { Color.parse ( 'hsla(150, 50%, 50%, .5)' ); } }); benchmark ({ name: 'hsla:deg', fn: () => { Color.parse ( 'hsla(0deg, 50%, 50%, .5)' ); } }); benchmark ({ name: 'hsla:scientific', fn: () => { Color.parse ( 'hsla(1e2, 2e1%, .5e2%, +.25e2%)' ); } }); benchmark ({ name: 'hsla:grad', fn: () => { Color.parse ( 'hsla(0grad, 50%, 50%, .5)' ); } }); benchmark ({ name: 'hsla:rad', fn: () => { Color.parse ( 'hsla(3.14rad, 50%, 50%, .5)' ); } }); benchmark ({ name: 'hsla:turn', fn: () => { Color.parse ( 'hsla(1turn, 50%, 50%, .5)' ); } }); }); }); benchmark.group ( 'stringify', () => { const channels = Color.parse ( '#ff00ff' ); benchmark ({ name: 'keyword', fn: () => { toKeyword ( channels ); } }); benchmark ({ name: 'hex', fn: () => { toHex ( channels ); } }); benchmark ({ name: 'rgba', fn: () => { toRgba ( channels ); } }); benchmark ({ name: 'hsla', fn: () => { toHsla ( channels ); } }); }); benchmark.group ( 'create', () => { benchmark ({ name: 'hex', fn: () => { hex ( 255, 204, 0 ); } }); benchmark ({ name: 'rgb', fn: () => { rgb ( 255, 204, 0 ); } }); benchmark ({ name: 'rgba', fn: () => { rgba ( 255, 204, 0, 136 ); } }); benchmark ({ name: 'hsl', fn: () => { hsl ( 150, 50, 50 ); } }); benchmark ({ name: 'hsla', fn: () => { hsla ( 150, 50, 50, .5 ); } }); }); benchmark.group ( 'get.channel', () => { benchmark ({ name: 'channel', fn: () => { channel ( '#ffcc00', 'r' ); } }); benchmark ({ name: 'red', fn: () => { red ( '#ffcc00' ); } }); benchmark ({ name: 'green', fn: () => { green ( '#ffcc00' ); } }); benchmark ({ name: 'blue', fn: () => { blue ( '#ffcc00' ); } }); benchmark ({ name: 'hue', fn: () => { hue ( '#ffcc00' ); } }); benchmark ({ name: 'saturation', fn: () => { saturation ( '#ffcc00' ); } }); benchmark ({ name: 'lightness', fn: () => { lightness ( '#ffcc00' ); } }); benchmark ({ name: 'alpha', fn: () => { alpha ( '#ffcc00' ); } }); }); benchmark.group ( 'get.more', () => { benchmark ({ name: 'contrast', fn: () => { contrast ( '#000000', '#ffffff' ); } }); benchmark ({ name: 'luminance', fn: () => { luminance ( '#ffcc00' ); } }); benchmark ({ name: 'isDark', fn: () => { isDark ( '#ffcc00' ); } }); benchmark ({ name: 'isLight', fn: () => { isLight ( '#ffcc00' ); } }); }); benchmark.group ( 'edit.channel', () => { benchmark ({ name: 'saturate', fn: () => { saturate ( '#ffcc00', 50 ); } }); benchmark ({ name: 'desaturate', fn: () => { desaturate ( '#ffcc00', 50 ); } }); benchmark ({ name: 'lighten', fn: () => { lighten ( '#ffcc00', 50 ); } }); benchmark ({ name: 'darken', fn: () => { darken ( '#ffcc00', 50 ); } }); benchmark ({ name: 'opacify', fn: () => { opacify ( '#ffcc00', .5 ); } }); benchmark ({ name: 'transparentize', fn: () => { transparentize ( '#ffcc00', .5 ); } }); benchmark ({ name: 'rgba', fn: () => { rgba ( '#ffcc00', .5 ); } }); benchmark ({ name: 'complement', fn: () => { complement ( '#ffcc00' ); } }); benchmark ({ name: 'grayscale', fn: () => { grayscale ( '#ffcc00' ); } }); }); benchmark.group ( 'edit.more', () => { benchmark ({ name: 'adjust', fn: () => { adjust ( '#ffcc00', { a: -.5 } ); } }); benchmark ({ name: 'change', fn: () => { change ( '#ffcc00', { a: .5 } ); } }); benchmark ({ name: 'invert', fn: () => { invert ( '#ffcc00', 50 ); } }); benchmark ({ name: 'mix', fn: () => { mix ( '#ffcc00', '#000000', 50 ); } }); benchmark ({ name: 'scale', fn: () => { scale ( '#ffcc00', { r: 50, g: 50, b: 50 } ); } }); }); benchmark.summary (); fabiospampinato-khroma-f998e9b/test/000077500000000000000000000000001422362466300175755ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/test/color/000077500000000000000000000000001422362466300207135ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/test/color/hex.js000066400000000000000000000024221422362466300220350ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'Hex', it => { it ( 'parses hex colors', t => { const tests = [ /* RGB */ ['#000', '#000000'], ['#fff', '#ffffff'], ['#a2b', '#aa22bb'], ['#a2B', '#aa22bb'], /* RGBA */ ['#0000', '#00000000'], ['#fffF', '#ffffff'], ['#fff8', '#ffffff88'], ['#a2bf', '#aa22bb'], ['#a2Bf', '#aa22bb'], /* RRGGBB */ ['#000000', '#000000'], ['#FFFFFF', '#ffffff'], ['#ffffff', '#ffffff'], ['#ae12b4', '#ae12b4'], ['#Ae12B4', '#ae12b4'], /* RRGGBBAA */ ['#000000ff', '#000000'], ['#00000000', '#00000000'], ['#ffffffa8', '#ffffffa8'], ['#ffffffA8', '#ffffffa8'] ]; tests.forEach ( ([ input, output ]) => { t.is ( Color.format.hex.stringify ( Color.parse ( input ) ), output ); }); }); it ( 'throws with unsupported colors', t => { const colors = [ '#', '#0', '#00', '#ggg', '#zzz', 'fff', '#0 0 0', '# 000', '#aabbc', '#aabbccd', '#aabbccdde' ]; colors.forEach ( color => { t.throws ( () => Color.parse ( color ), /unsupported/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/color/hsl.js000066400000000000000000000063771422362466300220540ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'HSL', it => { it ( 'parses HSL colors', t => { const tests = [ /* NO UNIT HUE */ ['hsl(0, 0%, 0%)', 'hsl(0, 0%, 0%)'], ['hsl(0, 100%, 0%)', 'hsl(0, 100%, 0%)'], ['hsl(0, 0%, 100%)', 'hsl(0, 0%, 100%)'], ['hsl(0, 100%, 100%)', 'hsl(0, 100%, 100%)'], ['hsl(180, 50%, 50%)', 'hsl(180, 50%, 50%)'], ['hsl(180, 40.4%, 70.4%)', 'hsl(180, 40.4%, 70.4%)'], /* S & L CLAMPING - H WRAPPING */ ['hsl(-180, 40.4%, 70.4%)', 'hsl(-180, 40.4%, 70.4%)'], ['hsl(180, -40.4%, -70.4%)', 'hsl(180, 0%, 0%)'], ['hsl(180, 400.4%, 700.4%)', 'hsl(180, 100%, 100%)'], ['hsl(540, 100%, 50%)', 'hsl(180, 100%, 50%)'], /* DEGREES */ ['hsl(-180deg, 50%, 50%)', 'hsl(-180, 50%, 50%)'], ['hsl(0deg, 50%, 50%)', 'hsl(0, 50%, 50%)'], ['hsl(180deg, 50%, 50%)', 'hsl(180, 50%, 50%)'], ['hsl(360deg, 50%, 50%)', 'hsl(0, 50%, 50%)'], ['hsl(540deg, 50%, 50%)', 'hsl(180, 50%, 50%)'], /* GRADIANS */ ['hsl(0grad, 50%, 50%)', 'hsl(0, 50%, 50%)'], ['hsl(200grad, 50%, 50%)', 'hsl(180, 50%, 50%)'], ['hsl(400grad, 50%, 50%)', 'hsl(0, 50%, 50%)'], /* RADIANS */ ['hsl(0rad, 50%, 50%)', 'hsl(0, 50%, 50%)'], ['hsl(3.14159265359rad, 50%, 50%)', 'hsl(180, 50%, 50%)'], /* TURNS */ ['hsl(0turn, 50%, 50%)', 'hsl(0, 50%, 50%)'], ['hsl(0.5turn, 50%, 50%)', 'hsl(180, 50%, 50%)'], ['hsl(1turn, 50%, 50%)', 'hsl(0, 50%, 50%)'], /* SCIENTIFIC NOTATION */ ['hsl(1.8e2, 4.04e1%, .704e2%)', 'hsl(180, 40.4%, 70.4%)'], ['hsl(1.8e2deg, 4.04e1%, .704e2%)', 'hsl(180, 40.4%, 70.4%)'], ['hsl(1800e-1deg, 404e-1%, 7040e-2%)', 'hsl(180, 40.4%, 70.4%)'], /* WITH COMMAS AND WEIRD SPACES */ ['hsl( 0 , 0% , 0% )', 'hsl(0, 0%, 0%)'], ['hsl(0,0%,0%)', 'hsl(0, 0%, 0%)'], /* WITHOUT COMMAS */ ['hsl(0 0% 0%)', 'hsl(0, 0%, 0%)'], ['hsl( 0 0% 0% )', 'hsl(0, 0%, 0%)'], ['hsl(180deg 50% 50%)', 'hsl(180, 50%, 50%)'], ['hsl(180 40.4% 70.4%)', 'hsl(180, 40.4%, 70.4%)'], /* WEIRD CASING */ ['HSL(0 0% 0%)', 'hsl(0, 0%, 0%)'], ['hSl(0 0% 0%)', 'hsl(0, 0%, 0%)'] ]; tests.forEach ( ([ input, output ]) => { t.is ( Color.format.hsl.stringify ( Color.parse ( input ) ), output ); }); }); it ( 'throws with unsupported colors', t => { const colors = [ 'hsl', 'hsl()', 'hsl(0, 0)', 'hsl(0, 1, dog)', 'hsl(0de, 0, 0)', 'hsl(0gra, 0, 0)', 'hsl(1, 2, 3, 4, 5)', 'hsl(1/2/3)', 'hsl(1,, 20, 255)', 'hsl(1%,, 20%, 255%)', 'hsl(1%, 255%)', 'hsl(1%, 10%%, 255%)', 'hsl(1%, %10%, 255%)', 'hsl(1)', 'hsl(1, 20, 255', 'hsl 1, 20, 255', 'hsl 1, 20, 255)', 'hsl(1, a, 255)', 'hsl (1, 2, 255)', 'hsl(1,2,3..5)', 'hsl(1, 2, 3.4.5)', 'hslQ(1, 2, 255)', 'h s l(1, 2, 255)', 'hsl(0, 0, 0)', 'hsl(0, 100, 0)', 'hsl(0, 0, 100)', 'hsl(0, 100, 100)', 'hsl(180, 50, 50)', 'hsl(180, 40.4, 70.4)' ]; colors.forEach ( color => { t.throws ( () => Color.parse ( color ), /unsupported/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/color/hsla.js000066400000000000000000000055741422362466300222130ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'HSLA', it => { it ( 'parses HSLA colors', t => { const tests = [ /* NO UNIT HUE */ ['hsla(0, 0%, 0%, 0)', 'hsla(0, 0%, 0%, 0)'], ['hsla(0, 0%, 0%)', 'hsl(0, 0%, 0%)'], ['hsla(0, 0%, 0%, 10)', 'hsl(0, 0%, 0%)'], ['hsla(0, 0%, 0%, -10)', 'hsla(0, 0%, 0%, 0)'], ['hsla(0, 0%, 0%, 0.5)', 'hsla(0, 0%, 0%, 0.5)'], ['hsla(0, 0%, 0%, .5)', 'hsla(0, 0%, 0%, 0.5)'], ['hsla(0, 0%, 0%, 0.5 )', 'hsla(0, 0%, 0%, 0.5)'], /* PERCENTAGE 0~100 */ ['hsla(0, 0%, 0%, 0%)', 'hsla(0, 0%, 0%, 0)'], ['hsla(0, 0%, 0%, 100%)', 'hsl(0, 0%, 0%)'], ['hsla(0, 0%, 0%, 110%)', 'hsl(0, 0%, 0%)'], ['hsla(0, 0%, 0%, -110%)', 'hsla(0, 0%, 0%, 0)'], ['hsla(0, 0%, 0%, 50%)', 'hsla(0, 0%, 0%, 0.5)'], ['hsla(0, 0%, 0%, 50.5%)', 'hsla(0, 0%, 0%, 0.505)'], /* WITH COMMAS AND WEIRD SPACES */ ['hsla( 1 , 20% , 50%, 0.5 )', 'hsla(1, 20%, 50%, 0.5)'], ['hsla(1,20%,50%,50%)', 'hsla(1, 20%, 50%, 0.5)'], ['hsla( 1,20%,50%,0.5 )', 'hsla(1, 20%, 50%, 0.5)'], /* WITH SLASH */ ['hsla(0 0% 0% / 0.4)', 'hsla(0, 0%, 0%, 0.4)'], ['hsla(0 0% 0%/0.4)', 'hsla(0, 0%, 0%, 0.4)'], ['hsla(0 0% 0% / 40%)', 'hsla(0, 0%, 0%, 0.4)'], ['hsla(0, 0%, 0% / 40%)', 'hsla(0, 0%, 0%, 0.4)'], ['hsla(0,0%,0%/40%)', 'hsla(0, 0%, 0%, 0.4)'], /* SCIENTIFIC NOTATION */ ['hsla(1e2, 2e1%, .5e2%, +.25e2%)', 'hsla(100, 20%, 50%, 0.25)'], ['hsla(1e2, 2e1%, .5e2%, +.25e1%)', 'hsla(100, 20%, 50%, 0.025)'], ['hsla(1e2, 2e1%, .5e2%, +.25e0%)', 'hsla(100, 20%, 50%, 0.0025)'], ['hsla(1e2, 2e1%, .5e2%, .25e0)', 'hsla(100, 20%, 50%, 0.25)'], ['hsla(1e2, 2e1%, .5e2%, .25e1)', 'hsl(100, 20%, 50%)'], ['hsla(1e2, 2e1%, .5e2%, 25e-2)', 'hsla(100, 20%, 50%, 0.25)'], /* MIXED UNITS */ ['hsla(1, 20%, .5e2%, +.25e2%)', 'hsla(1, 20%, 50%, 0.25)'], /* WEIRD CASING */ ['HSLA(1, 20%, 50%, 0.5)', 'hsla(1, 20%, 50%, 0.5)'], ['hSlA(1, 20%, 50%, 0.5)', 'hsla(1, 20%, 50%, 0.5)'] ]; tests.forEach ( ([ input, output ]) => { t.is ( Color.format.hsla.stringify ( Color.parse ( input ) ), output ); }); }); it ( 'throws with unsupported colors', t => { const colors = [ 'hsla()', 'hsla(51 170 51 0.4)', 'hsla(1, 2, 3, 4, 5)', 'hsla(0, 0, 0, 0..5)', 'hsla(0, 0, 0, 0.5.)', 'hsla(0, 0, 0, 0%%)', 'hsla(51 170 51 // 0.4)', 'hsla(1ee2, .5e1, .5e0, +.25e2%)', 'hsla(1f2, .5e1, .5e0, +.25e2%)', 'hsla(0, 0, 0, 0)', 'hsla(0, 100, 0, 0)', 'hsla(0, 0, 100, 0)', 'hsla(0, 100, 100, 0)', 'hsla(180, 50, 50, 0)', 'hsla(180, 40.4, 70.4, 0)' ]; colors.forEach ( color => { t.throws ( () => Color.parse ( color ), /unsupported/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/color/keyword.js000066400000000000000000000012161422362466300227350ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'Keyword', it => { it ( 'parses keywords colors', t => { const tests = [ ['black', '#000000'], ['BLACK', '#000000'], ['tranSpaRent', '#00000000'] ]; tests.forEach ( ([ input, output ]) => { t.is ( Color.format.hex.stringify ( Color.parse ( input ) ), output ); }); }); it ( 'throws with unsupported colors', t => { const colors = [ 'foo', 'bar' ]; colors.forEach ( color => { t.throws ( () => Color.parse ( color ), /unsupported/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/color/rgb.js000066400000000000000000000036261422362466300220320ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'RGB', it => { it ( 'parses RGB colors', t => { const tests = [ /* DECIAML 0~255 */ ['rgb(1, 20, 255)', 'rgb(1, 20, 255)'], ['rgb(1.99, 20.5, 255)', 'rgb(1.99, 20.5, 255)'], ['rgb(300, 255, -100)', 'rgb(255, 255, 0)'], /* PERCENTAGE 0~100 */ ['rgb(10%, 20%, 30%)', 'rgb(25.5, 51, 76.5)'], ['rgb(10.5%, 20.7%, 30%)', 'rgb(26.775, 52.785, 76.5)'], ['rgb(100% 200% -30%)', 'rgb(255, 255, 0)'], /* WITH COMMAS AND WEIRD SPACES */ ['rgb( 1 , 20 , 255 )', 'rgb(1, 20, 255)'], ['rgb(1,20,255)', 'rgb(1, 20, 255)'], ['rgb( 1,20,255 )', 'rgb(1, 20, 255)'], /* WITHOUT COMMAS */ ['rgb(10% 20% 30%)', 'rgb(25.5, 51, 76.5)'], ['rgb(1 20 255)', 'rgb(1, 20, 255)'], ['rgb( 1 20 255 )', 'rgb(1, 20, 255)'], /* MIXED UNITS */ ['rgb(10% 20 30%)', 'rgb(25.5, 20, 76.5)'], ['rgb(1 25.5 25.5)', 'rgb(1, 25.5, 25.5)'], /* WEIRD CASING */ ['RGB(1, 20, 255)', 'rgb(1, 20, 255)'], ['rGb(1, 20, 255)', 'rgb(1, 20, 255)'] ]; tests.forEach ( ([ input, output ]) => { t.is ( Color.format.rgb.stringify ( Color.parse ( input ) ), output ); }); }); it ( 'throws with unsupported colors', t => { const colors = [ 'rgb()', 'rgb(1, 2, 3, 4, 5)', 'rgb(1/2/3)', 'rgb(1,, 20, 255)', 'rgb(1%,, 20%, 255%)', 'rgb(1%, 255%)', 'rgb(1%, 10%%, 255%)', 'rgb(1%, %10%, 255%)', 'rgb(1)', 'rgb(1, 20, 255', 'rgb 1, 20, 255', 'rgb 1, 20, 255)', 'rgb(1, a, 255)', 'rgb (1, 2, 255)', 'rgb(1,2,3..5)', 'rgb(1, 2, 3.4.5)', 'rgbQ(1, 2, 255)', 'r g b(1, 2, 255)' ]; colors.forEach ( color => { t.throws ( () => Color.parse ( color ), /unsupported/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/color/rgba.js000066400000000000000000000051161422362466300221670ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'RGBA', it => { it ( 'parses RGBA colors', t => { const tests = [ /* FRACTION 0~1 */ ['rgba(0, 0, 0, 0)', 'rgba(0, 0, 0, 0)'], ['rgba(0, 0, 0)', 'rgb(0, 0, 0)'], ['rgba(0, 0, 0, 10)', 'rgb(0, 0, 0)'], ['rgba(0, 0, 0, -10)', 'rgba(0, 0, 0, 0)'], ['rgba(0, 0, 0, 0.5)', 'rgba(0, 0, 0, 0.5)'], ['rgba(0, 0, 0, .5)', 'rgba(0, 0, 0, 0.5)'], ['rgba(0, 0, 0, 0.5 )', 'rgba(0, 0, 0, 0.5)'], /* PERCENTAGE 0~100 */ ['rgba(0, 0, 0, 0%)', 'rgba(0, 0, 0, 0)'], ['rgba(0, 0, 0, 100%)', 'rgb(0, 0, 0)'], ['rgba(0, 0, 0, 110%)', 'rgb(0, 0, 0)'], ['rgba(0, 0, 0, -110%)', 'rgba(0, 0, 0, 0)'], ['rgba(0, 0, 0, 50%)', 'rgba(0, 0, 0, 0.5)'], ['rgba(0, 0, 0, 50.5%)', 'rgba(0, 0, 0, 0.505)'], /* WITH COMMAS AND WEIRD SPACES */ ['rgba( 1 , 20 , 255, 0.5 )', 'rgba(1, 20, 255, 0.5)'], ['rgba(1,20,255,50%)', 'rgba(1, 20, 255, 0.5)'], ['rgba( 1,20,255,0.5 )', 'rgba(1, 20, 255, 0.5)'], /* WITH SLASH */ ['rgba(51 170 51 / 0.4)', 'rgba(51, 170, 51, 0.4)'], ['rgba(51 170 51/0.4)', 'rgba(51, 170, 51, 0.4)'], ['rgba(51 170 51 / 40%)', 'rgba(51, 170, 51, 0.4)'], ['rgba(51, 170, 51 / 40%)', 'rgba(51, 170, 51, 0.4)'], ['rgba(51,170,51/40%)', 'rgba(51, 170, 51, 0.4)'], /* SCIENTIFIC NOTATION */ ['rgba(1e2, .5e1, .5e0, +.25e2%)', 'rgba(100, 5, 0.5, 0.25)'], ['rgba(1e2, .5e1, .5e0, +.25e1%)', 'rgba(100, 5, 0.5, 0.025)'], ['rgba(1e2, .5e1, .5e0, +.25e0%)', 'rgba(100, 5, 0.5, 0.0025)'], ['rgba(1e2, .5e1, .5e0, .25e0)', 'rgba(100, 5, 0.5, 0.25)'], ['rgba(1e2, .5e1, .5e0, .25e1)', 'rgb(100, 5, 0.5)'], /* MIXED UNITS */ ['rgba(1, 10%, .5e0, +.25e2%)', 'rgba(1, 25.5, 0.5, 0.25)'], /* WEIRD CASING */ ['RGBA(1, 20, 255, 0.5)', 'rgba(1, 20, 255, 0.5)'], ['rgbA(1, 20, 255, 0.5)', 'rgba(1, 20, 255, 0.5)'] ]; tests.forEach ( ([ input, output ]) => { t.is ( Color.format.rgba.stringify ( Color.parse ( input ) ), output ); }); }); it ( 'throws with unsupported colors', t => { const colors = [ 'rgba()', 'rgba(51 170 51 0.4)', 'rgba(1, 2, 3, 4, 5)', 'rgba(0, 0, 0, 0..5)', 'rgba(0, 0, 0, 0.5.)', 'rgba(0, 0, 0, 0%%)', 'rgba(51 170 51 // 0.4)', 'rgba(1ee2, .5e1, .5e0, +.25e2%)', 'rgba(1f2, .5e1, .5e0, +.25e2%)' ]; colors.forEach ( color => { t.throws ( () => Color.parse ( color ), /unsupported/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/000077500000000000000000000000001422362466300212405ustar00rootroot00000000000000fabiospampinato-khroma-f998e9b/test/methods/adjust.js000066400000000000000000000061101422362466300230660ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {adjust} from '../../dist/index.js'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'adjust', it => { it ( 'increases or decreases the value of any RGB channel of the color', t => { const tests = [ [['rgb(0, 0, 0)', { r: 100 }], 'rgb(100, 0, 0)'], [['rgb(0, 0, 0)', { g: 100 }], 'rgb(0, 100, 0)'], [['rgb(0, 0, 0)', { b: 100 }], 'rgb(0, 0, 100)'], [['rgb(0, 0, 0)', { r: 100, g: 100 }], 'rgb(100, 100, 0)'], [['rgb(0, 0, 0)', { r: 100, g: 100, b: 100 }], 'rgb(100, 100, 100)'], [['rgb(255, 255, 255)', { r: -100 }], 'rgb(155, 255, 255)'], [['rgb(255, 255, 255)', { g: -100 }], 'rgb(255, 155, 255)'], [['rgb(255, 255, 255)', { b: -100 }], 'rgb(255, 255, 155)'], [['rgb(200, 0, 0)', { r: 100 }], 'rgb(255, 0, 0)'], [['rgb(100, 0, 0)', { r: -200 }], 'rgb(0, 0, 0)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( Color.format.rgb.stringify ( Color.parse ( adjust ( ...args ) ) ), output ); }); }); it ( 'increases or decreases the value of any HSL channel of the color', t => { const tests = [ [['hsl(0, 50%, 50%)', { h: 100 }], 'hsl(100, 50%, 50%)'], [['hsl(0, 50%, 50%)', { s: 25 }], 'hsl(0, 75%, 50%)'], [['hsl(0, 50%, 50%)', { l: 25 }], 'hsl(0, 50%, 75%)'], [['hsl(0, 50%, 50%)', { h: 100, s: 25 }], 'hsl(100, 75%, 50%)'], [['hsl(0, 50%, 50%)', { h: 100, s: 25, l: 25 }], 'hsl(100, 75%, 75%)'], [['hsl(100, 50%, 50%)', { h: -100 }], 'hsl(0, 50%, 50%)'], [['hsl(0, 50%, 50%)', { s: -25 }], 'hsl(0, 25%, 50%)'], [['hsl(0, 50%, 50%)', { l: -25 }], 'hsl(0, 50%, 25%)'], [['hsl(300, 50%, 50%)', { h: 100 }], 'hsl(40, 50%, 50%)'], [['hsl(0, 50%, 50%)', { h: -100 }], 'hsl(-100, 50%, 50%)'], [['hsl(0, 100%, 50%)', { s: 25 }], 'hsl(0, 100%, 50%)'], [['hsl(0, 0%, 50%)', { s: -25 }], 'hsl(0, 0%, 50%)'], [['hsla(0, 0%, 50%, .5)', { s: -25 }], 'hsla(0, 0%, 50%, 0.5)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( Color.format.hsl.stringify ( Color.parse ( adjust ( ...args ) ) ), output ); }); }); it ( 'increases or decreases the value of the alpha channel of the color', t => { const tests = [ [['rgba(0, 0, 0, 0)', { a: 0.5 }], 'rgba(0, 0, 0, 0.5)'], [['hsla(0, 0%, 0%, 0)', { a: 0.5 }], 'hsla(0, 0%, 0%, 0.5)'], [['rgba(0, 0, 0, 0)', { a: 1 }], '#000000'], [['rgba(0, 0, 0, 1)', { a: -0.5 }], 'rgba(0, 0, 0, 0.5)'], [['rgba(0, 0, 0, 1)', { a: -1 }], 'rgba(0, 0, 0, 0)'], [['rgba(0, 0, 0, 0.5)', { a: 1 }], '#000000'], [['rgba(0, 0, 0, 0.5)', { a: -1 }], 'rgba(0, 0, 0, 0)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( adjust ( ...args ), output ); }); }); it ( 'throws when setting RGB and HSL channels at the same time', t => { const tests = [ ['#000', { r: 10, h: 10 }], ['#000', { g: 10, l: 10 }], ['#000', { b: 10, s: 10 }] ]; tests.forEach ( args => { t.throws ( () => adjust ( ...args ), /cannot.*at the same time/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/alpha.js000066400000000000000000000007151422362466300226660ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {alpha} from '../../dist/index.js'; /* MAIN */ describe ( 'alpha', it => { it ( 'gets the alpha channel of the color', t => { const tests = [ ['rgba(10, 20, 30)', 1], ['rgba(10, 20, 30, 0.1)', 0.1], ['#10203040', 0.2509803922], ['hsla(10, 20%, 30%, 0.5)', 0.5] ]; tests.forEach ( ([ color, output ]) => { t.is ( alpha ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/blue.js000066400000000000000000000006221422362466300225250ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {blue} from '../../dist/index.js'; /* MAIN */ describe ( 'blue', it => { it ( 'gets the blue channel of the color', t => { const tests = [ ['rgb(10, 20, 30)', 30], ['#102030', 48], ['hsl(10, 20%, 30%)', 61.2] ]; tests.forEach ( ([ color, output ]) => { t.is ( blue ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/change.js000066400000000000000000000054201422362466300230240ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {change} from '../../dist/index.js'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'change', it => { it ( 'sets a new value for any RGBA channel of the color', t => { const tests = [ [['rgb(100, 100, 100)', { r: 0 }], 'rgb(0, 100, 100)'], [['rgb(100, 100, 100)', { r: 255 }], 'rgb(255, 100, 100)'], [['rgb(100, 100, 100)', { r: 100 }], 'rgb(100, 100, 100)'], [['rgb(100, 100, 100)', { g: 0 }], 'rgb(100, 0, 100)'], [['rgb(100, 100, 100)', { b: 0 }], 'rgb(100, 100, 0)'], [['rgb(100, 100, 100)', { r: 50, g: 150 }], 'rgb(50, 150, 100)'], [['rgb(100, 100, 100)', { r: 50, g: 150, b: 200 }], 'rgb(50, 150, 200)'], [['rgb(100, 100, 100)', { r: 50, g: 150, b: 200, a: 0.5 }], 'rgba(50, 150, 200, 0.5)'], [['rgb(100, 100, 100)', { r: 0, g: 0, b: 0, a: 0 }], 'rgba(0, 0, 0, 0)'], [['rgba(100, 100, 100, .5)', { g: 0 }], 'rgba(100, 0, 100, 0.5)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( Color.format.rgb.stringify ( Color.parse ( change ( ...args ) ) ), output ); }); }); it ( 'sets a new value for any HSLA channel of the color', t => { const tests = [ [['hsl(50, 50%, 50%)', { h: 100 }], 'hsl(100, 50%, 50%)'], [['hsl(50, 50%, 50%)', { h: 400 }], 'hsl(40, 50%, 50%)'], [['hsl(50, 50%, 50%)', { h: 0 }], 'hsl(0, 50%, 50%)'], [['hsl(50, 50%, 50%)', { s: 25 }], 'hsl(50, 25%, 50%)'], [['hsl(50, 50%, 50%)', { l: 25 }], 'hsl(50, 50%, 25%)'], [['hsl(50, 50%, 50%)', { h: 100, s: 75 }], 'hsl(100, 75%, 50%)'], [['hsl(50, 50%, 50%)', { h: 100, s: 75, l: 25 }], 'hsl(100, 75%, 25%)'], [['hsl(50, 50%, 50%)', { h: 100, s: 75, l: 25, a: 0.5 }], 'hsla(100, 75%, 25%, 0.5)'], [['hsl(50, 50%, 50%)', { h: 0, s: 0, l: 0, a: 0 }], 'hsla(0, 0%, 0%, 0)'], [['hsla(50, 50%, 50%, 0.5)', { h: 360, s: 100, l: 100, a: 1 }], 'hsl(0, 100%, 100%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( Color.format.hsl.stringify ( Color.parse ( change ( ...args ) ) ), output ); }); }); it ( 'sets a new value for the alpha channel of the color', t => { const tests = [ [['rgba(0, 0, 0, 0)', { a: 0.5 }], 'rgba(0, 0, 0, 0.5)'], [['hsla(0, 0%, 0%, 0.25)', { a: 0.5 }], 'hsla(0, 0%, 0%, 0.5)'], [['rgba(0, 0, 0, 1)', { a: 1 }], '#000000'] ]; tests.forEach ( ([ args, output ]) => { t.is ( change ( ...args ), output ); }); }); it ( 'throws when setting RGB and HSL channels at the same time', t => { const tests = [ ['#000', { r: 0, h: 0 }], ['#000', { g: 0, l: 0 }], ['#000', { b: 0, s: 0 }] ]; tests.forEach ( args => { t.throws ( () => change ( ...args ), /cannot.*at the same time/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/complement.js000066400000000000000000000007371422362466300237500ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {complement} from '../../dist/index.js'; /* MAIN */ describe ( 'complement', it => { it ( 'gets the complement of the color', t => { const tests = [ ['#6b717f', 'hsl(42, 8.547008547%, 45.8823529412%)'], ['#d2e1dd', 'hsl(344, 20%, 85.2941176471%)'], ['#036', 'hsl(30, 100%, 20%)'] ]; tests.forEach ( ([ color, output ]) => { t.is ( complement ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/contrast.js000066400000000000000000000010751422362466300234360ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {contrast} from '../../dist/index.js'; /* MAIN */ describe ( 'contrast', it => { it ( 'gets the contrast ratio between two colors', t => { const tests = [ ['#000000', '#000000', 1], ['#ffffff', '#ffffff', 1], ['#000000', '#ffffff', 10], ['#ffffff', '#000000', 10], ['#888888', '#ffffff', 4.0617165366], ['#ffffff', '#888888', 4.0617165366] ]; tests.forEach ( ([ color1, color2, output ]) => { t.is ( contrast ( color1, color2 ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/darken.js000066400000000000000000000011221422362466300230360ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {darken} from '../../dist/index.js'; /* MAIN */ describe ( 'darken', it => { it ( 'decreases the lightness channel of the color', t => { const tests = [ [['hsl(0, 0%, 100%)', 0], 'hsl(0, 0%, 100%)'], [['hsl(0, 0%, 100%)', 50], 'hsl(0, 0%, 50%)'], [['hsl(0, 0%, 100%)', 75], 'hsl(0, 0%, 25%)'], [['hsl(0, 0%, 100%)', 100], 'hsl(0, 0%, 0%)'], [['hsl(0, 0%, 50%)', 100], 'hsl(0, 0%, 0%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( darken ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/desaturate.js000066400000000000000000000012361422362466300237410ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {desaturate} from '../../dist/index.js'; /* MAIN */ describe ( 'desaturate', it => { it ( 'decreases the saturation channel of the color', t => { const tests = [ [['hsl(0, 100%, 50%)', 0], 'hsl(0, 100%, 50%)'], [['hsl(0, 100%, 50%)', 50], 'hsl(0, 50%, 50%)'], [['hsl(0, 100%, 50%)', 75], 'hsl(0, 25%, 50%)'], [['hsl(0, 100%, 50%)', 100], 'hsl(0, 0%, 50%)'], [['hsl(0, 50%, 50%)', 100], 'hsl(0, 0%, 50%)'], [['hsl(0, 0%, 50%)', 100], 'hsl(0, 0%, 50%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( desaturate ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/grayscale.js000066400000000000000000000007301422362466300235500ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {grayscale} from '../../dist/index.js'; /* MAIN */ describe ( 'grayscale', it => { it ( 'gets the grayscale version of the color', t => { const tests = [ ['#6b717f', 'hsl(222, 0%, 45.8823529412%)'], ['#d2e1dd', 'hsl(164, 0%, 85.2941176471%)'], ['#036', 'hsl(210, 0%, 20%)'] ]; tests.forEach ( ([ color, output ]) => { t.is ( grayscale ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/green.js000066400000000000000000000006261422362466300227020ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {green} from '../../dist/index.js'; /* MAIN */ describe ( 'green', it => { it ( 'gets the green channel of the color', t => { const tests = [ ['rgb(10, 20, 30)', 20], ['#102030', 32], ['hsl(10, 20%, 30%)', 66.3] ]; tests.forEach ( ([ color, output ]) => { t.is ( green ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/hex.js000066400000000000000000000014741422362466300223700ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {hex} from '../../dist/index.js'; /* MAIN */ describe ( 'hex', it => { it ( 'creates a new color given its rgba channels', t => { const tests = [ [[0, 0, 0, 0], 'rgba(0, 0, 0, 0)'], [[255, 255, 255, 0.5], 'rgba(255, 255, 255, 0.5)'], [[0, 0, 0, 1], '#000000'], [[128, 128, 128, 1], '#808080'], [[-1, -1, -1, -1], 'rgba(0, 0, 0, 0)'], [[1000, 1000, 1000, 1000], '#ffffff'] ]; tests.forEach ( ([ args, output ]) => { t.is ( hex ( ...args ), output ); }); }); it ( 'allows ommiting the alpha channel', t => { const tests = [ [[0, 0, 0], '#000000'], [[255, 255, 255], '#ffffff'] ]; tests.forEach ( ([ args, output ]) => { t.is ( hex ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/hsla.js000066400000000000000000000015511422362466300225270ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {hsla} from '../../dist/index.js'; /* MAIN */ describe ( 'hsla', it => { it ( 'creates a new color given its hsla channels.', t => { const tests = [ [[0, 0, 0, 0], 'hsla(0, 0%, 0%, 0)'], [[0, 0, 0, 0.5], 'hsla(0, 0%, 0%, 0.5)'], [[0, 0, 0, 1], 'hsl(0, 0%, 0%)'], [[180, 50, 50, 1], 'hsl(180, 50%, 50%)'], [[-1, -1, -1, -1], 'hsla(-1, 0%, 0%, 0)'], [[1000, 1000, 1000, 1000], 'hsl(280, 100%, 100%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( hsla ( ...args ), output ); }); }); it ( 'allows ommiting the alpha channel', t => { const tests = [ [[0, 0, 0], 'hsl(0, 0%, 0%)'], [[180, 50, 50], 'hsl(180, 50%, 50%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( hsla ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/hue.js000066400000000000000000000006161422362466300223620ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {hue} from '../../dist/index.js'; /* MAIN */ describe ( 'hue', it => { it ( 'gets the hue channel of the color', t => { const tests = [ ['hsl(10, 20%, 30%)', 10], ['rgb(10, 20, 30)', 210], ['#102030', 210] ]; tests.forEach ( ([ color, output ]) => { t.is ( hue ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/invert.js000066400000000000000000000006531422362466300231110ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {invert} from '../../dist/index.js'; /* MAIN */ describe ( 'invert', it => { it ( 'gets the inverse of the color', t => { const tests = [ [['#b37399'], '#4c8c66'], [['black'], '#ffffff'], [['#550e0c', 20 ], 'rgb(102, 59.4, 58.2)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( invert ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/is_dark.js000066400000000000000000000007421422362466300232150ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {isDark} from '../../dist/index.js'; /* MAIN */ describe ( 'isDark', it => { it ( 'checks if the provided color is a dark color', t => { const tests = [ ['#000000', true], ['#8a8a8a', true], ['#bbbbbb', true], ['#ffcc00', false], ['#e0e0e0', false], ['#ffffff', false] ]; tests.forEach ( ([ color, output ]) => { t.is ( isDark ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/is_light.js000066400000000000000000000007461422362466300234070ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {isLight} from '../../dist/index.js'; /* MAIN */ describe ( 'isLight', it => { it ( 'checks if the provided color is a light color', t => { const tests = [ ['#000000', false], ['#8a8a8a', false], ['#bbbbbb', false], ['#ffcc00', true], ['#e0e0e0', true], ['#ffffff', true] ]; tests.forEach ( ([ color, output ]) => { t.is ( isLight ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/is_valid.js000066400000000000000000000007561422362466300234000ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {isValid} from '../../dist/index.js'; /* MAIN */ describe ( 'isValid', it => { it ( 'checks if the provided color is a valid color', t => { const tests = [ ['black', true], ['rgb(10, 20, 30)', true], ['#102030', true], ['hsl(10, 20%, 30%)', true], ['#ggg', false], ['foo', false] ]; tests.forEach ( ([ color, output ]) => { t.is ( isValid ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/lighten.js000066400000000000000000000012061422362466300232270ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {lighten} from '../../dist/index.js'; /* MAIN */ describe ( 'lighten', it => { it ( 'increases the lightness channel of the color', t => { const tests = [ [['hsl(0, 0%, 0%)', 0], 'hsl(0, 0%, 0%)'], [['hsl(0, 0%, 0%)', 50], 'hsl(0, 0%, 50%)'], [['hsl(0, 0%, 0%)', 75], 'hsl(0, 0%, 75%)'], [['hsl(0, 0%, 0%)', 100], 'hsl(0, 0%, 100%)'], [['hsl(0, 0%, 50%)', 100], 'hsl(0, 0%, 100%)'], [['hsl(0, 0%, 100%)', 100], 'hsl(0, 0%, 100%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( lighten ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/lightness.js000066400000000000000000000007241422362466300236010ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {lightness} from '../../dist/index.js'; /* MAIN */ describe ( 'lightness', it => { it ( 'gets the lightness channel of the color', t => { const tests = [ ['hsl(10, 20%, 30%)', 30], ['rgb(10, 20, 30)', 7.8431372549], ['rgb(0, 0, 0)', 0], ['#102030', 12.5490196078] ]; tests.forEach ( ([ color, output ]) => { t.is ( lightness ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/luminance.js000066400000000000000000000007721422362466300235570ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {luminance} from '../../dist/index.js'; /* MAIN */ describe ( 'luminance', it => { it ( 'gets the relative luminance of the color', t => { const tests = [ ['#000000', 0], ['#8a8a8a', .2541520943], ['#bbbbbb', .4969329951], ['#ffcc00', .6444573127], ['#e0e0e0', .7454042095], ['#ffffff', 1] ]; tests.forEach ( ([ color, output ]) => { t.is ( luminance ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/mix.js000066400000000000000000000010661422362466300223760ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {mix} from '../../dist/index.js'; /* MAIN */ describe ( 'mix', it => { it ( 'mixes two colors together', t => { const tests = [ [['#036', '#d2e1dd'], 'rgb(105, 138, 161.5)'], [['#036', '#d2e1dd', 75 ], 'rgb(52.5, 94.5, 131.75)'], [['#036', '#d2e1dd', 25 ], 'rgb(157.5, 181.5, 191.25)'], [['rgba(242, 236, 228, 0.5)', '#6b717f'], 'rgba(140.75, 143.75, 152.25, 0.75)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( mix ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/opacify.js000066400000000000000000000007311422362466300232310ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {opacify} from '../../dist/index.js'; /* MAIN */ describe ( 'opacify', it => { it ( 'increases the opacity channel of the color', t => { const tests = [ [['#000000', 1], '#000000'], [['rgba(0, 0, 0, 0.5)', 0.5], '#000000'], [['rgba(0, 0, 0, 0.5)', 0.1], 'rgba(0, 0, 0, 0.6)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( opacify ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/red.js000066400000000000000000000006161422362466300223530ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {red} from '../../dist/index.js'; /* MAIN */ describe ( 'red', it => { it ( 'gets the red channel of the color', t => { const tests = [ ['rgb(10, 20, 30)', 10], ['#102030', 16], ['hsl(10, 20%, 30%)', 91.8] ]; tests.forEach ( ([ color, output ]) => { t.is ( red ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/rgba.js000066400000000000000000000024271422362466300225160ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {rgba} from '../../dist/index.js'; /* MAIN */ describe ( 'rgba', it => { it ( 'creates a new color given its rgba channels', t => { const tests = [ [[0, 0, 0, 0], 'rgba(0, 0, 0, 0)'], [[255, 255, 255, 0.5], 'rgba(255, 255, 255, 0.5)'], [[0, 0, 0, 1], '#000000'], [[128, 128, 128, 1], '#808080'], [[-1, -1, -1, -1], 'rgba(0, 0, 0, 0)'], [[1000, 1000, 1000, 1000], '#ffffff'] ]; tests.forEach ( ([ args, output ]) => { t.is ( rgba ( ...args ), output ); }); }); it ( 'sets the opacity', t => { const tests = [ [['#000000', .5], 'rgba(0, 0, 0, 0.5)'], [['#00000066', .5], 'rgba(0, 0, 0, 0.5)'], [['#ffffff', .5], 'rgba(255, 255, 255, 0.5)'], [['#ffffff66', .5], 'rgba(255, 255, 255, 0.5)'], [['#ffcc00', .5], 'rgba(255, 204, 0, 0.5)'], [['#ffcc0066', .5], 'rgba(255, 204, 0, 0.5)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( rgba ( ...args ), output ); }); }); it ( 'allows ommiting the alpha channel', t => { const tests = [ [[0, 0, 0], '#000000'], [[255, 255, 255], '#ffffff'] ]; tests.forEach ( ([ args, output ]) => { t.is ( rgba ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/saturate.js000066400000000000000000000012261422362466300234270ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {saturate} from '../../dist/index.js'; /* MAIN */ describe ( 'saturate', it => { it ( 'increases the saturation channel of the color', t => { const tests = [ [['hsl(0, 0%, 50%)', 0], 'hsl(0, 0%, 50%)'], [['hsl(0, 0%, 50%)', 50], 'hsl(0, 50%, 50%)'], [['hsl(0, 0%, 50%)', 75], 'hsl(0, 75%, 50%)'], [['hsl(0, 0%, 50%)', 100], 'hsl(0, 100%, 50%)'], [['hsl(0, 50%, 50%)', 100], 'hsl(0, 100%, 50%)'], [['hsl(0, 100%, 50%)', 100], 'hsl(0, 100%, 50%)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( saturate ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/saturation.js000066400000000000000000000007331422362466300237720ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {saturation} from '../../dist/index.js'; /* MAIN */ describe ( 'saturation', it => { it ( 'gets the saturation channel of the color', t => { const tests = [ ['hsl(10, 20%, 30%)', 20], ['rgb(10, 20, 30)', 50], ['rgb(0, 0, 0)', 0], ['#102030', 50], ['#ff0000', 100] ]; tests.forEach ( ([ color, output ]) => { t.is ( saturation ( color ), output ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/scale.js000066400000000000000000000064301422362466300226700ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {scale} from '../../dist/index.js'; import Color from '../../dist/color/index.js'; /* MAIN */ describe ( 'scale', it => { it ( 'scales any RGBA channels of the color', t => { const tests = [ [['rgb(0, 0, 0)', { r: 100 }], 'rgb(255, 0, 0)'], [['rgb(0, 0, 0)', { r: 50 }], 'rgb(127.5, 0, 0)'], [['rgb(0, 0, 0)', { r: 0 }], 'rgb(0, 0, 0)'], [['rgb(255, 0, 0)', { r: -100 }], 'rgb(0, 0, 0)'], [['rgb(255, 0, 0)', { r: -50 }], 'rgb(127.5, 0, 0)'], [['rgb(255, 0, 0)', { r: -0 }], 'rgb(255, 0, 0)'], [['rgb(0, 0, 0)', { g: 100 }], 'rgb(0, 255, 0)'], [['rgb(0, 0, 0)', { g: 50 }], 'rgb(0, 127.5, 0)'], [['rgb(0, 0, 0)', { g: 0 }], 'rgb(0, 0, 0)'], [['rgb(0, 0, 0)', { b: 100 }], 'rgb(0, 0, 255)'], [['rgb(0, 0, 0)', { b: 50 }], 'rgb(0, 0, 127.5)'], [['rgb(0, 0, 0)', { b: 0 }], 'rgb(0, 0, 0)'], [['rgb(0, 0, 0)', { r: 50, g: 50, b: 50 }], 'rgb(127.5, 127.5, 127.5)'], [['rgba(0, 0, 0, .5)', { b: 0 }], 'rgba(0, 0, 0, 0.5)'] ]; tests.forEach ( ([ args, ouptut ]) => { t.is ( Color.format.rgb.stringify ( Color.parse ( scale ( ...args ) ) ), ouptut ); }); }); it ( 'scales any HSLA channels of the color', t => { const tests = [ [['hsl(0, 50%, 50%)', { h: 100 }], 'hsl(0, 50%, 50%)'], // Wraps becuase 360deg = 0deg [['hsl(0, 50%, 50%)', { h: 50 }], 'hsl(180, 50%, 50%)'], [['hsl(0, 50%, 50%)', { h: 0 }], 'hsl(0, 50%, 50%)'], [['hsl(180, 50%, 50%)', { h: -100 }], 'hsl(0, 50%, 50%)'], [['hsl(180, 50%, 50%)', { h: -50 }], 'hsl(90, 50%, 50%)'], [['hsl(0, 50%, 50%)', { s: 100 }], 'hsl(0, 100%, 50%)'], [['hsl(0, 50%, 50%)', { s: 50 }], 'hsl(0, 75%, 50%)'], [['hsl(0, 50%, 50%)', { s: 0 }], 'hsl(0, 50%, 50%)'], [['hsl(0, 50%, 50%)', { s: -100 }], 'hsl(0, 0%, 50%)'], [['hsl(0, 50%, 50%)', { s: -50 }], 'hsl(0, 25%, 50%)'], [['hsl(0, 50%, 50%)', { l: 100 }], 'hsl(0, 50%, 100%)'], [['hsl(0, 50%, 50%)', { l: 50 }], 'hsl(0, 50%, 75%)'], [['hsl(0, 50%, 50%)', { l: 0 }], 'hsl(0, 50%, 50%)'], [['hsla(0, 50%, 50%, .5)', { l: 0 }], 'hsla(0, 50%, 50%, 0.5)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( Color.format.hsl.stringify ( Color.parse ( scale ( ...args ) ) ), output ); }); }); it ( 'scales the alpha channel', t => { const tests = [ [['rgba(0, 0, 0, 0)', { a: 0 }], 'rgba(0, 0, 0, 0)'], [['rgba(0, 0, 0, 0)', { a: 50 }], 'rgba(0, 0, 0, 0.5)'], [['rgba(0, 0, 0, 0)', { a: 100 }], '#000000'], [['hsla(0, 0%, 0%, 0)', { a: 50 }], 'hsla(0, 0%, 0%, 0.5)'], [['rgba(0, 0, 0, 1)', { a: -50 }], 'rgba(0, 0, 0, 0.5)'], [['rgba(0, 0, 0, 1)', { a: -100 }], 'rgba(0, 0, 0, 0)'], [['rgba(0, 0, 0, 0.5)', { a: 100 }], '#000000'], [['rgba(0, 0, 0, 0.5)', { a: -100 }], 'rgba(0, 0, 0, 0)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( scale ( ...args ), output ); }); }); it ( 'throws when setting RGB and HSL channels at the same time', t => { const tests = [ ['#000', { r: 10, h: 10 }], ['#000', { g: 10, l: 10 }], ['#000', { b: 10, s: 10 }] ]; tests.forEach ( args => { t.throws ( () => scale ( ...args ), /cannot.*at the same time/i ); }); }); }); fabiospampinato-khroma-f998e9b/test/methods/transparentize.js000066400000000000000000000010671422362466300246530ustar00rootroot00000000000000 /* IMPORT */ import {describe} from 'fava'; import {transparentize} from '../../dist/index.js'; /* MAIN */ describe ( 'transparentize', it => { it ( 'decreases the opacity channel of the color', t => { const tests = [ [['#000000', 1], 'rgba(0, 0, 0, 0)'], [['rgba(0, 0, 0, 0.5)', 0.5], 'rgba(0, 0, 0, 0)'], [['rgba(0, 0, 0, 0.5)', 1], 'rgba(0, 0, 0, 0)'], [['rgba(0, 0, 0, 0.5)', 0.1], 'rgba(0, 0, 0, 0.4)'] ]; tests.forEach ( ([ args, output ]) => { t.is ( transparentize ( ...args ), output ); }); }); }); fabiospampinato-khroma-f998e9b/tsconfig.json000077500000000000000000000001351422362466300213270ustar00rootroot00000000000000{ "extends": "tsex/tsconfig.json", "compilerOptions": { "noImplicitAny": false } }