├── .github ├── CODEOWNERS └── workflows │ ├── npmjs.yml │ └── node.js.yml ├── .eslintignore ├── .prettierignore ├── src ├── index.ts ├── converters │ ├── index.ts │ ├── hsv.spec.ts │ ├── hsb.spec.ts │ ├── hwb.spec.ts │ ├── hsl.spec.ts │ ├── hex.spec.ts │ ├── hsv.ts │ ├── rgb.spec.ts │ ├── hex.ts │ ├── hsb.ts │ ├── hwb.ts │ ├── rgb.ts │ └── hsl.ts ├── .eslintrc.json ├── tsconfig.json ├── tsconfig.app.json ├── tsconfig.spec.json ├── colorcolor.ts ├── utilities.spec.ts ├── utilities.ts ├── detectors.spec.ts ├── colors.ts └── colorcolor.spec.ts ├── documentation ├── coverage │ └── lcov-report │ │ ├── favicon.png │ │ ├── sort-arrow-sprite.png │ │ ├── prettify.css │ │ ├── block-navigation.js │ │ ├── src │ │ ├── converters │ │ │ ├── index.ts.html │ │ │ └── index.html │ │ └── index.html │ │ ├── index.html │ │ ├── base.css │ │ └── sorter.js ├── .nojekyll ├── assets │ └── highlight.css ├── modules │ ├── colorcolor.html │ └── detectors.html ├── variables │ └── detectors.ColorDetectors.html └── modules.html ├── jest.config.json ├── typedoc.json ├── .editorconfig ├── .prettierrc.json ├── rollup.config.js ├── dist ├── colorcolor.d.ts └── colors.d.ts ├── docs ├── SECURITY.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── tsconfig.json ├── LICENSE ├── CHANGELOG.md ├── .gitignore ├── package.json ├── .eslintrc.json └── README.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @metaloha 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { colorcolor } from './colorcolor'; 2 | 3 | export default colorcolor; 4 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russelporosky/color2color/HEAD/documentation/coverage/lcov-report/favicon.png -------------------------------------------------------------------------------- /documentation/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russelporosky/color2color/HEAD/documentation/coverage/lcov-report/sort-arrow-sprite.png -------------------------------------------------------------------------------- /src/converters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hex'; 2 | export * from './hsb'; 3 | export * from './hsl'; 4 | export * from './hsv'; 5 | export * from './hwb'; 6 | export * from './rgb'; 7 | -------------------------------------------------------------------------------- /src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.js"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverageDirectory": "./documentation/coverage", 3 | "moduleFileExtensions": [ 4 | "ts", 5 | "js" 6 | ], 7 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|js)$", 8 | "transform": { 9 | ".ts": "ts-jest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./src"], 3 | "entryPointStrategy": "expand", 4 | "exclude": ["**/*+(index|.spec|.e2e).ts"], 5 | "includeVersion": true, 6 | "out": "./documentation", 7 | "searchInComments": true, 8 | "tsconfig": "./src/tsconfig.app.json" 9 | } 10 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../dist", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": [ 9 | "**/*.ts" 10 | ], 11 | "exclude": [ 12 | "**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../dist", 5 | "module": "commonjs", 6 | "types": [ 7 | "jest", 8 | "node" 9 | ] 10 | }, 11 | "include": [ 12 | "**/*.spec.ts", 13 | "**/*.spec.js", 14 | "**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.json] 11 | indent_size = 2 12 | indent_style = space 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "endOfLine": "lf", 7 | "htmlWhitespaceSensitivity": "css", 8 | "printWidth": 80, 9 | "proseWrap": "preserve", 10 | "quoteProps": "as-needed", 11 | "semi": true, 12 | "singleAttributePerLine": false, 13 | "singleQuote": true, 14 | "trailingComma": "all", 15 | "useTabs": true 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/npmjs.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | # Setup .npmrc file to publish to npm 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: '16.x' 14 | registry-url: 'https://registry.npmjs.org' 15 | - run: npm ci 16 | - run: npm publish 17 | env: 18 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 19 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import json from '@rollup/plugin-json'; 2 | import nodeResolve from '@rollup/plugin-node-resolve'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | import sourcemaps from 'rollup-plugin-sourcemaps'; 5 | 6 | export default { 7 | input: './src/index.ts', 8 | output: [ 9 | { 10 | exports: 'default', 11 | extend: true, 12 | file: './dist/colorcolor.js', 13 | format: 'umd', 14 | name: 'colorcolor', 15 | noConflict: false, 16 | sourcemap: true, 17 | }, 18 | ], 19 | plugins: [ 20 | typescript({ 21 | tsconfig: './src/tsconfig.app.json', 22 | }), 23 | json(), 24 | nodeResolve({ 25 | browser: true, 26 | }), 27 | sourcemaps(), 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [14.x, 16.x, 18.x] 17 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | cache: 'npm' 26 | - run: npm ci 27 | - run: npm run build --if-present 28 | - run: npm run test 29 | -------------------------------------------------------------------------------- /dist/colorcolor.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This contains the sole method exposed by this library, {@link colorcolor}. 3 | * 4 | * @module 5 | */ 6 | import { ColorType } from './colors'; 7 | /** 8 | * Convert a color string in these valid color formats (name, RGB, RGBA, Hex, HexA, HSL, HSLA, HSB, or HSV) into a color 9 | * string of another format. 10 | * 11 | * @example 12 | * colorcolorObject('hsla(109, 100%, 37%, 1)', 'rgb'); // return 'rgb(35,189,0)' 13 | * colorcolorObject('hwb(180 50% 25% / 0.7)', 'hexa'); // return '#80bfbfb3' 14 | * 15 | * @param originalColor The CSS color value that needs to be converted. 16 | * @param targetColor The CSS color model to convert to. 17 | */ 18 | export declare const colorcolor: (originalColor: string, targetColor?: ColorType) => string; 19 | -------------------------------------------------------------------------------- /docs/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If there are any vulnerabilities in the **Color2Color** project, don't hesitate to _report them_. 6 | 7 | 1. Use any of the [private contact addresses](https://github.com/metaloha/color2color#support). 8 | 2. Describe the vulnerability. 9 | 10 | - If you have a fix, explain or attach it. 11 | - In the near time, expect a reply with the required steps. Also, there may be a demand for a pull request which include the fixes. 12 | 13 | > You should not disclose the vulnerability publicly if you haven't received an answer in some weeks. 14 | > If the vulnerability is rejected, you may post it publicly within some hour of rejection, unless the rejection is withdrawn within that time period. 15 | > After the vulnerability has been fixed, you may disclose the vulnerability details publicly over some days. 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "emitDecoratorMetadata": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "lib": [ 10 | "es5", 11 | "dom", 12 | ], 13 | "module": "es2015", 14 | "moduleResolution": "node", 15 | "noEmitOnError": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitAny": true, 18 | "noImplicitReturns": true, 19 | "noImplicitThis": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "outDir": "./dist", 23 | "paths": {}, 24 | "resolveJsonModule": true, 25 | "rootDir": ".", 26 | "sourceMap": true, 27 | "strict": true, 28 | "target": "es5" 29 | }, 30 | "exclude": [ 31 | "dist", 32 | "node_modules" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Josh Donnell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/converters/hsv.spec.ts: -------------------------------------------------------------------------------- 1 | import { hsvToHex, hsvToHsb, hsvToHsl, hsvToHwb, hsvToRgb } from './hsv'; 2 | 3 | describe('hsv', () => { 4 | describe('hsvToHex', () => { 5 | it('converts HSV to HEXA', () => { 6 | expect(hsvToHex(['270', 0.3333, 0.35])).toEqual({ 7 | r: 0.2917, 8 | g: 0.2334, 9 | b: 0.35, 10 | a: 1, 11 | }); 12 | }); 13 | }); 14 | 15 | describe('hsvToHsb', () => { 16 | it('converts HSV to HSB', () => { 17 | expect(hsvToHsb(['0.85turn', 0.3333, 0.35])).toEqual({ 18 | h: 306, 19 | s: 0.3333, 20 | b: 0.35, 21 | }); 22 | }); 23 | }); 24 | 25 | describe('hsvToHsl', () => { 26 | it('converts HSV to HSLA', () => { 27 | expect(hsvToHsl(['270', 0.3333, 0.35])).toEqual({ 28 | h: 270, 29 | s: 0.2, 30 | l: 0.2917, 31 | a: 1, 32 | }); 33 | }); 34 | }); 35 | 36 | describe('hsvToHwb', () => { 37 | it('converts HSV to HWB', () => { 38 | expect(hsvToHwb(['270deg', 0.2, 0.2917])).toEqual({ 39 | h: 270, 40 | w: 0.2334, 41 | b: 0.7083, 42 | a: 1, 43 | }); 44 | }); 45 | }); 46 | 47 | describe('hsvToRgb', () => { 48 | it('converts HSV to RGBA', () => { 49 | expect(hsvToRgb(['270', 0.3333, 0.35])).toEqual({ 50 | r: 0.2917, 51 | g: 0.2334, 52 | b: 0.35, 53 | a: 1, 54 | }); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v3.0.0 2 | * added HWB support 3 | * update converters to directly convert between formats instead of to RGBA first 4 | * now understands all valid formats for CSS color functions 5 | * _Example:_ rgba(75% 50% 66% / 50%) 6 | * code is broken into multiple files for better readability and maintainability 7 | * greatly expanded test coverage 8 | * greatly expanded documentation for developers 9 | * distribution folder includes both module and ES versions 10 | 11 | ## v2.0.3 12 | * rebuilt with TypeScript 13 | * fixed HSB/HSL/HSLA/HSV to allow whitespace 14 | * minimized and unminimized versions available in the `dist` folder 15 | 16 | ## v1.1.1 17 | * fixed some rounding issues with the HSL/HSV conversions 18 | * added HSB (identical to HSB) 19 | * improved testing (and made it a little worse at the same time due to the lack of 1-to-1 mapping from HSL/HSV/HSB to RGB) 20 | 21 | ## v1.1.0 22 | * refactored to more modern JS standards 23 | * added Grunt to automate minifying and hinting 24 | 25 | ## v1.0.1 26 | * updated all occurrences of `color2color` to `colorcolor` 27 | 28 | ## v1.0.0 29 | * NPM and Bower installable 30 | ** package and script name is now `colorcolor` to avoid conflict 31 | 32 | ## v0.2.1 33 | #### All the below fixes thanks to [*Kevin Jett*](https://github.com/kevjett/) 34 | * 3 character color names now not accidentally converted to hex 35 | * corrected HSL to RGB saturation miscalculation 36 | * fixed incorrect hex expansion when one or more of the original hex values were single-digit 37 | 38 | ## v0.2 39 | * added HSL, HSLA and HSV sources and targets 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | [._]*.s[a-w][a-z] 3 | [._]s[a-w][a-z] 4 | Session.vim 5 | .netrwhist 6 | *~ 7 | tags 8 | /.tgitconfig 9 | .~lock.*# 10 | nbproject/private/ 11 | build/ 12 | nbbuild/ 13 | nbdist/ 14 | nbactions.xml 15 | .nb-gradle/ 16 | *.DS_Store 17 | .AppleDouble 18 | .LSOverride 19 | Icon 20 | ._* 21 | .DocumentRevisions-V100 22 | .fseventsd 23 | .Spotlight-V100 24 | .TemporaryItems 25 | .Trashes 26 | .VolumeIcon.icns 27 | .com.apple.timemachine.donotpresent 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | *.tmlanguage.cache 34 | *.tmPreferences.cache 35 | *.stTheme.cache 36 | *.sublime-workspace 37 | sftp-config.json 38 | Package Control.last-run 39 | Package Control.ca-list 40 | Package Control.ca-bundle 41 | Package Control.system-ca-bundle 42 | Package Control.cache/ 43 | Package Control.ca-certs/ 44 | bh_unicode_properties.cache 45 | GitHub.sublime-settings 46 | .vagrant/ 47 | Thumbs.db 48 | ehthumbs.db 49 | Desktop.ini 50 | $RECYCLE.BIN/ 51 | *.cab 52 | *.msi 53 | *.msm 54 | *.msp 55 | *.lnk 56 | .idea 57 | *.iws 58 | /out/ 59 | .idea_modules/ 60 | atlassian-ide-plugin.xml 61 | com_crashlytics_export_strings.xml 62 | crashlytics.properties 63 | crashlytics-build.properties 64 | fabric.properties 65 | .vscode 66 | _notes 67 | _compareTemp 68 | configs/ 69 | dwsync.xml 70 | dw_php_codehinting.config 71 | *.mno 72 | .metadata 73 | bin/ 74 | tmp/ 75 | *.tmp 76 | *.bak 77 | *.swp 78 | *~.nib 79 | local.properties 80 | .settings/ 81 | .loadpath 82 | .recommenders 83 | .project 84 | .externalToolBuilders/ 85 | *.launch 86 | *.pydevproject 87 | .cproject 88 | .classpath 89 | .factorypath 90 | .buildpath 91 | .target 92 | .tern-project 93 | .texlipse 94 | .springBeans 95 | .recommenders/ 96 | 97 | npm-debug.log 98 | -------------------------------------------------------------------------------- /src/converters/hsb.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | hsbToHex, 3 | hsbToHsb, 4 | hsbToHsl, 5 | hsbToHsv, 6 | hsbToHwb, 7 | hsbToRgb, 8 | } from './hsb'; 9 | 10 | describe('hsb', () => { 11 | describe('hsbToHex', () => { 12 | it('converts HSB to HEXA', () => { 13 | expect(hsbToHex(['270', 0.3333, 0.35])).toEqual({ 14 | r: 0.2917, 15 | g: 0.2334, 16 | b: 0.35, 17 | a: 1, 18 | }); 19 | }); 20 | }); 21 | 22 | describe('hsbToHsb', () => { 23 | it('passes through values, but still converts degrees', () => { 24 | expect(hsbToHsb(['1.5turn', 0.55, 0.2345])).toEqual({ 25 | h: 540, 26 | s: 0.55, 27 | b: 0.2345, 28 | }); 29 | }); 30 | }); 31 | 32 | describe('hsbToHsl', () => { 33 | it('converts HSB to HSLA', () => { 34 | expect(hsbToHsl(['270', 0.3333, 0.35])).toEqual({ 35 | h: 270, 36 | s: 0.2, 37 | l: 0.2917, 38 | a: 1, 39 | }); 40 | expect(hsbToHsl(['270', 1, 1])).toEqual({ 41 | h: 270, 42 | s: 1, 43 | l: 0.5, 44 | a: 1, 45 | }); 46 | expect(hsbToHsl(['270', 0, 1])).toEqual({ 47 | h: 270, 48 | s: 0, 49 | l: 1, 50 | a: 1, 51 | }); 52 | }); 53 | }); 54 | 55 | describe('hsbToHsv', () => { 56 | it('converts HSB to HSV', () => { 57 | expect(hsbToHsv(['0.85turn', 0.3333, 0.35])).toEqual({ 58 | h: 306, 59 | s: 0.3333, 60 | v: 0.35, 61 | }); 62 | }); 63 | }); 64 | 65 | describe('hsbToHwb', () => { 66 | it('converts HSB to HWB', () => { 67 | expect(hsbToHwb(['270deg', 0.2, 0.2917])).toEqual({ 68 | h: 270, 69 | w: 0.2334, 70 | b: 0.7083, 71 | a: 1, 72 | }); 73 | }); 74 | }); 75 | 76 | describe('hsbToRgb', () => { 77 | it('converts HSB to RGBA', () => { 78 | expect(hsbToRgb(['270', 0.3333, 0.35])).toEqual({ 79 | r: 0.2917, 80 | g: 0.2334, 81 | b: 0.35, 82 | a: 1, 83 | }); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. 4 | Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 5 | 6 | ## Development environment setup 7 | 8 | To set up a development environment, please follow these steps: 9 | 10 | 1. Clone the repo 11 | 12 | ```shell 13 | git clone https://github.com/metaloha/ts-boilerplate 14 | ``` 15 | 16 | 2. Install dependencies 17 | 18 | ```shell 19 | npm ci 20 | ``` 21 | 22 | 3. Run tests 23 | 24 | ```shell 25 | npm run test:watch 26 | ``` 27 | 28 | ## Issues and feature requests 29 | 30 | You've found a bug in the source code, a mistake in the documentation, or maybe you'd like a new feature? You can help us by submitting an issue to our [GitHub Repository](https://github.com/metaloha/color2color/issues). Before you create an issue, make sure you search the archive, maybe your question was already answered. 31 | 32 | Please try to create bug reports that are: 33 | 34 | - _Reproducible._ Include steps to reproduce the problem. 35 | - _Specific._ Include as much detail as possible: which version, what environment, etc. 36 | - _Unique._ Do not duplicate existing opened issues. 37 | - _Scoped to a Single Bug._ One bug per report. 38 | 39 | Even better: You could submit a pull request with a fix or new feature! 40 | 41 | ## Pull request process 42 | 43 | 1. Search our repository for open or closed 44 | [pull requests](https://github.com/metaloha/color2color/pulls) 45 | that relates to your submission. You don't want to duplicate effort. 46 | 2. Fork the project 47 | 3. Create your feature branch (`git checkout -b amazing_feature`) 48 | 4. Commit your changes (`git commit -m 'add amazing_feature'`) 49 | 5. Push to the branch (`git push origin amazing_feature`) 50 | 6. Open a pull request 51 | -------------------------------------------------------------------------------- /documentation/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #001080; 3 | --dark-hl-0: #9CDCFE; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #795E26; 7 | --dark-hl-2: #DCDCAA; 8 | --light-hl-3: #A31515; 9 | --dark-hl-3: #CE9178; 10 | --light-hl-4: #008000; 11 | --dark-hl-4: #6A9955; 12 | --light-hl-5: #098658; 13 | --dark-hl-5: #B5CEA8; 14 | --light-code-background: #FFFFFF; 15 | --dark-code-background: #1E1E1E; 16 | } 17 | 18 | @media (prefers-color-scheme: light) { :root { 19 | --hl-0: var(--light-hl-0); 20 | --hl-1: var(--light-hl-1); 21 | --hl-2: var(--light-hl-2); 22 | --hl-3: var(--light-hl-3); 23 | --hl-4: var(--light-hl-4); 24 | --hl-5: var(--light-hl-5); 25 | --code-background: var(--light-code-background); 26 | } } 27 | 28 | @media (prefers-color-scheme: dark) { :root { 29 | --hl-0: var(--dark-hl-0); 30 | --hl-1: var(--dark-hl-1); 31 | --hl-2: var(--dark-hl-2); 32 | --hl-3: var(--dark-hl-3); 33 | --hl-4: var(--dark-hl-4); 34 | --hl-5: var(--dark-hl-5); 35 | --code-background: var(--dark-code-background); 36 | } } 37 | 38 | :root[data-theme='light'] { 39 | --hl-0: var(--light-hl-0); 40 | --hl-1: var(--light-hl-1); 41 | --hl-2: var(--light-hl-2); 42 | --hl-3: var(--light-hl-3); 43 | --hl-4: var(--light-hl-4); 44 | --hl-5: var(--light-hl-5); 45 | --code-background: var(--light-code-background); 46 | } 47 | 48 | :root[data-theme='dark'] { 49 | --hl-0: var(--dark-hl-0); 50 | --hl-1: var(--dark-hl-1); 51 | --hl-2: var(--dark-hl-2); 52 | --hl-3: var(--dark-hl-3); 53 | --hl-4: var(--dark-hl-4); 54 | --hl-5: var(--dark-hl-5); 55 | --code-background: var(--dark-code-background); 56 | } 57 | 58 | .hl-0 { color: var(--hl-0); } 59 | .hl-1 { color: var(--hl-1); } 60 | .hl-2 { color: var(--hl-2); } 61 | .hl-3 { color: var(--hl-3); } 62 | .hl-4 { color: var(--hl-4); } 63 | .hl-5 { color: var(--hl-5); } 64 | pre, code { background: var(--code-background); } 65 | -------------------------------------------------------------------------------- /src/converters/hwb.spec.ts: -------------------------------------------------------------------------------- 1 | import { hwbToHex, hwbToHsb, hwbToHsl, hwbToHsv, hwbToRgb } from './hwb'; 2 | 3 | describe('hwb', () => { 4 | describe('hwbToHex', () => { 5 | it('converts HWB without alpha to RGBA', () => { 6 | expect(hwbToHex(['180', 0.5, 0.25])).toEqual({ 7 | r: 0.5, 8 | g: 0.75, 9 | b: 0.75, 10 | a: 1, 11 | }); 12 | }); 13 | 14 | it('converts HWB to RGBA', () => { 15 | expect(hwbToHex(['180', 0.5, 0.25, 0.9])).toEqual({ 16 | r: 0.5, 17 | g: 0.75, 18 | b: 0.75, 19 | a: 0.9, 20 | }); 21 | }); 22 | }); 23 | 24 | describe('hwbToHsb', () => { 25 | it('converts HSL to HSB', () => { 26 | expect(hwbToHsb(['227', 0.3333, 0.35])).toEqual({ 27 | h: 227, 28 | s: 0.4872, 29 | b: 0.65, 30 | }); 31 | expect(hwbToHsb(['227', 0.3333, 1])).toEqual({ 32 | h: 227, 33 | s: 0, 34 | b: 0, 35 | }); 36 | }); 37 | }); 38 | 39 | describe('hwbToHsl', () => { 40 | it('converts HWB without alpha to HSLA', () => { 41 | expect(hwbToHsl(['180', 0.5, 0.25])).toEqual({ 42 | h: 180, 43 | s: 0.3333, 44 | l: 0.625, 45 | a: 1, 46 | }); 47 | }); 48 | 49 | it('converts HWB to HSLA', () => { 50 | expect(hwbToHsl(['0.5turn', 0.5, 0.25, 0.9])).toEqual({ 51 | h: 180, 52 | s: 0.3333, 53 | l: 0.625, 54 | a: 0.9, 55 | }); 56 | }); 57 | }); 58 | 59 | describe('hwbToHsv', () => { 60 | it('converts HSL to HSB', () => { 61 | expect(hwbToHsv(['227', 0.3333, 0.35])).toEqual({ 62 | h: 227, 63 | s: 0.4872, 64 | v: 0.65, 65 | }); 66 | }); 67 | }); 68 | 69 | describe('hwbToRgb', () => { 70 | it('converts HWB without alpha to RGBA', () => { 71 | expect(hwbToRgb(['180', 0.5, 0.25])).toEqual({ 72 | r: 0.5, 73 | g: 0.75, 74 | b: 0.75, 75 | a: 1, 76 | }); 77 | expect(hwbToRgb(['180', 0.5, 0.75])).toEqual({ 78 | r: 0.4, 79 | g: 0.4, 80 | b: 0.4, 81 | a: 1, 82 | }); 83 | }); 84 | 85 | it('converts HWB to RGBA', () => { 86 | expect(hwbToRgb(['180', 0.5, 0.25, 0.9])).toEqual({ 87 | r: 0.5, 88 | g: 0.75, 89 | b: 0.75, 90 | a: 0.9, 91 | }); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /src/converters/hsl.spec.ts: -------------------------------------------------------------------------------- 1 | import { hslToHex, hslToHsb, hslToHsv, hslToHwb, hslToRgb } from './hsl'; 2 | 3 | describe('hsl', () => { 4 | describe('hslToHex', () => { 5 | it('converts HSL without alpha to HEXA', () => { 6 | expect(hslToHex(['227', 0.3333, 0.35])).toEqual({ 7 | r: 0.2333, 8 | g: 0.2839, 9 | b: 0.4667, 10 | a: 1, 11 | }); 12 | }); 13 | 14 | it('converts HSLA to HEXA', () => { 15 | expect(hslToHex(['227', 0.3333, 0.35, 0.75])).toEqual({ 16 | r: 0.2333, 17 | g: 0.2839, 18 | b: 0.4667, 19 | a: 0.75, 20 | }); 21 | }); 22 | }); 23 | 24 | describe('hslToHsb', () => { 25 | it('converts HSL to HSB', () => { 26 | expect(hslToHsb(['227', 0.3333, 0.35])).toEqual({ 27 | h: 227, 28 | s: 0.5, 29 | b: 0.4667, 30 | }); 31 | }); 32 | }); 33 | 34 | describe('hslToHsv', () => { 35 | it('converts HSL to HSB', () => { 36 | expect(hslToHsv(['227', 0.3333, 0.35])).toEqual({ 37 | h: 227, 38 | s: 0.5, 39 | v: 0.4667, 40 | }); 41 | }); 42 | }); 43 | 44 | describe('hslToHwb', () => { 45 | it('converts HSL without alpha to HWB', () => { 46 | expect(hslToHwb(['227', 0.3333, 0.35])).toEqual({ 47 | h: 227, 48 | w: 0.2334, 49 | b: 0.5333, 50 | a: 1, 51 | }); 52 | }); 53 | 54 | it('converts HSLA to HWB', () => { 55 | expect(hslToHwb(['227', 0.3333, 0.35, 0.75])).toEqual({ 56 | h: 227, 57 | w: 0.2334, 58 | b: 0.5333, 59 | a: 0.75, 60 | }); 61 | }); 62 | }); 63 | 64 | describe('hslToRgb', () => { 65 | it('converts HSL without alpha to RGBA', () => { 66 | // expect(hslToRgb(['227', 0.3333, 0.35])).toEqual({ 67 | // r: 0.2333, 68 | // g: 0.2839, 69 | // b: 0.4667, 70 | // a: 1, 71 | // }); 72 | // expect(hslToRgb(['227', 1, 1])).toEqual({ 73 | // r: 1, 74 | // g: 1, 75 | // b: 1, 76 | // a: 1, 77 | // }); 78 | expect(hslToRgb(['227', 0, 0])).toEqual({ 79 | r: 0, 80 | g: 0, 81 | b: 0, 82 | a: 1, 83 | }); 84 | }); 85 | 86 | it('converts HSLA to RGBA', () => { 87 | expect(hslToRgb(['227', 0.3333, 0.35, 0.75])).toEqual({ 88 | r: 0.2333, 89 | g: 0.2839, 90 | b: 0.4667, 91 | a: 0.75, 92 | }); 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colorcolor", 3 | "version": "3.0.1", 4 | "description": "colorcolor converts Hex/HexA/RGB/RGBA/HSL/HSLA/HSV/HSB/HWB color strings to Hex/HexA/RGB/RGBA/HSL/HSLA/HSV/HSB/HWB color strings.", 5 | "main": "./dist/colorcolor.cjs", 6 | "types": "./dist/colorcolor.d.ts", 7 | "scripts": { 8 | "build": "npm run clean && npm run rollup && copyfiles -f ./dist/src/colorcolor.d.ts ./dist && copyfiles -f ./dist/src/colors.d.ts ./dist && rimraf ./dist/src", 9 | "clean": "rimraf ./dist", 10 | "documentation": "typedoc && npm run test:coverage", 11 | "lint": "eslint ./src", 12 | "minify": "uglifyjs dist/colorcolor.js --output dist/colorcolor.min.js", 13 | "postbuild": "npm run minify", 14 | "prettier": "prettier --write \"src/**/*.ts\"", 15 | "rollup": "rollup -c", 16 | "test": "tsc --noEmit -p . && jest", 17 | "test:coverage": "tsc --noEmit -p . && jest --collectCoverage", 18 | "test:watch": "tsc --noEmit -p . && jest --watch" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/metaloha/color2color.git" 23 | }, 24 | "keywords": [ 25 | "color", 26 | "convert" 27 | ], 28 | "author": "Russel Porosky ", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/metaloha/color2color/issues" 32 | }, 33 | "homepage": "http://metaloha.github.io/color2color/", 34 | "devDependencies": { 35 | "@rollup/plugin-commonjs": "^23.0.2", 36 | "@rollup/plugin-json": "^4.1.0", 37 | "@rollup/plugin-node-resolve": "^15.0.1", 38 | "@rollup/plugin-terser": "^0.1.0", 39 | "@rollup/plugin-typescript": "^9.0.2", 40 | "@types/jest": "^29.1.2", 41 | "@types/node": "^18.8.3", 42 | "@typescript-eslint/eslint-plugin": "^5.39.0", 43 | "@typescript-eslint/parser": "^5.39.0", 44 | "copyfiles": "^2.4.1", 45 | "eslint": "^8.24.0", 46 | "eslint-import-resolver-typescript": "3.5.1", 47 | "eslint-plugin-import": "^2.26.0", 48 | "eslint-plugin-jsdoc": "^39.3.6", 49 | "eslint-plugin-prefer-arrow": "^1.2.3", 50 | "jest": "^29.1.2", 51 | "prettier": "^2.7.1", 52 | "rimraf": "^3.0.2", 53 | "rollup": "^2.79.1", 54 | "rollup-plugin-sourcemaps": "^0.6.3", 55 | "ts-jest": "^29.0.3", 56 | "tslib": "^2.4.1", 57 | "typedoc": "^0.23.21", 58 | "typescript": "^4.8.4", 59 | "uglify-js": "^3.17.3" 60 | }, 61 | "type": "module", 62 | "dependencies": { 63 | "css-color-names": "^1.0.1" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/block-navigation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var jumpToCode = (function init() { 3 | // Classes of code we would like to highlight in the file view 4 | var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; 5 | 6 | // Elements to highlight in the file listing view 7 | var fileListingElements = ['td.pct.low']; 8 | 9 | // We don't want to select elements that are direct descendants of another match 10 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` 11 | 12 | // Selecter that finds elements on the page to which we can jump 13 | var selector = 14 | fileListingElements.join(', ') + 15 | ', ' + 16 | notSelector + 17 | missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` 18 | 19 | // The NodeList of matching elements 20 | var missingCoverageElements = document.querySelectorAll(selector); 21 | 22 | var currentIndex; 23 | 24 | function toggleClass(index) { 25 | missingCoverageElements 26 | .item(currentIndex) 27 | .classList.remove('highlighted'); 28 | missingCoverageElements.item(index).classList.add('highlighted'); 29 | } 30 | 31 | function makeCurrent(index) { 32 | toggleClass(index); 33 | currentIndex = index; 34 | missingCoverageElements.item(index).scrollIntoView({ 35 | behavior: 'smooth', 36 | block: 'center', 37 | inline: 'center' 38 | }); 39 | } 40 | 41 | function goToPrevious() { 42 | var nextIndex = 0; 43 | if (typeof currentIndex !== 'number' || currentIndex === 0) { 44 | nextIndex = missingCoverageElements.length - 1; 45 | } else if (missingCoverageElements.length > 1) { 46 | nextIndex = currentIndex - 1; 47 | } 48 | 49 | makeCurrent(nextIndex); 50 | } 51 | 52 | function goToNext() { 53 | var nextIndex = 0; 54 | 55 | if ( 56 | typeof currentIndex === 'number' && 57 | currentIndex < missingCoverageElements.length - 1 58 | ) { 59 | nextIndex = currentIndex + 1; 60 | } 61 | 62 | makeCurrent(nextIndex); 63 | } 64 | 65 | return function jump(event) { 66 | if ( 67 | document.getElementById('fileSearch') === document.activeElement && 68 | document.activeElement != null 69 | ) { 70 | // if we're currently focused on the search input, we don't want to navigate 71 | return; 72 | } 73 | 74 | switch (event.which) { 75 | case 78: // n 76 | case 74: // j 77 | goToNext(); 78 | break; 79 | case 66: // b 80 | case 75: // k 81 | case 80: // p 82 | goToPrevious(); 83 | break; 84 | } 85 | }; 86 | })(); 87 | window.addEventListener('keydown', jumpToCode); 88 | -------------------------------------------------------------------------------- /src/converters/hex.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | hexToHex, 3 | hexToHsb, 4 | hexToHsl, 5 | hexToHsv, 6 | hexToHwb, 7 | hexToRgb, 8 | } from './hex'; 9 | 10 | describe('hex', () => { 11 | describe('hexToHex', () => { 12 | it('converts hex to HEXA', () => { 13 | expect(hexToHex([0.6706, 0.8039, 0.9373])).toEqual({ 14 | r: 0.6706, 15 | g: 0.8039, 16 | b: 0.9373, 17 | a: 1, 18 | }); 19 | expect(hexToHex([0.6706, 0.8039, 0.9373, 0.0392])).toEqual({ 20 | r: 0.6706, 21 | g: 0.8039, 22 | b: 0.9373, 23 | a: 0.0392, 24 | }); 25 | }); 26 | }); 27 | 28 | describe('hexToHsb', () => { 29 | it('converts hex to HSB', () => { 30 | expect(hexToHsb([0.2392, 0.2902, 0.4706])).toEqual({ 31 | h: 226.78, 32 | s: 0.4917, 33 | b: 0.4706, 34 | }); 35 | expect(hexToHsb([0, 1, 0.2941])).toEqual({ 36 | h: 137.65, 37 | s: 1, 38 | b: 1, 39 | }); 40 | }); 41 | }); 42 | 43 | describe('hexToHsl', () => { 44 | it('converts hex without alpha to HSLA', () => { 45 | expect(hexToHsl([0.2392, 0.2902, 0.4706])).toEqual({ 46 | h: 226.78, 47 | s: 0.326, 48 | l: 0.3549, 49 | a: 1, 50 | }); 51 | expect(hexToHsl([0, 0.1961, 0.2941])).toEqual({ 52 | h: 199.99, 53 | s: 1, 54 | l: 0.147, 55 | a: 1, 56 | }); 57 | }); 58 | 59 | it('converts hex with alpha to HSLA', () => { 60 | expect(hexToHsl([0.2392, 0.2902, 0.4706, 0.251])).toEqual({ 61 | h: 226.78, 62 | s: 0.326, 63 | l: 0.3549, 64 | a: 0.251, 65 | }); 66 | expect(hexToHsl([0, 0.1961, 0.2941, 0.9961])).toEqual({ 67 | h: 199.99, 68 | s: 1, 69 | l: 0.147, 70 | a: 0.9961, 71 | }); 72 | }); 73 | }); 74 | 75 | describe('hexToHsv', () => { 76 | it('converts hex to HSV', () => { 77 | expect(hexToHsv([0.2392, 0.2902, 0.4706])).toEqual({ 78 | h: 226.78, 79 | s: 0.4917, 80 | v: 0.4706, 81 | }); 82 | expect(hexToHsv([0, 1, 0.2941])).toEqual({ 83 | h: 137.65, 84 | s: 1, 85 | v: 1, 86 | }); 87 | }); 88 | }); 89 | 90 | describe('hexToHwb', () => { 91 | it('converts hex without alpha to HWB', () => { 92 | expect(hexToHwb([0.2392, 0.2902, 0.4706])).toEqual({ 93 | h: 226.78, 94 | w: 0.2392, 95 | b: 0.5294, 96 | a: 1, 97 | }); 98 | expect(hexToHwb([0, 0.1961, 0.2941])).toEqual({ 99 | h: 199.99, 100 | w: 0, 101 | b: 0.7059, 102 | a: 1, 103 | }); 104 | }); 105 | 106 | it('converts hex with alpha to HWB', () => { 107 | expect(hexToHwb([0.2392, 0.2902, 0.4706, 0.251])).toEqual({ 108 | h: 226.78, 109 | w: 0.2392, 110 | b: 0.5294, 111 | a: 0.251, 112 | }); 113 | expect(hexToHwb([0, 0.1961, 0.2941, 0.9961])).toEqual({ 114 | h: 199.99, 115 | w: 0, 116 | b: 0.7059, 117 | a: 0.9961, 118 | }); 119 | }); 120 | }); 121 | 122 | describe('hexToRgb', () => { 123 | it('converts hex without alpha to RGB', () => { 124 | expect(hexToRgb([1, 0.5, 0.3729])).toEqual({ 125 | r: 1, 126 | g: 0.5, 127 | b: 0.3729, 128 | a: 1, 129 | }); 130 | }); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer using any of the [private contact addresses](https://github.com/metaloha/color2color#support). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at 44 | 45 | For answers to common questions about this code of conduct, see 46 | -------------------------------------------------------------------------------- /src/converters/hsv.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of methods that take normalized HSV values and outputs normalized values for each 3 | * supported color model. Hsv -> hsv and hsv -> hsb are passthroughs. 4 | * 5 | * @module 6 | */ 7 | 8 | import { HSB, HSLA, HSV, HWB, RGBA } from '../colors'; 9 | import { hsbToHex, hsbToHsl, hsbToHwb, hsbToRgb } from './hsb'; 10 | import { toDegrees } from '../utilities'; 11 | 12 | /** 13 | * Converts an HSV color to a HEXA color. 14 | * 15 | * @example 16 | * hsvToHex('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 17 | * 18 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 19 | */ 20 | export const hsvToHex = ([h, s, v]: [string, number, number]): RGBA => 21 | hsvToHexa([h, s, v]); 22 | 23 | /** 24 | * Converts an HSV color to a HEXA color. 25 | * 26 | * @example 27 | * hsvToHexa('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 28 | * 29 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 30 | */ 31 | export const hsvToHexa = ([h, s, v]: [string, number, number]): RGBA => 32 | hsbToHex([h, s, v]); 33 | 34 | /** 35 | * Converts an HSV color to an HSV color. 36 | * 37 | * @example 38 | * hsvToHsv('0.85turn', 0.3333, 0.35) // return { h: 306, s: 0.3333, v: 0.35 } 39 | * 40 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 41 | */ 42 | export const hsvToHsb = ([h, s, v]: [string, number, number]): HSB => ({ 43 | h: toDegrees(h), 44 | s, 45 | b: v, 46 | }); 47 | 48 | /** 49 | * Converts an HSV color to an HSL color. 50 | * 51 | * @example 52 | * hsvToHsl('270', 0.3333, 0.35) // return { h: 270, s: 0.2, b: 0.2917 } 53 | * 54 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 55 | */ 56 | export const hsvToHsl = ([h, s, v]: [string, number, number]): HSLA => 57 | hsvToHsla([h, s, v]); 58 | 59 | /** 60 | * Converts an HSV color to an HSL color. 61 | * 62 | * @example 63 | * hsvToHsla('270', 0.3333, 0.35) // return { h: 270, s: 0.2, b: 0.2917 } 64 | * 65 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 66 | */ 67 | export const hsvToHsla = ([h, s, v]: [string, number, number]): HSLA => 68 | hsbToHsl([h, s, v]); 69 | 70 | /** 71 | * Passthrough for HSV to HSV conversion. 72 | * 73 | * @example 74 | * hsvToHsv('1.5turn', 0.6, 0.343) // return { h: 540, s: 0.6, b: 0.343 } 75 | * 76 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 77 | */ 78 | export const hsvToHsv = ([h, s, v]: [string, number, number]): HSV => ({ 79 | h: toDegrees(h), 80 | s, 81 | v, 82 | }); 83 | 84 | /** 85 | * Converts an HSV color to an HWB color. 86 | * 87 | * @example 88 | * hsvToHwb('270deg', 0.2, 0.2917) // return { h: 270, w: 0.2334, b: 0.7083, a: 1 } 89 | * 90 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 91 | */ 92 | export const hsvToHwb = ([h, s, v]: [string, number, number]): HWB => 93 | hsbToHwb([h, s, v]); 94 | 95 | /** 96 | * Converts an HSV color to an RGBA color. 97 | * 98 | * @example 99 | * hsvToRgb('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 100 | * 101 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 102 | */ 103 | export const hsvToRgb = ([h, s, v]: [string, number, number]): RGBA => 104 | hsvToRgba([h, s, v]); 105 | 106 | /** 107 | * Converts an HSV color to an RGBA color. 108 | * 109 | * @example 110 | * hsvToRgba('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 111 | * 112 | * @param hsv - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 113 | */ 114 | export const hsvToRgba = ([h, s, v]: [string, number, number]): RGBA => 115 | hsbToRgb([h, s, v]); 116 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/src/converters/index.ts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/converters/index.ts 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files / src/converters index.ts

23 |
24 | 25 |
26 | 100% 27 | Statements 28 | 6/6 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 0/0 43 |
44 | 45 | 46 |
47 | 100% 48 | Lines 49 | 6/6 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 | 63 |
64 |
65 |

 66 | 
1 67 | 2 68 | 3 69 | 4 70 | 5 71 | 6 72 | 72x 73 | 2x 74 | 2x 75 | 2x 76 | 2x 77 | 2x 78 |  
export * from './hex';
 79 | export * from './hsb';
 80 | export * from './hsl';
 81 | export * from './hsv';
 82 | export * from './hwb';
 83 | export * from './rgb';
 84 |  
85 | 86 |
87 |
88 | 93 | 94 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/converters/rgb.spec.ts: -------------------------------------------------------------------------------- 1 | import { rgbToHex, rgbToHsb, rgbToHsl, rgbToHsv, rgbToHwb } from './rgb'; 2 | import { toPercent } from '../utilities'; 3 | 4 | describe('rgb', () => { 5 | describe('rgbToHex', () => { 6 | it('converts RGB without alpha to HEXA', () => { 7 | expect( 8 | rgbToHex([toPercent(61, 255), toPercent(74, 255), toPercent(120, 255)]), 9 | ).toEqual({ 10 | r: 0.2392, 11 | g: 0.2902, 12 | b: 0.4706, 13 | a: 1, 14 | }); 15 | }); 16 | 17 | it('converts RGB with alpha to HEXA', () => { 18 | expect( 19 | rgbToHex([ 20 | toPercent(61, 255), 21 | toPercent(74, 255), 22 | toPercent(120, 255), 23 | 0.6, 24 | ]), 25 | ).toEqual({ 26 | r: 0.2392, 27 | g: 0.2902, 28 | b: 0.4706, 29 | a: 0.6, 30 | }); 31 | }); 32 | }); 33 | 34 | describe('rgbToHsb', () => { 35 | it('converts RGB to HSB', () => { 36 | expect( 37 | rgbToHsb([toPercent(61, 255), toPercent(74, 255), toPercent(120, 255)]), 38 | ).toEqual({ 39 | h: 226.78, 40 | s: 0.4917, 41 | b: 0.4706, 42 | }); 43 | expect( 44 | rgbToHsb([toPercent(0, 255), toPercent(255, 255), toPercent(75, 255)]), 45 | ).toEqual({ 46 | h: 137.65, 47 | s: 1, 48 | b: 1, 49 | }); 50 | }); 51 | }); 52 | 53 | describe('rgbToHsl', () => { 54 | it('converts RGB without alpha to HSLA', () => { 55 | expect( 56 | rgbToHsl([toPercent(61, 255), toPercent(74, 255), toPercent(120, 255)]), 57 | ).toEqual({ 58 | h: 226.78, 59 | s: 0.326, 60 | l: 0.3549, 61 | a: 1, 62 | }); 63 | expect( 64 | rgbToHsl([toPercent(0, 255), toPercent(50, 255), toPercent(75, 255)]), 65 | ).toEqual({ 66 | h: 199.99, 67 | s: 1, 68 | l: 0.147, 69 | a: 1, 70 | }); 71 | }); 72 | 73 | it('converts RGB with alpha to HSLA', () => { 74 | expect( 75 | rgbToHsl([ 76 | toPercent(255, 255), 77 | toPercent(128, 255), 78 | toPercent(51, 255), 79 | 0.25, 80 | ]), 81 | ).toEqual({ 82 | h: 22.65, 83 | s: 1, 84 | l: 0.6, 85 | a: 0.25, 86 | }); 87 | expect( 88 | rgbToHsl([ 89 | toPercent(0, 255), 90 | toPercent(50, 255), 91 | toPercent(75, 255), 92 | 0.999, 93 | ]), 94 | ).toEqual({ 95 | h: 199.99, 96 | s: 1, 97 | l: 0.147, 98 | a: 0.999, 99 | }); 100 | expect( 101 | rgbToHsl([ 102 | toPercent(255, 255), 103 | toPercent(51, 255), 104 | toPercent(255, 255), 105 | 0.25, 106 | ]), 107 | ).toEqual({ 108 | h: 300, 109 | s: 1, 110 | l: 0.6, 111 | a: 0.25, 112 | }); 113 | }); 114 | }); 115 | 116 | describe('rgbToHsv', () => { 117 | it('converts RGB to HSV', () => { 118 | expect( 119 | rgbToHsv([toPercent(61, 255), toPercent(74, 255), toPercent(120, 255)]), 120 | ).toEqual({ 121 | h: 226.78, 122 | s: 0.4917, 123 | v: 0.4706, 124 | }); 125 | expect( 126 | rgbToHsv([toPercent(0, 255), toPercent(255, 255), toPercent(75, 255)]), 127 | ).toEqual({ 128 | h: 137.65, 129 | s: 1, 130 | v: 1, 131 | }); 132 | }); 133 | }); 134 | 135 | describe('rgbToHwb', () => { 136 | it('converts RGB without alpha to HWB', () => { 137 | expect( 138 | rgbToHwb([toPercent(61, 255), toPercent(74, 255), toPercent(120, 255)]), 139 | ).toEqual({ 140 | h: 226.78, 141 | w: 0.2392, 142 | b: 0.5294, 143 | a: 1, 144 | }); 145 | expect( 146 | rgbToHwb([toPercent(0, 255), toPercent(50, 255), toPercent(75, 255)]), 147 | ).toEqual({ 148 | h: 199.99, 149 | w: 0, 150 | b: 0.7059, 151 | a: 1, 152 | }); 153 | }); 154 | 155 | it('converts RGB with alpha to HWB', () => { 156 | expect( 157 | rgbToHwb([ 158 | toPercent(61, 255), 159 | toPercent(74, 255), 160 | toPercent(120, 255), 161 | 0.25, 162 | ]), 163 | ).toEqual({ 164 | h: 226.78, 165 | w: 0.2392, 166 | b: 0.5294, 167 | a: 0.25, 168 | }); 169 | expect( 170 | rgbToHwb([ 171 | toPercent(0, 255), 172 | toPercent(50, 255), 173 | toPercent(75, 255), 174 | 0.999, 175 | ]), 176 | ).toEqual({ 177 | h: 199.99, 178 | w: 0, 179 | b: 0.7059, 180 | a: 0.999, 181 | }); 182 | }); 183 | }); 184 | }); 185 | -------------------------------------------------------------------------------- /src/converters/hex.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of methods that take normalized RGBA values (0..1) and outputs normalized values for each 3 | * supported color model. Hex -> hex and hex -> rgb are passthroughs. 4 | * 5 | * @module 6 | */ 7 | 8 | import { rgbToHsb, rgbToHsl, rgbToHsv, rgbToHwb } from './rgb'; 9 | import { DefaultOpacity } from '../utilities'; 10 | import { HSB, HSLA, HSV, HWB, RGBA } from '../colors'; 11 | 12 | /** 13 | * Converts a hexadecimal RGB[A] color to a HEXA color. 14 | * 15 | * @example 16 | * hexToHex(0.6706, 0.8039, 0.9373) // return { r: 0.6706, g: 0.8039, b: 0.9373, a: 1 } 17 | * hexToHex(0.6706, 0.8039, 0.9373, 0.0392) // return { r: 0.6706, g: 0.8039, b: 0.9373, a: 0.0392 } 18 | * 19 | * @param rgba - Color components as fractions. 20 | */ 21 | export const hexToHex = ([r, g, b, a = DefaultOpacity]: [ 22 | number, 23 | number, 24 | number, 25 | number?, 26 | ]): RGBA => hexToHexa([r, g, b, a]); 27 | 28 | /** 29 | * Converts a hexadecimal RGB[A] color to a HEXA color. 30 | * 31 | * @example 32 | * hexToHexa(0.6706, 0.8039, 0.9373, 1) // return { r: 0.6706, g: 0.8039, b: 0.9373, a: 1 } 33 | * hexToHexa(0.6706, 0.8039, 0.9373, 0.0392) // return { r: 0.6706, g: 0.8039, b: 0.9373, a: 0.0392 } 34 | * 35 | * @param rgba - Color components as fractions. 36 | */ 37 | export const hexToHexa = ([r, g, b, a = DefaultOpacity]: [ 38 | number, 39 | number, 40 | number, 41 | number?, 42 | ]): RGBA => ({ 43 | r, 44 | g, 45 | b, 46 | a, 47 | }); 48 | 49 | /** 50 | * Converts a hexadecimal RGB color to an HSB color. 51 | * 52 | * @example 53 | * hexToHsb(0.2392, 0.2902, 0.4706) // return { h: 226.78, s: 0.4917, b: 0.4706 } 54 | * 55 | * @param rgb - Color components as fractions. 56 | */ 57 | export const hexToHsb = ([r, g, b]: [number, number, number]): HSB => 58 | rgbToHsb([r, g, b]); 59 | 60 | /** 61 | * Converts a hexadecimal RGB[A] color to an HSLA color. 62 | * 63 | * @example 64 | * hexToHsl(0.2392, 0.2902, 0.4706) // return { h: 226.78, s: 0.326, l: 0.3549, a: 1 } 65 | * hexToHsl(0.2392, 0.2902, 0.4706, 0.251) // return { h: 226.78, s: 0.326, l: 0.3549, a: 0.251 } 66 | * 67 | * @param rgba - Color components as fractions. 68 | */ 69 | export const hexToHsl = ([r, g, b, a = DefaultOpacity]: [ 70 | number, 71 | number, 72 | number, 73 | number?, 74 | ]): HSLA => hexToHsla([r, g, b, a]); 75 | 76 | /** 77 | * Converts a hexadecimal RGB[A] color to an HSLA color. 78 | * 79 | * @example 80 | * hexToHsla(0.2392, 0.2902, 0.4706, 1) // return { h: 226.78, s: 0.326, l: 0.3549, a: 1 } 81 | * hexToHsla(0.2392, 0.2902, 0.4706, 0.251) // return { h: 226.78, s: 0.326, l: 0.3549, a: 0.251 } 82 | * 83 | * @param rgba - Color components as fractions. 84 | */ 85 | export const hexToHsla = ([r, g, b, a = DefaultOpacity]: [ 86 | number, 87 | number, 88 | number, 89 | number?, 90 | ]): HSLA => rgbToHsl([r, g, b, a]); 91 | 92 | /** 93 | * Converts a hexadecimal RGB color to an HSV color. 94 | * 95 | * @example 96 | * hexToHsv(0.2392, 0.2902, 0.4706) // return { h: 226.78, s: 0.4917, v: 0.4706 } 97 | * 98 | * @param rgb - Color components as fractions. 99 | */ 100 | export const hexToHsv = ([r, g, b]: [number, number, number]): HSV => 101 | rgbToHsv([r, g, b]); 102 | 103 | /** 104 | * Converts a hexadecimal RGB[A] color to an HWB color. 105 | * 106 | * @example 107 | * hexToHwb(0.2392, 0.2902, 0.4706) // return { h: 226.78, w: 0.2392, b: 0.5294, a: 1 } 108 | * hexToHwb(0.2392, 0.2902, 0.4706, 0.251) // return { h: 226.78, w: 0.2392, b: 0.5294, a: 0.251 } 109 | * 110 | * @param rgba - Color components as fractions. 111 | */ 112 | export const hexToHwb = ([r, g, b, a = DefaultOpacity]: [ 113 | number, 114 | number, 115 | number, 116 | number?, 117 | ]): HWB => rgbToHwb([r, g, b, a]); 118 | 119 | /** 120 | * Converts a hexadecimal RGB[A] color to an RGBA color. 121 | * 122 | * @example 123 | * hexToRgb(0.6706, 0.8039, 0.9373, 0.0392) // return { r: 0.6706, g: 0.8039, b: 0.9373, a: 0.0392 } 124 | * 125 | * @param rgba - Color components as fractions. 126 | */ 127 | export const hexToRgb = ([r, g, b, a = DefaultOpacity]: [ 128 | number, 129 | number, 130 | number, 131 | number?, 132 | ]): RGBA => hexToRgba([r, g, b, a]); 133 | 134 | /** 135 | * Converts a hexadecimal RGB[A] color to an RGBA color. 136 | * 137 | * @example 138 | * hexToRgba(0.6706, 0.8039, 0.9373, 0.0392) // return { r: 0.6706, g: 0.8039, b: 0.9373, a: 0.0392 } 139 | * 140 | * @param rgba - Color components as fractions. 141 | */ 142 | export const hexToRgba = ([r, g, b, a = DefaultOpacity]: [ 143 | number, 144 | number, 145 | number, 146 | number?, 147 | ]): RGBA => ({ 148 | r, 149 | g, 150 | b, 151 | a, 152 | }); 153 | -------------------------------------------------------------------------------- /src/converters/hsb.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of methods that take normalized HSB values and outputs normalized values for each 3 | * supported color model. Hsb -> hsb and hsb -> hsv are passthroughs. 4 | * 5 | * @module 6 | */ 7 | 8 | import { HSB, HSLA, HSV, HWB, RGBA } from '../colors'; 9 | import { hslToHwb, hslToRgb } from './hsl'; 10 | import { 11 | DefaultOpacity, 12 | MaxFixedFloat, 13 | MaxPercentFloat, 14 | toDegrees, 15 | } from '../utilities'; 16 | 17 | /** 18 | * Converts an HSB color to a HEXA color. 19 | * 20 | * @example 21 | * hsbToHex('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 22 | * 23 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 24 | */ 25 | export const hsbToHex = ([h, s, b]: [string, number, number]): RGBA => 26 | hsbToHexa([h, s, b]); 27 | 28 | /** 29 | * Converts an HSB color to a HEXA color. 30 | * 31 | * @example 32 | * hsbToHexa('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 33 | * 34 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 35 | */ 36 | export const hsbToHexa = ([h, s, b]: [string, number, number]): RGBA => 37 | hsbToRgb([h, s, b]); 38 | 39 | /** 40 | * Passthrough for HSB to HSB conversion. 41 | * 42 | * @example 43 | * hsbToHsb('1.5turn', 0.6, 0.343) // return { h: 540, s: 0.6, b: 0.343 } 44 | * 45 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 46 | */ 47 | export const hsbToHsb = ([h, s, b]: [string, number, number]): HSB => { 48 | const Angle = toDegrees(h); 49 | 50 | return { 51 | h: Angle, 52 | s, 53 | b, 54 | }; 55 | }; 56 | 57 | /** 58 | * Converts an HSB color to an HSLA color. 59 | * 60 | * @example 61 | * hsbToHsl('270', 0.3333, 0.35) // return { h: 270, s: 0.2, b: 0.2917, a: 1 } 62 | * 63 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 64 | */ 65 | export const hsbToHsl = ([h, s, b]: [string, number, number]): HSLA => 66 | hsbToHsla([h, s, b]); 67 | 68 | /** 69 | * Converts an HSB color to an HSLA color. 70 | * 71 | * @example 72 | * hsbToHsla('270', 0.3333, 0.35) // return { h: 270, s: 0.2, b: 0.2917, a: 1 } 73 | * 74 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 75 | */ 76 | export const hsbToHsla = ([h, s, b]: [string, number, number]): HSLA => { 77 | const Angle = toDegrees(h); 78 | const Lightness = +(((2 - s) * b) / 2).toFixed(MaxFixedFloat); 79 | const Hsla: HSLA = { 80 | h: Angle, 81 | s, 82 | l: Lightness, 83 | a: DefaultOpacity, 84 | }; 85 | 86 | if (Lightness !== 0) { 87 | if (Lightness === MaxPercentFloat) { 88 | Hsla.s = 0; 89 | } else if (Lightness < MaxPercentFloat / 2) { 90 | Hsla.s = (Hsla.s * b) / (Lightness * 2); 91 | } else { 92 | Hsla.s = (Hsla.s * b) / (2 - Lightness * 2); 93 | } 94 | } 95 | Hsla.s = +Hsla.s.toFixed(MaxFixedFloat); 96 | 97 | return Hsla; 98 | }; 99 | 100 | /** 101 | * Converts an HSB color to an HSV color. 102 | * 103 | * @example 104 | * hsbToHsv('0.85turn', 0.3333, 0.35) // return { h: 306, s: 0.3333, v: 0.35 } 105 | * 106 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 107 | */ 108 | export const hsbToHsv = ([h, s, b]: [string, number, number]): HSV => ({ 109 | h: toDegrees(h), 110 | s, 111 | v: b, 112 | }); 113 | 114 | /** 115 | * Converts an HSB color to an HWB color. 116 | * 117 | * @example 118 | * hsbToHwb('270deg', 0.2, 0.2917) // return { h: 270, w: 0.2334, b: 0.7083, a: 1 } 119 | * 120 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 121 | */ 122 | export const hsbToHwb = ([h, s, b]: [string, number, number]): HWB => { 123 | const Hsla = hsbToHsl([h, s, b]); 124 | 125 | return hslToHwb([`${Hsla.h}`, Hsla.s, Hsla.l, Hsla.a]); 126 | }; 127 | 128 | /** 129 | * Converts an HSB color to an RGBA color. 130 | * 131 | * @example 132 | * hsbToRgb('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 133 | * 134 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 135 | */ 136 | export const hsbToRgb = ([h, s, b]: [string, number, number]): RGBA => 137 | hsbToRgba([h, s, b]); 138 | 139 | /** 140 | * Converts an HSB color to an RGBA color. 141 | * 142 | * @example 143 | * hsbToRgba('227deg', 0.3333, 0.35) // return { r: 0.2917, g: 0.2334, b: 0.35, a: 1 } 144 | * 145 | * @param hsb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 146 | */ 147 | export const hsbToRgba = ([h, s, b]: [string, number, number]): RGBA => { 148 | const Hsla = hsbToHsl([h, s, b]); 149 | 150 | return hslToRgb([`${Hsla.h}`, Hsla.s, Hsla.l, Hsla.a]); 151 | }; 152 | -------------------------------------------------------------------------------- /src/colorcolor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This contains the sole method exposed by this library, {@link colorcolor}. 3 | * 4 | * @module 5 | */ 6 | 7 | import colorNames from 'css-color-names'; 8 | import { 9 | ColorDefinitionType, 10 | ColorModel, 11 | ColorName, 12 | ColorType, 13 | HEXA, 14 | HSB, 15 | HSLA, 16 | HSV, 17 | HWB, 18 | RGBA, 19 | } from './colors'; 20 | import { ColorDetectors } from './detectors'; 21 | 22 | /** 23 | * Convert a color string in these valid color formats (name, RGB, RGBA, Hex, HexA, HSL, HSLA, HSB, or HSV) into a color 24 | * string of another format. 25 | * 26 | * @example 27 | * colorcolorObject('hsla(109, 100%, 37%, 1)', 'rgb'); // return 'rgb(35,189,0)' 28 | * colorcolorObject('hwb(180 50% 25% / 0.7)', 'hexa'); // return '#80bfbfb3' 29 | * 30 | * @param originalColor The CSS color value that needs to be converted. 31 | * @param targetColor The CSS color model to convert to. 32 | */ 33 | export const colorcolor = ( 34 | originalColor: string, 35 | targetColor: ColorType = ColorName.RGBA, 36 | ): string => { 37 | const TargetColor = (targetColor.toLowerCase() || 'rgba') as ColorType; 38 | const Color = colorcolorObject(originalColor, TargetColor); 39 | 40 | let colorObject: ColorModel; 41 | let returnedColor = ''; 42 | 43 | switch (TargetColor) { 44 | case ColorName.HEX: 45 | colorObject = Color as HEXA; 46 | returnedColor = `#${colorObject.r}${colorObject.g}${colorObject.b}`; 47 | break; 48 | case ColorName.HEXA: 49 | colorObject = Color as HEXA; 50 | returnedColor = `#${colorObject.r}${colorObject.g}${colorObject.b}${colorObject.a}`; 51 | break; 52 | case ColorName.HSB: 53 | colorObject = Color as HSB; 54 | returnedColor = `hsb(${colorObject.h}, ${colorObject.s}%, ${colorObject.b}%)`; 55 | break; 56 | case ColorName.HSL: 57 | colorObject = Color as HSLA; 58 | returnedColor = `hsl(${colorObject.h}, ${colorObject.s}%, ${colorObject.l}%)`; 59 | break; 60 | case ColorName.HSLA: 61 | colorObject = Color as HSLA; 62 | returnedColor = `hsla(${colorObject.h}, ${colorObject.s}%, ${colorObject.l}%, ${colorObject.a})`; 63 | break; 64 | case ColorName.HSV: 65 | colorObject = Color as HSV; 66 | returnedColor = `hsv(${colorObject.h}, ${colorObject.s}%, ${colorObject.v}%)`; 67 | break; 68 | case ColorName.HWB: 69 | colorObject = Color as HWB; 70 | returnedColor = `hwb(${colorObject.h} ${colorObject.w}% ${colorObject.b}% / ${colorObject.a})`; 71 | break; 72 | case ColorName.RGB: 73 | colorObject = Color as RGBA; 74 | returnedColor = `rgb(${colorObject.r}, ${colorObject.g}, ${colorObject.b})`; 75 | break; 76 | case ColorName.RGBA: 77 | colorObject = Color as RGBA; 78 | returnedColor = `rgba(${colorObject.r}, ${colorObject.g}, ${colorObject.b}, ${colorObject.a})`; 79 | break; 80 | } 81 | 82 | return returnedColor; 83 | }; 84 | 85 | /** 86 | * Convert a color string in these valid color formats (name, RGB, RGBA, Hex, HexA, HSL, HSLA, HSB, or HSV) into a color 87 | * object of another format. 88 | * 89 | * @example 90 | * colorcolorObject('hsla(109, 100%, 37%, 1)'); // return { r: 35, g: 189, b: 0, a: 1 } 91 | * colorcolorObject('hwb(180 50% 25% / 0.7)', 'hexa'); // return { r: '80', g: 'bf', b: 'bf', a: 'b3' } 92 | * 93 | * @param originalColor The CSS color value that needs to be converted. 94 | * @param targetColor The CSS color model to convert to. 95 | */ 96 | const colorcolorObject = ( 97 | originalColor: string, 98 | targetColor: ColorType = ColorName.RGBA, 99 | ): ColorModel => { 100 | const ConvertedTargetColor = targetColor.toLowerCase(); 101 | 102 | let convertedOriginalColor = originalColor.toLowerCase(); 103 | 104 | const NamedColor = (colorNames as { [key: string]: string })[ 105 | convertedOriginalColor 106 | ]; 107 | if (NamedColor) { 108 | convertedOriginalColor = NamedColor; 109 | } 110 | 111 | let finalColor: ColorModel = { r: '00', g: '00', b: '00', a: 'ff' }; // default to black 112 | 113 | for (const ColorDetector in ColorDetectors) { 114 | if (Object.prototype.hasOwnProperty.call(ColorDetectors, ColorDetector)) { 115 | const Detector = ColorDetectors[ColorDetector as ColorDefinitionType]; 116 | const Finalizer = ColorDetectors[ConvertedTargetColor as ColorType]; 117 | const Bits = Detector.re.exec(convertedOriginalColor); 118 | 119 | if (Bits) { 120 | // we were able to extract valid color values from the string 121 | const NormalizedBits = Detector.normalize(Bits); 122 | 123 | if ( 124 | Detector.converters && 125 | Object.prototype.hasOwnProperty.call( 126 | Detector.converters, 127 | ConvertedTargetColor, 128 | ) 129 | ) { 130 | const ConvertedBits = Detector.converters[ConvertedTargetColor as ColorType]( 131 | // @ts-ignore 132 | NormalizedBits, 133 | ); 134 | 135 | finalColor = Finalizer.finalize(ConvertedBits); 136 | } 137 | } 138 | } 139 | } 140 | 141 | return finalColor; 142 | }; 143 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "**/*" 5 | ], 6 | "plugins": [], 7 | "settings": { 8 | "import/resolver": { 9 | "typescript": {} 10 | } 11 | }, 12 | "overrides": [ 13 | { 14 | "files": [ 15 | "*.ts", 16 | "*.js" 17 | ], 18 | "parserOptions": { 19 | "project": [ 20 | "tsconfig.json" 21 | ], 22 | "createDefaultProgram": true 23 | }, 24 | "extends": [ 25 | "eslint:recommended", 26 | "plugin:@typescript-eslint/recommended", 27 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 28 | "plugin:import/errors", 29 | "plugin:import/warnings", 30 | "plugin:import/typescript" 31 | ], 32 | "rules": { 33 | "@typescript-eslint/array-type": "error", 34 | "@typescript-eslint/consistent-type-assertions": "error", 35 | "@typescript-eslint/default-param-last": "error", 36 | "@typescript-eslint/lines-between-class-members": [ 37 | "error", 38 | "always", 39 | { 40 | "exceptAfterSingleLine": true 41 | } 42 | ], 43 | "@typescript-eslint/no-array-constructor": [ 44 | "error" 45 | ], 46 | "@typescript-eslint/no-empty-function": "error", 47 | "@typescript-eslint/no-magic-numbers": [ 48 | "warn", 49 | { 50 | "ignoreArrayIndexes": true, 51 | "ignoreEnums": true 52 | } 53 | ], 54 | "@typescript-eslint/no-shadow": "error", 55 | "@typescript-eslint/no-useless-constructor": "error", 56 | "@typescript-eslint/unbound-method": [ 57 | "error", 58 | { 59 | "ignoreStatic": true 60 | } 61 | ], 62 | "import/no-extraneous-dependencies": "error", 63 | "accessor-pairs": "error", 64 | "array-callback-return": "error", 65 | "arrow-body-style": [ 66 | "error", 67 | "as-needed" 68 | ], 69 | "block-scoped-var": "error", 70 | "camelcase": "warn", 71 | "comma-dangle": "off", 72 | "complexity": [ 73 | "warn", 74 | 12 75 | ], 76 | "consistent-return": "error", 77 | "default-case-last": "error", 78 | "default-param-last": "off", 79 | "grouped-accessor-pairs": "warn", 80 | "guard-for-in": "error", 81 | "lines-between-class-members": "off", 82 | "max-len": "off", 83 | "no-alert": "warn", 84 | "no-array-constructor": "off", 85 | "no-console": [ 86 | "warn", 87 | { 88 | "allow": [ 89 | "warn", 90 | "error" 91 | ] 92 | } 93 | ], 94 | "no-duplicate-imports": "error", 95 | "no-else-return": "error", 96 | "no-empty-function": "off", 97 | "no-lone-blocks": "error", 98 | "no-lonely-if": "error", 99 | "no-loop-func": "error", 100 | "no-magic-numbers": "off", 101 | "no-negated-condition": "error", 102 | "no-param-reassign": "error", 103 | "no-promise-executor-return": "error", 104 | "no-restricted-syntax": [ 105 | "error", 106 | "SequenceExpression" 107 | ], 108 | "no-sequences": "off", 109 | "no-tabs": [ 110 | "error", 111 | { 112 | "allowIndentationTabs": true 113 | } 114 | ], 115 | "no-template-curly-in-string": "error", 116 | "no-throw-literal": "error", 117 | "no-unmodified-loop-condition": "warn", 118 | "no-unneeded-ternary": "warn", 119 | "no-unreachable-loop": "error", 120 | "no-unsafe-optional-chaining": "error", 121 | "no-unused-expressions": "error", 122 | "no-useless-computed-key": "error", 123 | "no-useless-constructor": "off", 124 | "no-useless-rename": "error", 125 | "no-useless-return": "error", 126 | "no-var": "error", 127 | "object-shorthand": "warn", 128 | "one-var": [ 129 | "error", 130 | "never" 131 | ], 132 | "padding-line-between-statements": [ 133 | "error", 134 | { 135 | "blankLine": "always", 136 | "next": "return", 137 | "prev": "*" 138 | } 139 | ], 140 | "prefer-arrow-callback": "error", 141 | "prefer-const": "warn", 142 | "prefer-destructuring": "warn", 143 | "prefer-object-spread": "error", 144 | "prefer-promise-reject-errors": "error", 145 | "prefer-rest-params": "error", 146 | "prefer-spread": "error", 147 | "prefer-template": "error", 148 | "quotes": "off", 149 | "radix": "error", 150 | "require-atomic-updates": "error" 151 | } 152 | } 153 | ] 154 | } 155 | -------------------------------------------------------------------------------- /src/utilities.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | capitalizeWord, 3 | clamp, 4 | clampAngle, 5 | clampPercent, 6 | clampPercentFloat, 7 | fromPercent, 8 | hexToNumber, 9 | hueToRgb, 10 | numberToHex, 11 | toDegrees, 12 | toPercent, 13 | } from './utilities'; 14 | 15 | describe('utilities', () => { 16 | describe('capitalizeWord', () => { 17 | it('capitalizes the first letter of the string', () => { 18 | expect(capitalizeWord('test')).toEqual('Test'); 19 | expect(capitalizeWord('test this')).toEqual('Test this'); 20 | }); 21 | 22 | it("has no effect if the first character can't be capitalized", () => { 23 | expect(capitalizeWord(' test')).toEqual(' test'); 24 | expect(capitalizeWord('3 amigos')).toEqual('3 amigos'); 25 | }); 26 | }); 27 | 28 | describe('clamp', () => { 29 | it('does not clamp when the number is within range', () => { 30 | expect(clamp(0.5, 0, 1)).toEqual(0.5); 31 | expect(clamp(192, 0, 255)).toEqual(192); 32 | }); 33 | 34 | it('does clamp when the number is above the maximum', () => { 35 | expect(clamp(1.005, 0, 1)).toEqual(1); 36 | expect(clamp(-43, -100, -50)).toEqual(-50); 37 | }); 38 | 39 | it('does clamp when the number is below the minimum', () => { 40 | expect(clamp(5, 10, 100)).toEqual(10); 41 | expect(clamp(-53, -50, 50)).toEqual(-50); 42 | }); 43 | }); 44 | 45 | describe('clampAngle', () => { 46 | it('does not clamp when the number is within range', () => { 47 | expect(clampAngle(180)).toEqual(180); 48 | }); 49 | 50 | it('does clamp when the number is above the maximum', () => { 51 | expect(clampAngle(500)).toEqual(360); 52 | }); 53 | 54 | it('does clamp when the number is below the minimum', () => { 55 | expect(clampAngle(-10)).toEqual(0); 56 | }); 57 | }); 58 | 59 | describe('clampPercent', () => { 60 | it('does not clamp when the number is within range', () => { 61 | expect(clampPercent(90)).toEqual(90); 62 | }); 63 | 64 | it('does clamp when the number is above the maximum', () => { 65 | expect(clampPercent(500)).toEqual(100); 66 | }); 67 | 68 | it('does clamp when the number is below the minimum', () => { 69 | expect(clampPercent(-10)).toEqual(0); 70 | }); 71 | }); 72 | 73 | describe('clampPercentFloat', () => { 74 | it('does not clamp when the number is within range', () => { 75 | expect(clampPercentFloat(0.9)).toEqual(0.9); 76 | }); 77 | 78 | it('does clamp when the number is above the maximum', () => { 79 | expect(clampPercentFloat(5)).toEqual(1); 80 | }); 81 | 82 | it('does clamp when the number is below the minimum', () => { 83 | expect(clampPercentFloat(-0.1)).toEqual(0); 84 | }); 85 | }); 86 | 87 | describe('hexToNumber', () => { 88 | it('converts from hexadecimal to decimal', () => { 89 | expect(hexToNumber('f')).toEqual(15); 90 | expect(hexToNumber('cd')).toEqual(205); 91 | }); 92 | }); 93 | 94 | describe('hueToRgb', () => { 95 | it('converts from hexadecimal to decimal', () => { 96 | expect(hueToRgb(0, 180, 0.7, 0.8)).toEqual(0.66); 97 | expect(hueToRgb(4, 263, 0.6712, 0.4358)).toEqual(0.7283); 98 | }); 99 | }); 100 | 101 | describe('fromPercent', () => { 102 | it('converts from a float to a number using the default maximum', () => { 103 | expect(fromPercent(0.5)).toEqual(50); 104 | expect(fromPercent(0.6667)).toEqual(66.67); 105 | }); 106 | 107 | it('converts from a float to a number with a given maximum', () => { 108 | expect(fromPercent(0.25, 500)).toEqual(125); 109 | expect(fromPercent(0, 73)).toEqual(0); 110 | expect(fromPercent(1, 360)).toEqual(360); 111 | expect(fromPercent(1.5, 100)).toEqual(150); 112 | }); 113 | }); 114 | 115 | describe('numberToHex', () => { 116 | it('converts from RGB value to hex', () => { 117 | expect(numberToHex(0)).toEqual('00'); 118 | expect(numberToHex(255)).toEqual('ff'); 119 | expect(numberToHex(221)).toEqual('dd'); 120 | expect(numberToHex(74)).toEqual('4a'); 121 | }); 122 | }); 123 | 124 | describe('toDegrees', () => { 125 | it('uses `deg` as default unit', () => { 126 | expect(toDegrees('180')).toEqual(180); 127 | expect(toDegrees('180deg')).toEqual(180); 128 | expect(toDegrees('47')).toEqual(47); 129 | expect(toDegrees('47deg')).toEqual(47); 130 | }); 131 | 132 | it('converts gradians to degrees', () => { 133 | expect(toDegrees('100grad')).toEqual(90); 134 | expect(toDegrees('150grad')).toEqual(135); 135 | expect(toDegrees('600grad')).toEqual(540); 136 | }); 137 | 138 | it('converts radians to degrees', () => { 139 | expect(toDegrees('1.5708rad')).toEqual(90); 140 | expect(toDegrees('5rad')).toEqual(286.48); 141 | expect(toDegrees('7rad')).toEqual(401.07); 142 | }); 143 | 144 | it('converts turns to degrees', () => { 145 | expect(toDegrees('.2turn')).toEqual(72); 146 | expect(toDegrees('1turn')).toEqual(360); 147 | expect(toDegrees('1.5turn')).toEqual(540); 148 | }); 149 | 150 | it('handles negative values', () => { 151 | expect(toDegrees('-0.25turn')).toEqual(270); 152 | }); 153 | }); 154 | 155 | describe('toPercent', () => { 156 | it('converts a number to a percent float', () => { 157 | expect(toPercent(30, 90)).toEqual(0.3333); 158 | expect(toPercent(123, 256)).toEqual(0.4805); 159 | expect(toPercent(256, 256)).toEqual(1); 160 | }); 161 | }); 162 | }); 163 | -------------------------------------------------------------------------------- /src/converters/hwb.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of methods that take normalized HWB values and outputs normalized values for each 3 | * supported color model. Hwb -> hwb is a passthrough. 4 | * 5 | * @module 6 | */ 7 | 8 | import { HSB, HSLA, HSV, HWB, RGBA } from '../colors'; 9 | import { hsbToHsl } from './hsb'; 10 | import { hslToRgb } from './hsl'; 11 | import { 12 | DefaultOpacity, 13 | MaxFixedFloat, 14 | MaxPercentFloat, 15 | toDegrees, 16 | } from '../utilities'; 17 | 18 | /** 19 | * Converts an HWB color to a HEXA color. 20 | * 21 | * @example 22 | * hwbToHex('180deg', 0.5, 0.25 / 0.9) // return { r: 0.5, g: 0.75, b: 0.75, a: 0.9 } 23 | * 24 | * @param hwba - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 25 | */ 26 | export const hwbToHex = ([h, w, b, a = DefaultOpacity]: [ 27 | string, 28 | number, 29 | number, 30 | number?, 31 | ]): RGBA => hwbToHexa([h, w, b, a]); 32 | 33 | /** 34 | * Converts an HWB color to a HEXA color. 35 | * 36 | * @example 37 | * hwbToHexa('180deg', 0.5, 0.25 / 0.9) // return { r: 0.5, g: 0.75, b: 0.75, a: 0.9 } 38 | * 39 | * @param hwba - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 40 | */ 41 | export const hwbToHexa = ([h, w, b, a = DefaultOpacity]: [ 42 | string, 43 | number, 44 | number, 45 | number?, 46 | ]): RGBA => hwbToRgb([h, w, b, a]); 47 | 48 | /** 49 | * Converts an HWB color to an HSB color. 50 | * 51 | * @example 52 | * hwbToHsb('227deg', 0.3333, 0.35, 0.75) // return { h: 227, w: 0.2334, b: 0.5333, a: 0.75 } 53 | * 54 | * @param hwb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 55 | */ 56 | export const hwbToHsb = ([h, w, b]: [string, number, number]): HSB => { 57 | const Angle = toDegrees(h); 58 | 59 | return { 60 | h: Angle, 61 | s: +(b === MaxPercentFloat ? 0 : 1 - w / (1 - b)).toFixed(MaxFixedFloat), 62 | b: +(1 - b).toFixed(MaxFixedFloat), 63 | }; 64 | }; 65 | 66 | /** 67 | * Converts an HWB color to an HSLA color. 68 | * 69 | * @example 70 | * hwbToHsl('180deg', 0.5, 0.25 / 0.9) // return { r: 0.5, g: 0.75, b: 0.75, a: 0.9 } 71 | * 72 | * @param hwba - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 73 | */ 74 | export const hwbToHsl = ([h, w, b, a = DefaultOpacity]: [ 75 | string, 76 | number, 77 | number, 78 | number?, 79 | ]): HSLA => hwbToHsla([h, w, b, a]); 80 | 81 | /** 82 | * Converts an HWB color to an HSLA color. 83 | * 84 | * @example 85 | * hwbToHsla('180deg', 0.5, 0.25 / 0.9) // return { r: 0.5, g: 0.75, b: 0.75, a: 0.9 } 86 | * 87 | * @param hwba - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 88 | */ 89 | export const hwbToHsla = ([h, w, b, a = DefaultOpacity]: [ 90 | string, 91 | number, 92 | number, 93 | number?, 94 | ]): HSLA => { 95 | const Hsb = hwbToHsb([h, w, b]); 96 | const Hsla = hsbToHsl([`${Hsb.h}`, Hsb.s, Hsb.b]); 97 | 98 | return { 99 | ...Hsla, 100 | a, 101 | }; 102 | }; 103 | 104 | /** 105 | * Converts an HWB color to an HSV color. 106 | * 107 | * @example 108 | * hwbToHsv('227deg', 0.3333, 0.35, 0.75) // return { h: 227, w: 0.2334, b: 0.5333, a: 0.75 } 109 | * 110 | * @param hwb - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 111 | */ 112 | export const hwbToHsv = ([h, w, b]: [string, number, number]): HSV => { 113 | const Hsb = hwbToHsb([h, w, b]); 114 | 115 | return { 116 | h: Hsb.h, 117 | s: Hsb.s, 118 | v: Hsb.b, 119 | }; 120 | }; 121 | 122 | /** 123 | * Passthrough for HWB to HWB conversion. 124 | * 125 | * @example 126 | * hwbToHwb('227deg', 0.3333, 0.35, 0.75) // return { h: 227, s: 0.3333, l: 0.35, a: 0.75 } 127 | * 128 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 129 | */ 130 | export const hwbToHwb = ([h, w, b, a = DefaultOpacity]: [ 131 | string, 132 | number, 133 | number, 134 | number?, 135 | ]): HWB => ({ 136 | h: toDegrees(h), 137 | w, 138 | b, 139 | a, 140 | }); 141 | 142 | /** 143 | * Converts an HWB color to an RGBA color. 144 | * 145 | * @example 146 | * hwbToRgb('180deg', 0.5, 0.25 / 0.9) // return { r: 0.5, g: 0.75, b: 0.75, a: 0.9 } 147 | * 148 | * @param hwba - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 149 | */ 150 | export const hwbToRgb = ([h, w, b, a = DefaultOpacity]: [ 151 | string, 152 | number, 153 | number, 154 | number?, 155 | ]): RGBA => hwbToRgba([h, w, b, a]); 156 | 157 | /** 158 | * Converts an HWB color to an RGBA color. 159 | * 160 | * @example 161 | * hwbToRgba('180deg', 0.5, 0.25 / 0.9) // return { r: 0.5, g: 0.75, b: 0.75, a: 0.9 } 162 | * 163 | * @param hwba - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 164 | */ 165 | export const hwbToRgba = ([h, w, b, a = DefaultOpacity]: [ 166 | string, 167 | number, 168 | number, 169 | number?, 170 | ]): RGBA => { 171 | const Angle = toDegrees(h); 172 | const Hwb: HWB = { 173 | h: Angle, 174 | w, 175 | b, 176 | a, 177 | }; 178 | const Rgba = hslToRgb([`${Angle}`, 1, 0.5, a]); 179 | const TotalBrightness = Hwb.w + Hwb.b; 180 | 181 | if (TotalBrightness >= 1) { 182 | Hwb.w = +(Hwb.w / TotalBrightness).toFixed(MaxFixedFloat); 183 | Hwb.b = +(Hwb.b / TotalBrightness).toFixed(MaxFixedFloat); 184 | } 185 | 186 | Rgba.r = Rgba.r * (1 - Hwb.w - Hwb.b) + Hwb.w; 187 | Rgba.g = Rgba.g * (1 - Hwb.w - Hwb.b) + Hwb.w; 188 | Rgba.b = Rgba.b * (1 - Hwb.w - Hwb.b) + Hwb.w; 189 | 190 | return Rgba; 191 | }; 192 | -------------------------------------------------------------------------------- /src/converters/rgb.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of methods that take normalized RGBA values (0..1) and outputs normalized values for each 3 | * supported color model. Rgb -> hex and rgb -> rgb are passthroughs. 4 | * 5 | * @module 6 | */ 7 | 8 | import { HSB, HSLA, HSV, HWB, RGBA } from '../colors'; 9 | import { 10 | DefaultOpacity, 11 | MaxFixedFloat, 12 | MaxFixedNumber, 13 | } from '../utilities'; 14 | 15 | /** 16 | * Converts an RGB color to a HEXA color. 17 | * 18 | * @example 19 | * rgbToHex(0.8667, 1, 0.9333) // return { r: 0.8667, g: 1, b: 0.9333, a: 1 } 20 | * rgbToHex(0.8667, 1, 0.9333, 0.6) // return { r: 0.8667, g: 1, b: 0.9333, a: 0.6 } 21 | * 22 | * @param rgba - Color components as fractions. 23 | */ 24 | export const rgbToHex = ([r, g, b, a = DefaultOpacity]: [ 25 | number, 26 | number, 27 | number, 28 | number?, 29 | ]): RGBA => rgbToHexa([r, g, b, a]); 30 | 31 | /** 32 | * Converts an RGB color to a HEXA color. 33 | * 34 | * @example 35 | * rgbToHexa(0.8667, 1, 0.9333) // return { r: 0.8667, g: 1, b: 0.9333, a: 1 } 36 | * rgbToHexa(0.8667, 1, 0.9333, 0.6) // return { r: 0.8667, g: 1, b: 0.9333, a: 0.6 } 37 | * 38 | * @param rgba - Color components as fractions. 39 | */ 40 | export const rgbToHexa = ([r, g, b, a = DefaultOpacity]: [ 41 | number, 42 | number, 43 | number, 44 | number?, 45 | ]): RGBA => rgbToRgba([r, g, b, a]); 46 | 47 | /** 48 | * Converts an RGB color to an HSB color. 49 | * 50 | * @example 51 | * rgbToHsb(0.8667, 1, 0.9333) // return { h: 226.78, s: 0.4917, b: 0.4706 } 52 | * 53 | * @param rgb - Color components as fractions. 54 | */ 55 | export const rgbToHsb = ([r, g, b]: [number, number, number]): HSB => { 56 | const Hsla = rgbToHsl([r, g, b]); 57 | const Saturation = Hsla.s * (Hsla.l < 0.5 ? Hsla.l : 1 - Hsla.l); 58 | 59 | return { 60 | h: Hsla.h, 61 | s: +((2 * Saturation) / (Hsla.l + Saturation)).toFixed(MaxFixedFloat), 62 | b: +(Hsla.l + Saturation).toFixed(MaxFixedFloat), 63 | }; 64 | }; 65 | 66 | /** 67 | * Converts an RGB[A] color to an HSLA color. 68 | * 69 | * @example 70 | * rgbToHsl(0.2392, 0.2902, 0.4706) // return { h: 226.78, s: 0.3260, l: 0.3549, a: 1 } 71 | * 72 | * @param rgba - Color components as fractions. 73 | */ 74 | export const rgbToHsl = ([r, g, b, a = DefaultOpacity]: [ 75 | number, 76 | number, 77 | number, 78 | number?, 79 | ]): HSLA => rgbToHsla([r, g, b, a]); 80 | 81 | /** 82 | * Converts an RGB[A] color to an HSLA color. 83 | * 84 | * @example 85 | * rgbToHsla(0.2392, 0.2902, 0.4706) // return { h: 226.78, s: 0.3260, l: 0.3549, a: 1 } 86 | * 87 | * @param rgba - Color components as fractions. 88 | */ 89 | export const rgbToHsla = ([r, g, b, a = DefaultOpacity]: [ 90 | number, 91 | number, 92 | number, 93 | number?, 94 | ]): HSLA => { 95 | const MaxLight = Math.max(r, g, b); 96 | const MinLight = Math.min(r, g, b); 97 | const LightnessDiff = MaxLight - MinLight; 98 | const Lightness = (MaxLight + MinLight) / 2; 99 | 100 | let hue = 0; 101 | let saturation = 0; 102 | 103 | if (LightnessDiff !== 0) { 104 | saturation = 105 | Lightness === 0 || Lightness === 1 106 | ? 0 107 | : (MaxLight - Lightness) / Math.min(Lightness, 1 - Lightness); 108 | 109 | switch (MaxLight) { 110 | case r: 111 | hue = (g - b) / LightnessDiff + (g < b ? 6 : 0); 112 | break; 113 | case g: 114 | hue = (b - r) / LightnessDiff + 2; 115 | break; 116 | case b: 117 | hue = (r - g) / LightnessDiff + 4; 118 | break; 119 | } 120 | 121 | hue *= 60; 122 | } 123 | 124 | return { 125 | h: +hue.toFixed(MaxFixedNumber), 126 | s: +saturation.toFixed(MaxFixedFloat), 127 | l: +Lightness.toFixed(MaxFixedFloat), 128 | a, 129 | }; 130 | }; 131 | 132 | /** 133 | * Converts an RGB color to an HSV color. 134 | * 135 | * @example 136 | * rgbToHsv(0.8667, 1, 0.9333) // return { h: 226.78, s: 0.4917, v: 0.4706 } 137 | * 138 | * @param rgb - Color components as fractions. 139 | */ 140 | export const rgbToHsv = ([r, g, b]: [number, number, number]): HSV => { 141 | const Hsb = rgbToHsb([r, g, b]); 142 | 143 | return { 144 | h: Hsb.h, 145 | s: Hsb.s, 146 | v: Hsb.b, 147 | }; 148 | }; 149 | 150 | /** 151 | * Converts an RGB[A] color to an HWB color. 152 | * 153 | * @example 154 | * rgbToHwb(0.8667, 1, 0.9333) // return { h: 226.78, w: 0.2392, b: 0.5294, a: 1 } 155 | * 156 | * @param rgba - Color components as fractions. 157 | */ 158 | export const rgbToHwb = ([r, g, b, a = DefaultOpacity]: [ 159 | number, 160 | number, 161 | number, 162 | number?, 163 | ]): HWB => { 164 | const Hsla = rgbToHsl([r, g, b, a]); 165 | const White = Math.min(r, g, b); 166 | const Black = 1 - Math.max(r, g, b); 167 | 168 | return { 169 | h: Hsla.h, 170 | w: White, 171 | b: Black, 172 | a: Hsla.a, 173 | }; 174 | }; 175 | 176 | /** 177 | * Passthrough for RGB[A] to RGBA conversion. 178 | * 179 | * @example 180 | * rgbToRgb(0.8667, 1, 0.9333) // return { r: 0.8667, g: 1, b: 0.9333, a: 1 } 181 | * 182 | * @param rgba - Color components as fractions. 183 | */ 184 | export const rgbToRgb = ([r, g, b, a = DefaultOpacity]: [ 185 | number, 186 | number, 187 | number, 188 | number?, 189 | ]): RGBA => rgbToRgba([r, g, b, a]); 190 | 191 | /** 192 | * Passthrough for RGB[A] to RGBA conversion. 193 | * 194 | * @example 195 | * rgbToRgba(0.8667, 1, 0.9333) // return { r: 0.8667, g: 1, b: 0.9333, a: 1 } 196 | * 197 | * @param rgba - Color components as fractions. 198 | */ 199 | export const rgbToRgba = ([r, g, b, a = DefaultOpacity]: [ 200 | number, 201 | number, 202 | number, 203 | number?, 204 | ]): RGBA => ({ 205 | r, 206 | g, 207 | b, 208 | a, 209 | }); 210 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for All files 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files

23 |
24 | 25 |
26 | 100% 27 | Statements 28 | 643/643 29 |
30 | 31 | 32 |
33 | 85.18% 34 | Branches 35 | 115/135 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 92/92 43 |
44 | 45 | 46 |
47 | 100% 48 | Lines 49 | 353/353 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 | 63 |
64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 |
FileStatementsBranchesFunctionsLines
src 84 |
85 |
100%179/17996.07%49/51100%38/38100%164/164
src/converters 99 |
100 |
100%464/46478.57%66/84100%54/54100%189/189
113 |
114 |
115 |
116 | 121 | 122 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/converters/hsl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of methods that take normalized HSLA values and outputs normalized values for each 3 | * supported color model. Hsl -> hsl is a passthrough. 4 | * 5 | * @module 6 | */ 7 | 8 | import { HSB, HSLA, HSV, HWB, RGBA } from '../colors'; 9 | import { 10 | DefaultOpacity, 11 | DegreesInCircle, 12 | hueToRgb, 13 | MaxFixedFloat, 14 | MaxPercentFloat, 15 | toDegrees, 16 | } from '../utilities'; 17 | 18 | /** 19 | * Converts an HSL[A] color to a HEXA color. 20 | * 21 | * @example 22 | * hslToHex('227deg', 0.3333, 0.35) // return { r: 0.2333, g: 0.2839, b: 0.4667, a: 1 } 23 | * 24 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 25 | */ 26 | export const hslToHex = ([h, s, l, a = DefaultOpacity]: [ 27 | string, 28 | number, 29 | number, 30 | number?, 31 | ]): RGBA => hslToHexa([h, s, l, a]); 32 | 33 | /** 34 | * Converts an HSL[A] color to a HEXA color. 35 | * 36 | * @example 37 | * hslToHexa('227deg', 0.3333, 0.35) // return { r: 0.2333, g: 0.2839, b: 0.4667, a: 1 } 38 | * 39 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 40 | */ 41 | export const hslToHexa = ([h, s, l, a = DefaultOpacity]: [ 42 | string, 43 | number, 44 | number, 45 | number?, 46 | ]): RGBA => hslToRgb([h, s, l, a]); 47 | 48 | /** 49 | * Converts an HSL color to an HSB color. 50 | * 51 | * @example 52 | * hslToHsb('0.85turn', 0.3333, 0.35) // return { h: 306, s: 0.5, b: 0.4667 } 53 | * 54 | * @param hsl - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 55 | */ 56 | export const hslToHsb = ([h, s, l]: [string, number, number]): HSB => { 57 | const Angle = toDegrees(h); 58 | const Saturation = s * (l < 0.5 ? l : 1 - l); 59 | 60 | return { 61 | h: Angle, 62 | s: +((2 * Saturation) / (l + Saturation)).toFixed(MaxFixedFloat), 63 | b: +(l + Saturation).toFixed(MaxFixedFloat), 64 | }; 65 | }; 66 | 67 | /** 68 | * Passthrough for HSL[A] to HSLA conversion. 69 | * 70 | * @example 71 | * hslToHsl('227deg', 0.3333, 0.35) // return { h: 227, s: 0.3333, l: 0.35, a: 1 } 72 | * 73 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 74 | */ 75 | export const hslToHsl = ([h, s, l, a = DefaultOpacity]: [ 76 | string, 77 | number, 78 | number, 79 | number?, 80 | ]): HSLA => hslToHsla([h, s, l, a]); 81 | 82 | /** 83 | * Passthrough for HSL[A] to HSLA conversion. 84 | * 85 | * @example 86 | * hslToHsla('227deg', 0.3333, 0.35) // return { h: 227, s: 0.3333, l: 0.35, a: 1 } 87 | * 88 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 89 | */ 90 | export const hslToHsla = ([h, s, l, a = DefaultOpacity]: [ 91 | string, 92 | number, 93 | number, 94 | number?, 95 | ]): HSLA => ({ 96 | h: toDegrees(h), 97 | s, 98 | l, 99 | a, 100 | }); 101 | 102 | /** 103 | * Converts an HSL color to an HSV color. 104 | * 105 | * @example 106 | * hslToHsv('0.85turn', 0.3333, 0.35) // return { h: 306, s: 0.5, v: 0.4667 } 107 | * 108 | * @param hsl - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 109 | */ 110 | export const hslToHsv = ([h, s, l]: [string, number, number]): HSV => { 111 | const Hsb = hslToHsb([h, s, l]); 112 | 113 | return { 114 | h: Hsb.h, 115 | s: Hsb.s, 116 | v: Hsb.b, 117 | }; 118 | }; 119 | 120 | /** 121 | * Converts an HSL[A] color to an HWB color. 122 | * 123 | * @example 124 | * hslToHwb('227deg', 0.3333, 0.35, 0.75) // return { h: 227, w: 0.2334, b: 0.5333, a: 0.75 } 125 | * 126 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 127 | */ 128 | export const hslToHwb = ([h, s, l, a = DefaultOpacity]: [ 129 | string, 130 | number, 131 | number, 132 | number?, 133 | ]): HWB => { 134 | const Angle = toDegrees(h); 135 | const Hsb = hslToHsb([h, s, l]); 136 | const Blackness = 1 - Hsb.b; 137 | const Whiteness = (1 - Hsb.s) * Hsb.b; 138 | 139 | return { 140 | h: Angle, 141 | w: +Whiteness.toFixed(MaxFixedFloat), 142 | b: +Blackness.toFixed(MaxFixedFloat), 143 | a, 144 | }; 145 | }; 146 | 147 | /** 148 | * Converts an HSL[A] color to an RGBA color. 149 | * 150 | * @example 151 | * hslToRgb('227deg', 0.3333, 0.35) // return { r: 0.2333, g: 0.2839, b: 0.4667, a: 1 } 152 | * 153 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 154 | */ 155 | export const hslToRgb = ([h, s, l, a = DefaultOpacity]: [ 156 | string, 157 | number, 158 | number, 159 | number?, 160 | ]): RGBA => hslToRgba([h, s, l, a]); 161 | 162 | /** 163 | * Converts an HSL[A] color to an RGBA color. 164 | * 165 | * @example 166 | * hslToRgba('227deg', 0.3333, 0.35) // return { r: 0.2333, g: 0.2839, b: 0.4667, a: 1 } 167 | * 168 | * @param hsla - Color components as an {@link utilities.toDegrees | angle} followed by fractions. 169 | */ 170 | export const hslToRgba = ([h, s, l, a = DefaultOpacity]: [ 171 | string, 172 | number, 173 | number, 174 | number?, 175 | ]): RGBA => { 176 | const Angle = toDegrees(h) % DegreesInCircle; 177 | const Saturation = s === MaxPercentFloat ? s : s % MaxPercentFloat; 178 | const Lightness = l === MaxPercentFloat ? l : l % MaxPercentFloat; 179 | const Rgba: RGBA = { 180 | r: 0, 181 | g: 0, 182 | b: 0, 183 | a, 184 | }; 185 | 186 | if (Saturation === 0) { 187 | Rgba.r = 0; 188 | Rgba.g = 0; 189 | Rgba.b = 0; 190 | } else { 191 | Rgba.r = hueToRgb(0, Angle, Saturation, Lightness); 192 | Rgba.g = hueToRgb(8, Angle, Saturation, Lightness); 193 | Rgba.b = hueToRgb(4, Angle, Saturation, Lightness); 194 | } 195 | 196 | return Rgba; 197 | }; 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # colorcolor - Javascript Function 2 | 3 | [![npm version](https://badge.fury.io/js/colorcolor.svg)](https://badge.fury.io/js/colorcolor) 4 | [![Build Status](https://github.com/metaloha/color2color/actions/workflows/node.js.yml/badge.svg)](https://github.com/metaloha/color2color/actions) 5 | 6 | The `colorcolor()` function converts HEX/HEXA/HSB/HSL/HSLA/HSV/HWB/RGB/RGBA color strings to 7 | HEX/HEXA/HSB/HSL/HSLA/HSV/HWB/RGB/RGBA color strings. 8 | 9 | ## Demo 10 | 11 | [http://metaloha.github.io/color2color/](http://metaloha.github.io/color2color/) 12 | 13 | ## Install 14 | 15 | `colorcolor` is available via NPM: 16 | 17 | npm install colorcolor 18 | 19 | ## Usage 20 | 21 | colorcolor( originalColor: string, [newColorType: string( hex|hexa|hsb|hsl|hsla|hsv|hwb|rgb|rgba ) = 'rgba'] ) 22 | 23 | The first argument is the original color string in HEX, HEXA, HSB, HSL, HSLA, HSV, HWB, RGB, or RGBA format, or a CSS 24 | named color. 25 | 26 | The second argument (optional) is which format you'd like the new color string to be in. This will always default 27 | to `'rgba'`. 28 | 29 | ## Examples 30 | 31 | ```ts 32 | colorcolor('#dfe') === 'rgba(221, 255, 238, 1)' 33 | colorcolor('#036', 'rgb') === 'rgb(0, 51, 102)' 34 | colorcolor('rgba(64,64,64,0.5)') === 'rgba(64, 64, 64, 0.5)' 35 | colorcolor('rgba(64 64 64 / 0.5)') === 'rgba(64, 64, 64, 0.5)' 36 | colorcolor('rgb(64,64,64)', 'hex') === '#404040' 37 | colorcolor('#dfe', 'rgba') === 'rgba(221, 255, 238, 1)' 38 | colorcolor('hsla(109,100%,37%,1)') === 'rgba(35, 189, 0, 1)' 39 | colorcolor('hsla(0.35turn 70% 55% / 1)') === 'rgba(60, 221, 76, 1)' 40 | colorcolor('rgba(35,189,0,0.75)', 'hsl') === 'hsl(109, 100%, 37%)' 41 | colorcolor('rgba(85%,55%,10.5%,70%)', 'hsl') === 'hsl(36, 78%, 48%)' 42 | colorcolor('#3fa796a0', 'hsla') === 'hsla(170, 45%, 45%, 0.63)' 43 | colorcolor('hwb(200grad 50% 25% / 0.75)') === 'rgba(128, 191, 191, 0.75)' 44 | colorcolor('AliceBlue', 'hwb') === 'hwb(208 94% 0% / 1)' 45 | ``` 46 | 47 | ## Valid color formats 48 | 49 | `colorcolor` understands a mix of CSS and non-CSS color models, and is being actively expanded. The following formats 50 | are currently understood by `colorcolor`: 51 | 52 | ### RGB (_[read more](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model)_) 53 | 54 | Both hexadecimal and numeric notations are supported. 55 | 56 | * **#RGB[A]** 57 | * **#RRGGBB[AA]** 58 | * **R**, **G**, **B**, and **A** are hexadecimal numbers from `00` to `ff` 59 | * _Examples_: 60 | * `#ad6` 61 | * `#AD6e` 62 | * `#f43E12` 63 | * `#F43e12d5` 64 | * **rgb(R,G,B)** 65 | * **rgb(R G B)** 66 | * **rgba(R, G, B, A)** 67 | * **rgba(R G B / A)** 68 | * **R**, **G**, and **B** can be decimals from 0 to 255 or percentages from 0% to 100% 69 | * **A** can be a percentage from 0% to 100% or a float from 0 to 1 70 | * _Examples_ 71 | * `rgb(121, 50, 89)` 72 | * `rgb(121 50 89)` 73 | * `rgba(80, 205, 40, 0.5)` 74 | * `rgba(80 205 40 / 0.5)` 75 | * `rgb(50%, 75%, 50%)` 76 | * `rgba(50% 75% 50% / 75%)` 77 | 78 | ### HSL (_[read more](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl_color_model)_) 79 | 80 | * **hsl(H, S, L)** 81 | * **hsl(H S L)** 82 | * **hsla(H, S, L, A)** 83 | * **hsla(H S L / A)** 84 | * **H** is an angle expressed as `deg`, `grad`, `rad`, or `turn` (degree is assumed if the number doesn't include a 85 | unit) 86 | * **S** and **L** are percentages from 0% to 100% 87 | * **A** can be a percentage from 0% to 100% or a float from 0 to 1 88 | * _Examples_ 89 | * `hsl(270, 100%, 50%)` 90 | * `hsl(0.75turn 100% 50%)` 91 | * `hsla(2.65grad, 100%, 50%, 0.75)` 92 | * `hsla(3.14rad 100% 50% / 75%)` 93 | 94 | ### HSV/HSB (_[read more](https://color.lukas-stratmann.com/color-systems/hsv.html)_) 95 | 96 | `hsv` and `hsb` are interchangeable and mean the same thing. These are not CSS colors, but are still supported as input 97 | and output formats. 98 | 99 | * **hsv(H, S, V)** 100 | * **hsb(H, S, B)** 101 | * **H** is an angle expressed as `deg`, `grad`, `rad`, or `turn` (degree is assumed if the number doesn't include a 102 | unit) 103 | * **S**, **V**, and **B** are percentages from 0% to 100% 104 | * _Examples_ 105 | * `hsv(0.75turn, 100%, 50%)` 106 | * `hsb(270, 100%, 50%)` 107 | 108 | ### HWB (_[read more](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hwb_color_model)_) 109 | 110 | Note that this function does not accept commas, and instead of an additional `hwba` function the opacity is optional. 111 | 112 | * **hwb(H W B)** 113 | * **hwb(H W B / A)** 114 | * **H** is an angle expressed as `deg`, `grad`, `rad`, or `turn` (degree is assumed if the number doesn't include a 115 | unit) 116 | * **W** and **B** are percentages from 0% to 100% 117 | * **A** can be a percentage from 0% to 100% or a float from 0 to 1 118 | * _Examples_ 119 | * `hwb(270 100% 50%)` 120 | * `hwb(0.75turn 100% 50%)` 121 | * `hwb(2.65grad 100% 50% / 0.75)` 122 | * `hwb(3.14rad 100% 50% / 75%)` 123 | 124 | ## Testing 125 | 126 | You can run `npm run test` or `npm run test:watch` to run the tests alone. The coverage report can be updated 127 | with `npm run test:coverage`. 128 | 129 | ## Support 130 | 131 | Reach out to the maintainer at one of the following places: 132 | 133 | - [GitHub discussions](https://github.com/metaloha/color2color/discussions) 134 | - The email which is located [in my GitHub profile](https://github.com/metaloha) 135 | 136 | ## Contributing [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/metaloha/color2color/issues) 137 | 138 | First off, thanks for taking the time to contribute! Contributions are what make the open-source community such an 139 | amazing place to learn, inspire, and create. Any contributions you make will benefit everybody else and are **greatly 140 | appreciated**. 141 | 142 | We have set up a separate document containing our [contribution guidelines](docs/CONTRIBUTING.md). 143 | 144 | Thank you for being involved! 145 | 146 | ## Authors & contributors 147 | 148 | The original setup of this repository is by [Russel Porosky](https://github.com/metaloha). 149 | 150 | For a full list of all authors and contributors, 151 | check [the contributor's page](https://github.com/metaloha/color2color/contributors). 152 | 153 | ## License 154 | 155 | This project is licensed under the **MIT license**. 156 | 157 | See [LICENSE](LICENSE) for more information. 158 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* yellow */ 156 | .cbranch-no { background: yellow !important; color: #111; } 157 | /* dark red */ 158 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 159 | .low .chart { border:1px solid #C21F39 } 160 | .highlighted, 161 | .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ 162 | background: #C21F39 !important; 163 | } 164 | /* medium red */ 165 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 166 | /* light red */ 167 | .low, .cline-no { background:#FCE1E5 } 168 | /* light green */ 169 | .high, .cline-yes { background:rgb(230,245,208) } 170 | /* medium green */ 171 | .cstat-yes { background:rgb(161,215,106) } 172 | /* dark green */ 173 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 174 | .high .chart { border:1px solid rgb(77,146,33) } 175 | /* dark yellow (gold) */ 176 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; } 177 | .medium .chart { border:1px solid #f9cd0b; } 178 | /* light yellow */ 179 | .medium { background: #fff4c2; } 180 | 181 | .cstat-skip { background: #ddd; color: #111; } 182 | .fstat-skip { background: #ddd; color: #111 !important; } 183 | .cbranch-skip { background: #ddd !important; color: #111; } 184 | 185 | span.cline-neutral { background: #eaeaea; } 186 | 187 | .coverage-summary td.empty { 188 | opacity: .5; 189 | padding-top: 4px; 190 | padding-bottom: 4px; 191 | line-height: 1; 192 | color: #888; 193 | } 194 | 195 | .cover-fill, .cover-empty { 196 | display:inline-block; 197 | height: 12px; 198 | } 199 | .chart { 200 | line-height: 0; 201 | } 202 | .cover-empty { 203 | background: white; 204 | } 205 | .cover-full { 206 | border-right: none !important; 207 | } 208 | pre.prettyprint { 209 | border: none !important; 210 | padding: 0 !important; 211 | margin: 0 !important; 212 | } 213 | .com { color: #999 !important; } 214 | .ignore-none { color: #999; font-weight: normal; } 215 | 216 | .wrapper { 217 | min-height: 100%; 218 | height: auto !important; 219 | height: 100%; 220 | margin: 0 auto -48px; 221 | } 222 | .footer, .push { 223 | height: 48px; 224 | } 225 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/sorter.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var addSorting = (function() { 3 | 'use strict'; 4 | var cols, 5 | currentSort = { 6 | index: 0, 7 | desc: false 8 | }; 9 | 10 | // returns the summary table element 11 | function getTable() { 12 | return document.querySelector('.coverage-summary'); 13 | } 14 | // returns the thead element of the summary table 15 | function getTableHeader() { 16 | return getTable().querySelector('thead tr'); 17 | } 18 | // returns the tbody element of the summary table 19 | function getTableBody() { 20 | return getTable().querySelector('tbody'); 21 | } 22 | // returns the th element for nth column 23 | function getNthColumn(n) { 24 | return getTableHeader().querySelectorAll('th')[n]; 25 | } 26 | 27 | function onFilterInput() { 28 | const searchValue = document.getElementById('fileSearch').value; 29 | const rows = document.getElementsByTagName('tbody')[0].children; 30 | for (let i = 0; i < rows.length; i++) { 31 | const row = rows[i]; 32 | if ( 33 | row.textContent 34 | .toLowerCase() 35 | .includes(searchValue.toLowerCase()) 36 | ) { 37 | row.style.display = ''; 38 | } else { 39 | row.style.display = 'none'; 40 | } 41 | } 42 | } 43 | 44 | // loads the search box 45 | function addSearchBox() { 46 | var template = document.getElementById('filterTemplate'); 47 | var templateClone = template.content.cloneNode(true); 48 | templateClone.getElementById('fileSearch').oninput = onFilterInput; 49 | template.parentElement.appendChild(templateClone); 50 | } 51 | 52 | // loads all columns 53 | function loadColumns() { 54 | var colNodes = getTableHeader().querySelectorAll('th'), 55 | colNode, 56 | cols = [], 57 | col, 58 | i; 59 | 60 | for (i = 0; i < colNodes.length; i += 1) { 61 | colNode = colNodes[i]; 62 | col = { 63 | key: colNode.getAttribute('data-col'), 64 | sortable: !colNode.getAttribute('data-nosort'), 65 | type: colNode.getAttribute('data-type') || 'string' 66 | }; 67 | cols.push(col); 68 | if (col.sortable) { 69 | col.defaultDescSort = col.type === 'number'; 70 | colNode.innerHTML = 71 | colNode.innerHTML + ''; 72 | } 73 | } 74 | return cols; 75 | } 76 | // attaches a data attribute to every tr element with an object 77 | // of data values keyed by column name 78 | function loadRowData(tableRow) { 79 | var tableCols = tableRow.querySelectorAll('td'), 80 | colNode, 81 | col, 82 | data = {}, 83 | i, 84 | val; 85 | for (i = 0; i < tableCols.length; i += 1) { 86 | colNode = tableCols[i]; 87 | col = cols[i]; 88 | val = colNode.getAttribute('data-value'); 89 | if (col.type === 'number') { 90 | val = Number(val); 91 | } 92 | data[col.key] = val; 93 | } 94 | return data; 95 | } 96 | // loads all row data 97 | function loadData() { 98 | var rows = getTableBody().querySelectorAll('tr'), 99 | i; 100 | 101 | for (i = 0; i < rows.length; i += 1) { 102 | rows[i].data = loadRowData(rows[i]); 103 | } 104 | } 105 | // sorts the table using the data for the ith column 106 | function sortByIndex(index, desc) { 107 | var key = cols[index].key, 108 | sorter = function(a, b) { 109 | a = a.data[key]; 110 | b = b.data[key]; 111 | return a < b ? -1 : a > b ? 1 : 0; 112 | }, 113 | finalSorter = sorter, 114 | tableBody = document.querySelector('.coverage-summary tbody'), 115 | rowNodes = tableBody.querySelectorAll('tr'), 116 | rows = [], 117 | i; 118 | 119 | if (desc) { 120 | finalSorter = function(a, b) { 121 | return -1 * sorter(a, b); 122 | }; 123 | } 124 | 125 | for (i = 0; i < rowNodes.length; i += 1) { 126 | rows.push(rowNodes[i]); 127 | tableBody.removeChild(rowNodes[i]); 128 | } 129 | 130 | rows.sort(finalSorter); 131 | 132 | for (i = 0; i < rows.length; i += 1) { 133 | tableBody.appendChild(rows[i]); 134 | } 135 | } 136 | // removes sort indicators for current column being sorted 137 | function removeSortIndicators() { 138 | var col = getNthColumn(currentSort.index), 139 | cls = col.className; 140 | 141 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 142 | col.className = cls; 143 | } 144 | // adds sort indicators for current column being sorted 145 | function addSortIndicators() { 146 | getNthColumn(currentSort.index).className += currentSort.desc 147 | ? ' sorted-desc' 148 | : ' sorted'; 149 | } 150 | // adds event listeners for all sorter widgets 151 | function enableUI() { 152 | var i, 153 | el, 154 | ithSorter = function ithSorter(i) { 155 | var col = cols[i]; 156 | 157 | return function() { 158 | var desc = col.defaultDescSort; 159 | 160 | if (currentSort.index === i) { 161 | desc = !currentSort.desc; 162 | } 163 | sortByIndex(i, desc); 164 | removeSortIndicators(); 165 | currentSort.index = i; 166 | currentSort.desc = desc; 167 | addSortIndicators(); 168 | }; 169 | }; 170 | for (i = 0; i < cols.length; i += 1) { 171 | if (cols[i].sortable) { 172 | // add the click event handler on the th so users 173 | // dont have to click on those tiny arrows 174 | el = getNthColumn(i).querySelector('.sorter').parentElement; 175 | if (el.addEventListener) { 176 | el.addEventListener('click', ithSorter(i)); 177 | } else { 178 | el.attachEvent('onclick', ithSorter(i)); 179 | } 180 | } 181 | } 182 | } 183 | // adds sorting functionality to the UI 184 | return function() { 185 | if (!getTable()) { 186 | return; 187 | } 188 | cols = loadColumns(); 189 | loadData(); 190 | addSearchBox(); 191 | addSortIndicators(); 192 | enableUI(); 193 | }; 194 | })(); 195 | 196 | window.addEventListener('load', addSorting); 197 | -------------------------------------------------------------------------------- /documentation/coverage/lcov-report/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src

23 |
24 | 25 |
26 | 100% 27 | Statements 28 | 179/179 29 |
30 | 31 | 32 |
33 | 96.07% 34 | Branches 35 | 49/51 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 38/38 43 |
44 | 45 | 46 |
47 | 100% 48 | Lines 49 | 164/164 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 | 63 |
64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 |
FileStatementsBranchesFunctionsLines
colorcolor.ts 84 |
85 |
100%57/5789.47%17/19100%2/2100%56/56
colors.ts 99 |
100 |
100%13/13100%4/4100%2/2100%13/13
detectors.ts 114 |
115 |
100%31/31100%12/12100%23/23100%29/29
utilities.ts 129 |
130 |
100%78/78100%16/16100%11/11100%66/66
143 |
144 |
145 |
146 | 151 | 152 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /src/utilities.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A collection of utility functions and constants used by the color conversion, normalization, and finalization 3 | * methods. 4 | * 5 | * @module 6 | */ 7 | 8 | const AngleIndex = 1; 9 | const AngleTypeIndex = 2; 10 | const DegreesInRadian = 180; 11 | const GradiansInCircle = 400; 12 | const HexRadix = 16; 13 | const HexNumberLength = 2; 14 | const MinPercent = 0; 15 | 16 | /** The index used for opacity when a string is converted via regex. */ 17 | export const AlphaIndex = 4; 18 | 19 | /** The index for blackness in HWB colors. */ 20 | export const BlacknessIndex = 3; 21 | 22 | /** The index for blue in HEX[A] and RGB[A] colors. */ 23 | export const BlueIndex = 3; 24 | 25 | /** The index for brightness in HSB colors. */ 26 | export const BrightnessIndex = 3; 27 | 28 | /** When not included, the default opacity to use. */ 29 | export const DefaultOpacity = 1; 30 | 31 | /** The number of degrees used for angle calculations. */ 32 | export const DegreesInCircle = 360; 33 | 34 | /** The index for green in HEX[A] and RGB[A] colors. */ 35 | export const GreenIndex = 2; 36 | 37 | /** The index for hue in HSB, HSL[A], and HWB colors. */ 38 | export const HueIndex = 1; 39 | 40 | /** The index for lightness in HSL[A] colors. */ 41 | export const LightnessIndex = 3; 42 | 43 | /** The precision for float calculations. */ 44 | export const MaxFixedFloat = 4; 45 | 46 | /** The precision used for numeric calculations. */ 47 | export const MaxFixedNumber = 2; 48 | 49 | /** The maximum value of a percentage. */ 50 | export const MaxPercent = 100; 51 | 52 | /** The maximum value of a percentage fraction. */ 53 | export const MaxPercentFloat = 1; 54 | 55 | /** The maximum value of an RGB value. */ 56 | export const MaxRgb = 255; 57 | 58 | /** The smallest number used for degrees in an angle. */ 59 | export const MinAngle = 0; 60 | 61 | /** The index for red in HEX[A] and RGB[A] colors. */ 62 | export const RedIndex = 1; 63 | 64 | /** The index for saturation in HSL[A] colors. */ 65 | export const SaturationIndex = 2; 66 | 67 | /** The index for value in HSV colors. */ 68 | export const ValueIndex = 3; 69 | 70 | /** The index for whiteness in HWB colors. */ 71 | export const WhitenessIndex = 2; 72 | 73 | /** 74 | * Capitalize the first character of a string. 75 | * 76 | * @example 77 | * capitalizeWord('test') // return 'Test' 78 | * 79 | * @param word The word to capitalize; it is not `trim`med first. 80 | */ 81 | export const capitalizeWord = (word: string): string => 82 | word.charAt(0).toUpperCase() + word.slice(1); 83 | 84 | /** 85 | * Clamps a number to be between a range, inclusive. 86 | * 87 | * @example 88 | * clamp(53, 0, 255) // return 53 89 | * clamp(105, 0, 100) // return 100 90 | * 91 | * @param n The number to clamp. 92 | * @param min The lowest amount the number can be. 93 | * @param max The highest amount the number can be. 94 | */ 95 | export const clamp = (n: number, min: number, max: number): number => 96 | Math.min(Math.max(n, min), max); 97 | 98 | /** 99 | * Clamps a number to be between 0 and 360, inclusive. 100 | * 101 | * @example 102 | * clamp(53) // return 53 103 | * clamp(580) // return 360 104 | * 105 | * @param n The number to clamp. 106 | */ 107 | export const clampAngle = (n: number): number => 108 | clamp(n, MinAngle, DegreesInCircle); 109 | 110 | /** 111 | * Clamps a number to be between 0 and 100, inclusive. 112 | * 113 | * @example 114 | * clamp(53) // return 53 115 | * clamp(-10) // return 0 116 | * 117 | * @param n The number to clamp. 118 | */ 119 | export const clampPercent = (n: number): number => 120 | clamp(n, MinPercent, MaxPercent); 121 | 122 | /** 123 | * Clamps a number to be between 0 and 1, inclusive. 124 | * 125 | * @example 126 | * clamp(0.53) // return 0.53 127 | * clamp(-0.1) // return 0 128 | * 129 | * @param n The number to clamp. 130 | */ 131 | export const clampPercentFloat = (n: number): number => 132 | clamp(n, MinPercent, MaxPercentFloat); 133 | 134 | /** 135 | * Convert a hexadecimal number to a Base-10 number. 136 | * 137 | * @example 138 | * hexToNumber('f') // return 15 139 | * 140 | * @param hex The number to convert. 141 | */ 142 | export const hexToNumber = (hex: string): number => parseInt(hex, HexRadix); 143 | 144 | /** 145 | * Converts a hue to an RGB percentage. 146 | * 147 | * @example 148 | * hueToRgb(0, 180, 0.7, 0.8) // return 0.94 149 | * 150 | * @param modifier 151 | * @param hue The hue in degrees (0 to 360). 152 | * @param saturation The percentage of saturation to use (0 to 1). 153 | * @param lightness The percentage of lightness to use (0 to 1). 154 | */ 155 | export const hueToRgb = ( 156 | modifier: number, 157 | hue: number, 158 | saturation: number, 159 | lightness: number, 160 | ): number => { 161 | const K = (modifier + hue / 30) % 12; 162 | const A = saturation * Math.min(lightness, 1 - lightness); 163 | 164 | return +(lightness - A * Math.max(-1, Math.min(K - 3, 9 - K, 1))).toFixed( 165 | MaxFixedFloat, 166 | ); 167 | }; 168 | 169 | /** 170 | * Convert a percent between 0 and 1 to a number. 171 | * 172 | * @example 173 | * fromPercent(0.5, 150) // return 75 174 | * 175 | * @param percent The percent as a float between 0 and 1. 176 | * @param maxDecimal The number that would become 100%. 177 | */ 178 | export const fromPercent = ( 179 | percent: number, 180 | maxDecimal: number = MaxPercent, 181 | ): number => maxDecimal * percent; 182 | 183 | /** 184 | * Convert a decimal number to hexadecimal. Adds a leading 0 if needed to be two digits in total. 185 | * 186 | * @example 187 | * numberToHex(221) // return 'dd' 188 | * 189 | * @param n The number (an RGB number between 0 and 255) to convert to hexadecimal. 190 | */ 191 | export const numberToHex = (n: number): string => 192 | `0${Math.round(n).toString(HexRadix)}`.slice(-HexNumberLength); 193 | 194 | /** 195 | * Convert degrees, gradians, radians, and turns to degrees. If no unit is given in the string, degrees is used. 196 | * 197 | * @example 198 | * toDegrees('180') // return 180 199 | * toDegrees('180deg') // return 180 200 | * toDegrees('0.75turn') // return 270 201 | * toDegrees('-0.25turn') // return 270 202 | * 203 | * @param angle A string comprised of an angle and a unit of `deg|grad|rad|turn`; if no unit is given, `deg` is used. 204 | */ 205 | export const toDegrees = (angle: string): number => { 206 | const Angle = /^\s*(-?\d*\.?\d+)(deg|rad|turn|grad)?$/i.exec(angle); 207 | let response = 0; 208 | 209 | if (Angle && Angle.length > 1) { 210 | const AngleAmount = Angle[AngleIndex]; 211 | const AngleType = Angle[AngleTypeIndex]?.toLowerCase() ?? 'deg'; 212 | 213 | switch (AngleType) { 214 | case 'deg': 215 | response = +AngleAmount; 216 | break; 217 | case 'grad': 218 | response = (+AngleAmount / GradiansInCircle) * DegreesInCircle; 219 | break; 220 | case 'rad': 221 | response = (+AngleAmount * DegreesInRadian) / Math.PI; 222 | break; 223 | case 'turn': 224 | response = +AngleAmount * DegreesInCircle; 225 | break; 226 | } 227 | } 228 | 229 | // we don't care about direction, so just ensure degrees is positive 230 | while (response < MinAngle) { 231 | response += DegreesInCircle; 232 | } 233 | 234 | return +response.toFixed(MaxFixedNumber); 235 | }; 236 | 237 | /** 238 | * Convert a decimal number to a percentage between 0 and 1. 239 | * 240 | * @example 241 | * toPercent(75, 150) // return 0.5 242 | * 243 | * @param amount The number to convert to percent. 244 | * @param limit The amount that would become 100%. 245 | */ 246 | export const toPercent = (amount: number, limit: number): number => 247 | +(amount / limit).toFixed(MaxFixedFloat); 248 | -------------------------------------------------------------------------------- /documentation/modules/colorcolor.html: -------------------------------------------------------------------------------- 1 | colorcolor | colorcolor - v3.0.0
2 |
3 | 10 |
11 |
12 |
13 |
14 | 17 |

Module colorcolor

18 |
19 |

This contains the sole method exposed by this library, colorcolor.

20 |
23 |
24 |
25 |
26 |
27 |

Index

28 |
29 |

Functions

30 |
32 |
66 |
67 |

Generated using TypeDoc

68 |
-------------------------------------------------------------------------------- /documentation/modules/detectors.html: -------------------------------------------------------------------------------- 1 | detectors | colorcolor - v3.0.0
2 |
3 | 10 |
11 |
12 |
13 |
14 | 17 |

Module detectors

18 |
19 |

This is where all the supported color strings are detected and converted to a normalized form. Each detector 20 | contains a link to every converter method for that model. Finally, each detector also converts the normalized 21 | input after conversion into the proper numbers for the output string.

22 |
25 |
26 |
27 |
28 |
29 |

Index

30 |
31 |

Variables

32 |
34 |
68 |
69 |

Generated using TypeDoc

70 |
-------------------------------------------------------------------------------- /documentation/coverage/lcov-report/src/converters/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/converters 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src/converters

23 |
24 | 25 |
26 | 100% 27 | Statements 28 | 464/464 29 |
30 | 31 | 32 |
33 | 78.57% 34 | Branches 35 | 66/84 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 54/54 43 |
44 | 45 | 46 |
47 | 100% 48 | Lines 49 | 189/189 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 | 63 |
64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 |
FileStatementsBranchesFunctionsLines
hex.ts 84 |
85 |
100%70/7078.57%11/14100%9/9100%20/20
hsb.ts 99 |
100 |
100%69/69100%5/5100%9/9100%32/32
hsl.ts 114 |
115 |
100%87/8772.72%16/22100%9/9100%37/37
hsv.ts 129 |
130 |
100%56/56100%0/0100%9/9100%18/18
hwb.ts 144 |
145 |
100%85/8576.47%13/17100%9/9100%35/35
index.ts 159 |
160 |
100%6/6100%0/0100%0/0100%6/6
rgb.ts 174 |
175 |
100%91/9180.76%21/26100%9/9100%41/41
188 |
189 |
190 |
191 | 196 | 197 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/detectors.spec.ts: -------------------------------------------------------------------------------- 1 | import { ColorDetectors } from './detectors'; 2 | 3 | describe('detectors', () => { 4 | const Detectors = ColorDetectors; 5 | 6 | describe('hex', () => { 7 | const Detector = Detectors.hex; 8 | 9 | it('converts Hex to fractions', () => { 10 | expect(Detector.normalize(Detector.re.exec('#abcdef') || [])).toEqual([ 11 | 0.6706, 0.8039, 0.9373, 1, 12 | ]); 13 | }); 14 | 15 | it('converts fractions to Hex', () => { 16 | expect( 17 | Detector.finalize({ r: 0.6706, g: 0.8039, b: 0.9373, a: 1 }), 18 | ).toEqual({ 19 | r: 'ab', 20 | g: 'cd', 21 | b: 'ef', 22 | a: 'ff', 23 | }); 24 | }); 25 | }); 26 | 27 | describe('hex3', () => { 28 | const Detector = Detectors.hex3; 29 | 30 | it('converts Hex3 to fractions', () => { 31 | expect(Detector.normalize(Detector.re.exec('#abc') || [])).toEqual([ 32 | 0.6667, 0.7333, 0.8, 1, 33 | ]); 34 | }); 35 | }); 36 | 37 | describe('hexa', () => { 38 | const Detector = Detectors.hexa; 39 | 40 | it('converts HexA to fractions', () => { 41 | expect(Detector.normalize(Detector.re.exec('#abcdef0a') || [])).toEqual([ 42 | 0.6706, 0.8039, 0.9373, 0.0392, 43 | ]); 44 | }); 45 | }); 46 | 47 | describe('hexa4', () => { 48 | const Detector = Detectors.hexa4; 49 | 50 | it('converts HexA4 to fractions', () => { 51 | expect(Detector.normalize(Detector.re.exec('#abcd') || [])).toEqual([ 52 | 0.6667, 0.7333, 0.8, 0.8667, 53 | ]); 54 | }); 55 | }); 56 | 57 | describe('hsb', () => { 58 | const Detector = Detectors.hsb; 59 | 60 | it('converts HSB to degrees and fractions within range', () => { 61 | expect( 62 | Detector.normalize(Detector.re.exec('hsb(3rad, 80%, 65%)') || []), 63 | ).toEqual([171.89, 0.8, 0.65, 1]); 64 | expect( 65 | Detector.normalize(Detector.re.exec('hsb(-0.25turn, 110%, 10%)') || []), 66 | ).toEqual([270, 1, 0.1, 1]); 67 | }); 68 | 69 | it('recognizes wacky spaces', () => { 70 | expect( 71 | Detector.normalize( 72 | Detector.re.exec('hsb( -0.25turn ,110%, 10% )') || [], 73 | ), 74 | ).toEqual([270, 1, 0.1, 1]); 75 | }); 76 | 77 | it('converts fractions to HSB', () => { 78 | expect(Detector.finalize({ h: 171.89, s: 0.8, b: 0.65 })).toEqual({ 79 | h: 172, 80 | s: 80, 81 | b: 65, 82 | }); 83 | }); 84 | }); 85 | 86 | describe('hsl', () => { 87 | const Detector = Detectors.hsl; 88 | 89 | it('converts HSL to degrees and fractions within range', () => { 90 | expect( 91 | Detector.normalize(Detector.re.exec('hsl(3rad, 80%, 65%)') || []), 92 | ).toEqual([171.89, 0.8, 0.65, 1]); 93 | expect( 94 | Detector.normalize(Detector.re.exec('hsl(-0.25turn, 110%, 10%)') || []), 95 | ).toEqual([270, 1, 0.1, 1]); 96 | }); 97 | 98 | it('recognizes alternate format', () => { 99 | expect( 100 | Detector.normalize(Detector.re.exec('hsl(-0.25turn 110% 10%)') || []), 101 | ).toEqual([270, 1, 0.1, 1]); 102 | }); 103 | 104 | it('recognizes wacky spaces', () => { 105 | expect( 106 | Detector.normalize( 107 | Detector.re.exec('hsl( -0.25turn 110% , 10% )') || [], 108 | ), 109 | ).toEqual([270, 1, 0.1, 1]); 110 | }); 111 | 112 | it('converts fractions to HSLA', () => { 113 | expect(Detector.finalize({ h: 171.89, s: 0.8, l: 0.65, a: 1 })).toEqual({ 114 | h: 172, 115 | s: 80, 116 | l: 65, 117 | a: 1, 118 | }); 119 | }); 120 | }); 121 | 122 | describe('hsla', () => { 123 | const Detector = Detectors.hsla; 124 | 125 | it('converts HSLA to degrees and fractions within range', () => { 126 | expect( 127 | Detector.normalize( 128 | Detector.re.exec('hsla(3rad, 80%, 65%, 105%)') || [], 129 | ), 130 | ).toEqual([171.89, 0.8, 0.65, 1]); 131 | expect( 132 | Detector.normalize( 133 | Detector.re.exec('hsla(-0.25turn, 110%, 10%, 0.5)') || [], 134 | ), 135 | ).toEqual([270, 1, 0.1, 0.5]); 136 | }); 137 | 138 | it('recognizes alternate format', () => { 139 | expect( 140 | Detector.normalize( 141 | Detector.re.exec('hsla(-0.25turn 110% 10% / 105%)') || [], 142 | ), 143 | ).toEqual([270, 1, 0.1, 1]); 144 | }); 145 | 146 | it('recognizes wacky spaces', () => { 147 | expect( 148 | Detector.normalize( 149 | Detector.re.exec('hsla( -0.25turn 110% , 10%/50%)') || [], 150 | ), 151 | ).toEqual([270, 1, 0.1, 0.5]); 152 | }); 153 | 154 | it('converts fractions to HSLA', () => { 155 | expect(Detector.finalize({ h: 171.89, s: 0.8, l: 0.65, a: 1 })).toEqual({ 156 | h: 172, 157 | s: 80, 158 | l: 65, 159 | a: 1, 160 | }); 161 | }); 162 | }); 163 | 164 | describe('hsv', () => { 165 | const Detector = Detectors.hsv; 166 | 167 | it('converts HSV to degrees and fractions within range', () => { 168 | expect( 169 | Detector.normalize(Detector.re.exec('hsv(3rad, 80%, 65%)') || []), 170 | ).toEqual([171.89, 0.8, 0.65, 1]); 171 | expect( 172 | Detector.normalize(Detector.re.exec('hsv(-0.25turn, 110%, 10%)') || []), 173 | ).toEqual([270, 1, 0.1, 1]); 174 | }); 175 | 176 | it('recognizes wacky spaces', () => { 177 | expect( 178 | Detector.normalize( 179 | Detector.re.exec('hsv( -0.25turn ,110%, 10% )') || [], 180 | ), 181 | ).toEqual([270, 1, 0.1, 1]); 182 | }); 183 | 184 | it('converts fractions to HSV', () => { 185 | expect(Detector.finalize({ h: 171.89, s: 0.8, v: 0.65 })).toEqual({ 186 | h: 172, 187 | s: 80, 188 | v: 65, 189 | }); 190 | }); 191 | }); 192 | 193 | describe('hwb', () => { 194 | const Detector = Detectors.hwb; 195 | 196 | describe('without alpha', () => { 197 | it('converts HWB to degrees and fractions within range', () => { 198 | expect( 199 | Detector.normalize(Detector.re.exec('hwb(3rad 80% 65%)') || []), 200 | ).toEqual([171.89, 0.8, 0.65, 1]); 201 | expect( 202 | Detector.normalize(Detector.re.exec('hwb(-0.25turn 110% 10%)') || []), 203 | ).toEqual([270, 1, 0.1, 1]); 204 | }); 205 | 206 | it('recognizes wacky spaces', () => { 207 | expect( 208 | Detector.normalize( 209 | Detector.re.exec('hwb( -0.25turn 110% 10%)') || [], 210 | ), 211 | ).toEqual([270, 1, 0.1, 1]); 212 | }); 213 | }); 214 | 215 | describe('with alpha', () => { 216 | it('converts HWB to degrees and fractions within range', () => { 217 | expect( 218 | Detector.normalize( 219 | Detector.re.exec('hwb(3rad 80% 65% / 105%)') || [], 220 | ), 221 | ).toEqual([171.89, 0.8, 0.65, 1]); 222 | expect( 223 | Detector.normalize( 224 | Detector.re.exec('hwb(-0.25turn 110% 10% / 0.5)') || [], 225 | ), 226 | ).toEqual([270, 1, 0.1, 0.5]); 227 | }); 228 | 229 | it('recognizes wacky spaces', () => { 230 | expect( 231 | Detector.normalize( 232 | Detector.re.exec('hwb( -0.25turn 110% 10%/50%)') || [], 233 | ), 234 | ).toEqual([270, 1, 0.1, 0.5]); 235 | }); 236 | }); 237 | 238 | it('converts fractions to HWB', () => { 239 | expect( 240 | Detector.finalize({ h: 171.89, w: 0.8, b: 0.65, a: 0.91 }), 241 | ).toEqual({ 242 | h: 172, 243 | w: 80, 244 | b: 65, 245 | a: 0.91, 246 | }); 247 | }); 248 | }); 249 | 250 | describe('rgb', () => { 251 | const Detector = Detectors.rgb; 252 | 253 | it('converts RGB to fractions within range', () => { 254 | expect( 255 | Detector.normalize(Detector.re.exec('rgb(100, 200, 45)') || []), 256 | ).toEqual([0.3922, 0.7843, 0.1765, 1]); 257 | expect( 258 | Detector.normalize(Detector.re.exec('rgb(100%, 50%, 70%)') || []), 259 | ).toEqual([1, 0.5, 0.7, 1]); 260 | expect( 261 | Detector.normalize(Detector.re.exec('rgb(300, 200, 123445)') || []), 262 | ).toEqual([1, 0.7843, 1, 1]); 263 | }); 264 | 265 | it('recognizes alternate format', () => { 266 | expect( 267 | Detector.normalize(Detector.re.exec('rgb(100 300 45)') || []), 268 | ).toEqual([0.3922, 1, 0.1765, 1]); 269 | }); 270 | 271 | it('recognizes wacky spaces', () => { 272 | expect( 273 | Detector.normalize(Detector.re.exec('rgb( 100,300 45)') || []), 274 | ).toEqual([0.3922, 1, 0.1765, 1]); 275 | }); 276 | 277 | it('converts fractions to RGBA', () => { 278 | expect( 279 | Detector.finalize({ r: 0.6706, g: 0.8039, b: 0.9373, a: 1 }), 280 | ).toEqual({ 281 | r: 171, 282 | g: 205, 283 | b: 239, 284 | a: 1, 285 | }); 286 | }); 287 | }); 288 | 289 | describe('rgba', () => { 290 | const Detector = Detectors.rgba; 291 | 292 | it('converts RGBA to fractions within range', () => { 293 | expect( 294 | Detector.normalize(Detector.re.exec('rgba(100, 200, 45, 0.75)') || []), 295 | ).toEqual([0.3922, 0.7843, 0.1765, 0.75]); 296 | expect( 297 | Detector.normalize( 298 | Detector.re.exec('rgba(100%, 50%, 70%, 0.75)') || [], 299 | ), 300 | ).toEqual([1, 0.5, 0.7, 0.75]); 301 | expect( 302 | Detector.normalize( 303 | Detector.re.exec('rgba(300, 200, 123445, 1.1)') || [], 304 | ), 305 | ).toEqual([1, 0.7843, 1, 1]); 306 | }); 307 | 308 | it('recognizes alternate format', () => { 309 | expect( 310 | Detector.normalize(Detector.re.exec('rgba(100 300 45 / 75%)') || []), 311 | ).toEqual([0.3922, 1, 0.1765, 0.75]); 312 | }); 313 | 314 | it('recognizes wacky spaces', () => { 315 | expect( 316 | Detector.normalize(Detector.re.exec('rgba( 100,300 45 / 0)') || []), 317 | ).toEqual([0.3922, 1, 0.1765, 0]); 318 | }); 319 | }); 320 | }); 321 | -------------------------------------------------------------------------------- /documentation/variables/detectors.ColorDetectors.html: -------------------------------------------------------------------------------- 1 | ColorDetectors | colorcolor - v3.0.0
2 |
3 | 10 |
11 |
12 |
13 |
14 | 18 |

Variable ColorDetectorsConst

19 |
ColorDetectors: ColorDefinitions = ...
20 |

This is used to detect a specific color model string via regular expression, normalize the values to be within 0..1 21 | for percentages and RGB, and 0..360 for angles.

22 | 23 |

Remarks

Each detector has a list of responsibilities:

24 |
    25 |
  • detect the color model from an input string using a regular expression
  • 26 |
  • normalize the output of the regex to numeric representations (ex. '0.5turn' will become 180; '255' will become 1)
  • 27 |
  • convert the normalized values to the new color model
  • 28 |
  • finalize the converted values to the ones used in the output string (ex. 1 might become 255 or 'ff')
  • 29 |
30 |
33 |
67 |
68 |

Generated using TypeDoc

69 |
-------------------------------------------------------------------------------- /documentation/modules.html: -------------------------------------------------------------------------------- 1 | colorcolor - v3.0.0
2 |
3 | 10 |
11 |
12 |
13 |
14 |

colorcolor - v3.0.0

15 |
16 |
17 |

Index

18 |
19 |

Modules

20 |
31 |
62 |
63 |

Generated using TypeDoc

64 |
-------------------------------------------------------------------------------- /src/colors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of interfaces and types for the various color models, definitions, and 3 | * converter methods. 4 | * 5 | * @module 6 | */ 7 | 8 | /** 9 | * Each supported color model requires a definition object for detection and normalization. 10 | * 11 | * @see {@link detectors.ColorDetectors} 12 | */ 13 | export interface ColorDefinition { 14 | /** 15 | * A list of methods that convert the current color model into another one. Every entry in the {@link ColorName} enum 16 | * must be present for each definition. 17 | */ 18 | converters: { 19 | [key in ColorName]: Converter; 20 | }; 21 | 22 | /** 23 | * Convert the bits from a converter method into the format used in the color string. 24 | * 25 | * @remarks 26 | * Fractions are converted to whole numbers (ex. 0.75 becomes 191 for RGB or 75 for percentage) 27 | * or hexadecimal (ex. 0.75 becomes 'bf') and rounded off. Angles will already be in degrees 28 | * before this method is called. 29 | * 30 | * @example 31 | * // convert RGBA to HEX 32 | * finalize({ r: 0.5, g: 1, b: 0.5, a: 1 }) // return { r: '80', g: 'ff', b: '80', a: 1 } 33 | * 34 | * @param color This is the color model (from {@link ColorDefinition.normalize}) that should be converted to the final format. 35 | */ 36 | finalize(color: ColorModel): ColorModel; 37 | 38 | /** 39 | * Convert the bits from the regex to percentages for percentage and RGB values, and degrees for angles. 40 | * 41 | * @remarks 42 | * Bits from the regex will begin at index 1 because index 0 is the original string. Each bit 43 | * can be in any format supported by the color model, such as an angle ({@link utilities.toDegrees}), a float 44 | * percentage, an actual percentage string, or a number. The normalize method is responsible for 45 | * converting all of those to degrees in the case of an angle, or a float percentage for everything else. If an 46 | * opacity isn't included (index {@link utilities.AlphaIndex} of `bits` is empty), then it is defaulted to `1`. 47 | * 48 | * @example 49 | * // input to {@link ColorDefinition.re} is 'rgb(128, 255, 191)' 50 | * normalize(['rgb(128, 255, 191)', '128', '255', '191']) // return [0.5, 1, 0.75, 1] 51 | * 52 | * @param bits Index 0 is the original string, so it should be ignored by the normalize method. 53 | */ 54 | normalize(bits: string[]): ConverterTuple; 55 | 56 | /** 57 | * This should be able to detect a CSS string for a specific color model. 58 | * 59 | * @remarks 60 | * Many color strings support multiple formats. For example, RGBA can be written as `rgba(191, 205, 45, 0.7)`, 61 | * `rgba(75%, 40%, 100%, 50%)`, `rgba(191 205 45 / 0.7)`, and `rgba(75% 40% 100% / 50%)` (and a few others). 62 | * Non-CSS color strings like `hsb` and `hsv` simply use commas and have no alternative formats. The regex should 63 | * also not care about extra spaces, so `hsv(75deg, 75% 65%)` and `hsv( 75deg ,75%, 65% )` have identical 64 | * results. 65 | * 66 | * @see [CSS named colors](https://developer.mozilla.org/en-US/docs/Web/CSS/named-color#value) 67 | * @see [RGB color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model) 68 | * @see [HSL color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl_color_model) 69 | * @see [HWB color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hwb_color_model) 70 | */ 71 | re: RegExp; 72 | } 73 | 74 | /** 75 | * This is the shape of the color detection object. Each supported _input_ color string needs a definition that 76 | * contains some necessary functionality, including string detection, number normalizing, and converting. 77 | * 78 | * @see {@link detectors.ColorDetectors} 79 | */ 80 | export type ColorDefinitions = { 81 | [key in ColorName | ColorDefinitionName]: ColorDefinition; 82 | }; 83 | 84 | /** 85 | * This is a combination of the target color names and internal color names. 86 | */ 87 | export type ColorDefinitionType = `${ColorDefinitionName | ColorName}`; 88 | 89 | /** 90 | * These are the available color formats that can be converted to. 91 | */ 92 | export enum ColorName { 93 | HEX = 'hex', 94 | HEXA = 'hexa', 95 | HSB = 'hsb', 96 | HSL = 'hsl', 97 | HSLA = 'hsla', 98 | HSV = 'hsv', 99 | HWB = 'hwb', 100 | RGB = 'rgb', 101 | RGBA = 'rgba', 102 | } 103 | 104 | /** 105 | * These are the additional color formats supported as an _input_ string. 106 | */ 107 | export enum ColorDefinitionName { 108 | HEX3 = 'hex3', 109 | HEXA4 = 'hexa4', 110 | } 111 | 112 | /** 113 | * This is the {@link ColorName} enum converted to a type. 114 | */ 115 | export type ColorType = `${ColorName}`; 116 | 117 | /** 118 | * This reflects the output shape of the color {@link colors.ColorDefinition.normalize} method. 119 | */ 120 | export type ConverterTuple = [number | string, number, number, number]; 121 | 122 | export type HexConverter = ([r, g, b]: [number, number, number]) => ColorModel; 123 | export type HexaConverter = ([r, g, b, a]: [ 124 | number, 125 | number, 126 | number, 127 | number?, 128 | ]) => ColorModel; 129 | export type HsbConverter = ([h, s, b]: [string, number, number]) => ColorModel; 130 | export type HslConverter = ([h, s, l]: [string, number, number]) => ColorModel; 131 | export type HslaConverter = ([h, s, l, a]: [ 132 | string, 133 | number, 134 | number, 135 | number?, 136 | ]) => ColorModel; 137 | export type HsvConverter = ([h, s, v]: [string, number, number]) => ColorModel; 138 | export type HwbConverter = ([h, w, b]: [string, number, number]) => ColorModel; 139 | export type HwbaConverter = ([h, w, b, a]: [ 140 | string, 141 | number, 142 | number, 143 | number?, 144 | ]) => ColorModel; 145 | export type RgbConverter = ([r, g, b]: [number, number, number]) => ColorModel; 146 | export type RgbaConverter = ([r, g, b, a]: [ 147 | number, 148 | number, 149 | number, 150 | number?, 151 | ]) => ColorModel; 152 | 153 | /** 154 | * The output of a {@link colors.ColorDefinition.converters color converter} method must be ones of these types. 155 | */ 156 | export type Converter = 157 | | HexConverter 158 | | HexaConverter 159 | | HsbConverter 160 | | HslConverter 161 | | HslaConverter 162 | | HsvConverter 163 | | HwbConverter 164 | | HwbaConverter 165 | | RgbConverter 166 | | RgbaConverter; 167 | 168 | /** 169 | * The RGB color model defines a given color in the sRGB color space according to its red, green, and blue components. 170 | * An optional alpha component represents the color's transparency. 171 | * 172 | * @see [RGB Color Model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model) 173 | */ 174 | export type HEXA = { 175 | /** 176 | * Red is a hexadecimal number from 0x00 to 0xff. 177 | */ 178 | r: string; 179 | 180 | /** 181 | * Red is a hexadecimal number from 0x00 to 0xff. 182 | */ 183 | g: string; 184 | 185 | /** 186 | * Red is a hexadecimal number from 0x00 to 0xff. 187 | */ 188 | b: string; 189 | 190 | /** 191 | * Red is a hexadecimal number from 0x00 to 0xff. 192 | */ 193 | a: string; 194 | }; 195 | 196 | /** 197 | * The HSB color model defines a given color in the sRGB color space according to its hue, saturation, and brightness 198 | * (also known as Value in the HSV color model). 199 | * 200 | * @see [What is HSV in Design](https://www.lifewire.com/what-is-hsv-in-design-1078068) 201 | */ 202 | export type HSB = { 203 | /** 204 | * Hue is a number between 0 and 360 degrees. 205 | */ 206 | h: number; 207 | 208 | /** 209 | * Saturation is a percentage between 0 and 1. 210 | */ 211 | s: number; 212 | 213 | /** 214 | * Brightness (or Value) is a percentage between 0 and 1. 215 | */ 216 | b: number; 217 | }; 218 | 219 | /** 220 | * The HSL color model defines a given color in the sRGB color space according to its hue, saturation, and lightness. An 221 | * optional alpha component represents the color's transparency. 222 | * 223 | * @see [HSL Color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl_color_model) 224 | */ 225 | export type HSLA = { 226 | /** 227 | * Hue is a number between 0 and 360 degrees. 228 | */ 229 | h: number; 230 | 231 | /** 232 | * Saturation is a percentage between 0 and 1. 233 | */ 234 | s: number; 235 | 236 | /** 237 | * Lightness is a percentage between 0 and 1. 238 | */ 239 | l: number; 240 | 241 | /** 242 | * Opacity is a percentage between 0 and 1. 243 | */ 244 | a: number; 245 | }; 246 | 247 | /** 248 | * The HSV color model defines a given color in the sRGB color space according to its hue, saturation, and value (also 249 | * known as Brightness in the HSB color model). 250 | * 251 | * @see [What is HSV in Design](https://www.lifewire.com/what-is-hsv-in-design-1078068) 252 | */ 253 | export type HSV = { 254 | /** 255 | * Hue is a number between 0 and 360 degrees. 256 | */ 257 | h: number; 258 | 259 | /** 260 | * Saturation is a percentage between 0 and 1. 261 | */ 262 | s: number; 263 | 264 | /** 265 | * Value (or Brightness) is a percentage between 0 and 1. 266 | */ 267 | v: number; 268 | }; 269 | 270 | /** 271 | * The HWB color model defines a given color in the sRGB color space according to its hue, whiteness, and blackness. An 272 | * optional alpha component represents the color's transparency. 273 | * 274 | * @see [HWB Color Model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hwb_color_model) 275 | */ 276 | export type HWB = { 277 | /** 278 | * Hue is a number between 0 and 360 degrees. 279 | */ 280 | h: number; 281 | 282 | /** 283 | * Whiteness is a percentage between 0 and 1. 284 | */ 285 | w: number; 286 | 287 | /** 288 | * Blackness is a percentage between 0 and 1. 289 | */ 290 | b: number; 291 | 292 | /** 293 | * Opacity is a percentage between 0 and 1. 294 | */ 295 | a: number; 296 | }; 297 | 298 | /** 299 | * The RGB color model defines a given color in the sRGB color space according to its red, green, and blue components. 300 | * An optional alpha component represents the color's transparency. 301 | * 302 | * @see [RGB Color Model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model) 303 | */ 304 | export type RGBA = { 305 | /** 306 | * Red is a percentage between 0 and 1. 307 | */ 308 | r: number; 309 | 310 | /** 311 | * Green is a percentage between 0 and 1. 312 | */ 313 | g: number; 314 | 315 | /** 316 | * Blue is a percentage between 0 and 1. 317 | */ 318 | b: number; 319 | 320 | /** 321 | * Opacity is a percentage between 0 and 1. 322 | */ 323 | a: number; 324 | }; 325 | 326 | /** 327 | * All color methods work with one of these models. HEX, HSL, and RGB aren't used since alpha always {@link utilities.DefaultOpacity defaults} to 1. 328 | */ 329 | export type ColorModel = HEXA | HSB | HSLA | HSV | HWB | RGBA; 330 | -------------------------------------------------------------------------------- /dist/colors.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a collection of interfaces and types for the various color models, definitions, and 3 | * converter methods. 4 | * 5 | * @module 6 | */ 7 | /** 8 | * Each supported color model requires a definition object for detection and normalization. 9 | * 10 | * @see {@link detectors.ColorDetectors} 11 | */ 12 | export interface ColorDefinition { 13 | /** 14 | * A list of methods that convert the current color model into another one. Every entry in the {@link ColorName} enum 15 | * must be present for each definition. 16 | */ 17 | converters: { 18 | [key in ColorName]: Converter; 19 | }; 20 | /** 21 | * Convert the bits from a converter method into the format used in the color string. 22 | * 23 | * @remarks 24 | * Fractions are converted to whole numbers (ex. 0.75 becomes 191 for RGB or 75 for percentage) 25 | * or hexadecimal (ex. 0.75 becomes 'bf') and rounded off. Angles will already be in degrees 26 | * before this method is called. 27 | * 28 | * @example 29 | * // convert RGBA to HEX 30 | * finalize({ r: 0.5, g: 1, b: 0.5, a: 1 }) // return { r: '80', g: 'ff', b: '80', a: 1 } 31 | * 32 | * @param color This is the color model (from {@link ColorDefinition.normalize}) that should be converted to the final format. 33 | */ 34 | finalize(color: ColorModel): ColorModel; 35 | /** 36 | * Convert the bits from the regex to percentages for percentage and RGB values, and degrees for angles. 37 | * 38 | * @remarks 39 | * Bits from the regex will begin at index 1 because index 0 is the original string. Each bit 40 | * can be in any format supported by the color model, such as an angle ({@link utilities.toDegrees}), a float 41 | * percentage, an actual percentage string, or a number. The normalize method is responsible for 42 | * converting all of those to degrees in the case of an angle, or a float percentage for everything else. If an 43 | * opacity isn't included (index {@link utilities.AlphaIndex} of `bits` is empty), then it is defaulted to `1`. 44 | * 45 | * @example 46 | * // input to {@link ColorDefinition.re} is 'rgb(128, 255, 191)' 47 | * normalize(['rgb(128, 255, 191)', '128', '255', '191']) // return [0.5, 1, 0.75, 1] 48 | * 49 | * @param bits Index 0 is the original string, so it should be ignored by the normalize method. 50 | */ 51 | normalize(bits: string[]): ConverterTuple; 52 | /** 53 | * This should be able to detect a CSS string for a specific color model. 54 | * 55 | * @remarks 56 | * Many color strings support multiple formats. For example, RGBA can be written as `rgba(191, 205, 45, 0.7)`, 57 | * `rgba(75%, 40%, 100%, 50%)`, `rgba(191 205 45 / 0.7)`, and `rgba(75% 40% 100% / 50%)` (and a few others). 58 | * Non-CSS color strings like `hsb` and `hsv` simply use commas and have no alternative formats. The regex should 59 | * also not care about extra spaces, so `hsv(75deg, 75% 65%)` and `hsv( 75deg ,75%, 65% )` have identical 60 | * results. 61 | * 62 | * @see [CSS named colors](https://developer.mozilla.org/en-US/docs/Web/CSS/named-color#value) 63 | * @see [RGB color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model) 64 | * @see [HSL color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl_color_model) 65 | * @see [HWB color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hwb_color_model) 66 | */ 67 | re: RegExp; 68 | } 69 | /** 70 | * This is the shape of the color detection object. Each supported _input_ color string needs a definition that 71 | * contains some necessary functionality, including string detection, number normalizing, and converting. 72 | * 73 | * @see {@link detectors.ColorDetectors} 74 | */ 75 | export declare type ColorDefinitions = { 76 | [key in ColorName | ColorDefinitionName]: ColorDefinition; 77 | }; 78 | /** 79 | * This is a combination of the target color names and internal color names. 80 | */ 81 | export declare type ColorDefinitionType = `${ColorDefinitionName | ColorName}`; 82 | /** 83 | * These are the available color formats that can be converted to. 84 | */ 85 | export declare enum ColorName { 86 | HEX = "hex", 87 | HEXA = "hexa", 88 | HSB = "hsb", 89 | HSL = "hsl", 90 | HSLA = "hsla", 91 | HSV = "hsv", 92 | HWB = "hwb", 93 | RGB = "rgb", 94 | RGBA = "rgba" 95 | } 96 | /** 97 | * These are the additional color formats supported as an _input_ string. 98 | */ 99 | export declare enum ColorDefinitionName { 100 | HEX3 = "hex3", 101 | HEXA4 = "hexa4" 102 | } 103 | /** 104 | * This is the {@link ColorName} enum converted to a type. 105 | */ 106 | export declare type ColorType = `${ColorName}`; 107 | /** 108 | * This reflects the output shape of the color {@link colors.ColorDefinition.normalize} method. 109 | */ 110 | export declare type ConverterTuple = [number | string, number, number, number]; 111 | export declare type HexConverter = ([r, g, b]: [number, number, number]) => ColorModel; 112 | export declare type HexaConverter = ([r, g, b, a]: [ 113 | number, 114 | number, 115 | number, 116 | number? 117 | ]) => ColorModel; 118 | export declare type HsbConverter = ([h, s, b]: [string, number, number]) => ColorModel; 119 | export declare type HslConverter = ([h, s, l]: [string, number, number]) => ColorModel; 120 | export declare type HslaConverter = ([h, s, l, a]: [ 121 | string, 122 | number, 123 | number, 124 | number? 125 | ]) => ColorModel; 126 | export declare type HsvConverter = ([h, s, v]: [string, number, number]) => ColorModel; 127 | export declare type HwbConverter = ([h, w, b]: [string, number, number]) => ColorModel; 128 | export declare type HwbaConverter = ([h, w, b, a]: [ 129 | string, 130 | number, 131 | number, 132 | number? 133 | ]) => ColorModel; 134 | export declare type RgbConverter = ([r, g, b]: [number, number, number]) => ColorModel; 135 | export declare type RgbaConverter = ([r, g, b, a]: [ 136 | number, 137 | number, 138 | number, 139 | number? 140 | ]) => ColorModel; 141 | /** 142 | * The output of a {@link colors.ColorDefinition.converters color converter} method must be ones of these types. 143 | */ 144 | export declare type Converter = HexConverter | HexaConverter | HsbConverter | HslConverter | HslaConverter | HsvConverter | HwbConverter | HwbaConverter | RgbConverter | RgbaConverter; 145 | /** 146 | * The RGB color model defines a given color in the sRGB color space according to its red, green, and blue components. 147 | * An optional alpha component represents the color's transparency. 148 | * 149 | * @see [RGB Color Model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model) 150 | */ 151 | export declare type HEXA = { 152 | /** 153 | * Red is a hexadecimal number from 0x00 to 0xff. 154 | */ 155 | r: string; 156 | /** 157 | * Red is a hexadecimal number from 0x00 to 0xff. 158 | */ 159 | g: string; 160 | /** 161 | * Red is a hexadecimal number from 0x00 to 0xff. 162 | */ 163 | b: string; 164 | /** 165 | * Red is a hexadecimal number from 0x00 to 0xff. 166 | */ 167 | a: string; 168 | }; 169 | /** 170 | * The HSB color model defines a given color in the sRGB color space according to its hue, saturation, and brightness 171 | * (also known as Value in the HSV color model). 172 | * 173 | * @see [What is HSV in Design](https://www.lifewire.com/what-is-hsv-in-design-1078068) 174 | */ 175 | export declare type HSB = { 176 | /** 177 | * Hue is a number between 0 and 360 degrees. 178 | */ 179 | h: number; 180 | /** 181 | * Saturation is a percentage between 0 and 1. 182 | */ 183 | s: number; 184 | /** 185 | * Brightness (or Value) is a percentage between 0 and 1. 186 | */ 187 | b: number; 188 | }; 189 | /** 190 | * The HSL color model defines a given color in the sRGB color space according to its hue, saturation, and lightness. An 191 | * optional alpha component represents the color's transparency. 192 | * 193 | * @see [HSL Color model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl_color_model) 194 | */ 195 | export declare type HSLA = { 196 | /** 197 | * Hue is a number between 0 and 360 degrees. 198 | */ 199 | h: number; 200 | /** 201 | * Saturation is a percentage between 0 and 1. 202 | */ 203 | s: number; 204 | /** 205 | * Lightness is a percentage between 0 and 1. 206 | */ 207 | l: number; 208 | /** 209 | * Opacity is a percentage between 0 and 1. 210 | */ 211 | a: number; 212 | }; 213 | /** 214 | * The HSV color model defines a given color in the sRGB color space according to its hue, saturation, and value (also 215 | * known as Brightness in the HSB color model). 216 | * 217 | * @see [What is HSV in Design](https://www.lifewire.com/what-is-hsv-in-design-1078068) 218 | */ 219 | export declare type HSV = { 220 | /** 221 | * Hue is a number between 0 and 360 degrees. 222 | */ 223 | h: number; 224 | /** 225 | * Saturation is a percentage between 0 and 1. 226 | */ 227 | s: number; 228 | /** 229 | * Value (or Brightness) is a percentage between 0 and 1. 230 | */ 231 | v: number; 232 | }; 233 | /** 234 | * The HWB color model defines a given color in the sRGB color space according to its hue, whiteness, and blackness. An 235 | * optional alpha component represents the color's transparency. 236 | * 237 | * @see [HWB Color Model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hwb_color_model) 238 | */ 239 | export declare type HWB = { 240 | /** 241 | * Hue is a number between 0 and 360 degrees. 242 | */ 243 | h: number; 244 | /** 245 | * Whiteness is a percentage between 0 and 1. 246 | */ 247 | w: number; 248 | /** 249 | * Blackness is a percentage between 0 and 1. 250 | */ 251 | b: number; 252 | /** 253 | * Opacity is a percentage between 0 and 1. 254 | */ 255 | a: number; 256 | }; 257 | /** 258 | * The RGB color model defines a given color in the sRGB color space according to its red, green, and blue components. 259 | * An optional alpha component represents the color's transparency. 260 | * 261 | * @see [RGB Color Model](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_color_model) 262 | */ 263 | export declare type RGBA = { 264 | /** 265 | * Red is a percentage between 0 and 1. 266 | */ 267 | r: number; 268 | /** 269 | * Green is a percentage between 0 and 1. 270 | */ 271 | g: number; 272 | /** 273 | * Blue is a percentage between 0 and 1. 274 | */ 275 | b: number; 276 | /** 277 | * Opacity is a percentage between 0 and 1. 278 | */ 279 | a: number; 280 | }; 281 | /** 282 | * All color methods work with one of these models. HEX, HSL, and RGB aren't used since alpha always {@link utilities.DefaultOpacity defaults} to 1. 283 | */ 284 | export declare type ColorModel = HEXA | HSB | HSLA | HSV | HWB | RGBA; 285 | -------------------------------------------------------------------------------- /src/colorcolor.spec.ts: -------------------------------------------------------------------------------- 1 | import { colorcolor } from './colorcolor'; 2 | 3 | describe('color2color', () => { 4 | describe('colorcolor', () => { 5 | it('converts everything to an RGBA string by default', () => { 6 | expect(colorcolor('chartreuse')).toEqual('rgba(127, 255, 0, 1)'); 7 | expect(colorcolor('#abc')).toEqual('rgba(170, 187, 204, 1)'); 8 | expect(colorcolor('#abce')).toEqual('rgba(170, 187, 204, 0.93)'); 9 | expect(colorcolor('#abcdef')).toEqual('rgba(171, 205, 239, 1)'); 10 | expect(colorcolor('#abcdefa0')).toEqual('rgba(171, 205, 239, 0.63)'); 11 | expect(colorcolor('hsb(109, 100%, 74%)')).toEqual('rgba(35, 189, 0, 1)'); 12 | expect(colorcolor('hsl(109, 100%, 37%)')).toEqual('rgba(35, 189, 0, 1)'); 13 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)')).toEqual('rgba(35, 189, 0, 0.7)'); 14 | expect(colorcolor('hsv(109, 100%, 74%)')).toEqual('rgba(35, 189, 0, 1)'); 15 | expect(colorcolor('hwb(180 50% 25%)')).toEqual('rgba(128, 191, 191, 1)'); 16 | expect(colorcolor('hwb(180 50% 25% / 0.7)')).toEqual('rgba(128, 191, 191, 0.7)'); 17 | expect(colorcolor('rgb(0% 75% 100%)')).toEqual('rgba(0, 191, 255, 1)'); 18 | expect(colorcolor('rgba(0% 75% 100% 0.5)')).toEqual('rgba(0, 191, 255, 0.5)'); 19 | }); 20 | 21 | it('converts everything to a HEX string', () => { 22 | expect(colorcolor('chartreuse', 'hex')).toEqual('#7fff00'); 23 | expect(colorcolor('#abc', 'hex')).toEqual('#aabbcc'); 24 | expect(colorcolor('#abce', 'hex')).toEqual('#aabbcc'); 25 | expect(colorcolor('#abcdef', 'hex')).toEqual('#abcdef'); 26 | expect(colorcolor('#abcdefa0', 'hex')).toEqual('#abcdef'); 27 | expect(colorcolor('hsb(109, 100%, 74%)', 'hex')).toEqual('#23bd00'); 28 | expect(colorcolor('hsl(109, 100%, 37%)', 'hex')).toEqual('#23bd00'); 29 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hex')).toEqual('#23bd00'); 30 | expect(colorcolor('hsv(109, 100%, 74%)', 'hex')).toEqual('#23bd00'); 31 | expect(colorcolor('hwb(180 50% 25%)', 'hex')).toEqual('#80bfbf'); 32 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hex')).toEqual('#80bfbf'); 33 | expect(colorcolor('rgb(0% 75% 100%)', 'hex')).toEqual('#00bfff'); 34 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hex')).toEqual('#00bfff'); 35 | }); 36 | 37 | it('converts everything to a HEXA string', () => { 38 | expect(colorcolor('chartreuse', 'hexa')).toEqual('#7fff00ff'); 39 | expect(colorcolor('#abc', 'hexa')).toEqual('#aabbccff'); 40 | expect(colorcolor('#abce', 'hexa')).toEqual('#aabbccee'); 41 | expect(colorcolor('#abcdef', 'hexa')).toEqual('#abcdefff'); 42 | expect(colorcolor('#abcdefa0', 'hexa')).toEqual('#abcdefa0'); 43 | expect(colorcolor('hsb(109, 100%, 74%)', 'hexa')).toEqual('#23bd00ff'); 44 | expect(colorcolor('hsl(109, 100%, 37%)', 'hexa')).toEqual('#23bd00ff'); 45 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hexa')).toEqual('#23bd00b3'); 46 | expect(colorcolor('hsv(109, 100%, 74%)', 'hexa')).toEqual('#23bd00ff'); 47 | expect(colorcolor('hwb(180 50% 25%)', 'hexa')).toEqual('#80bfbfff'); 48 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hexa')).toEqual('#80bfbfb3'); 49 | expect(colorcolor('rgb(0% 75% 100%)', 'hexa')).toEqual('#00bfffff'); 50 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hexa')).toEqual('#00bfff80'); 51 | }); 52 | 53 | it('converts everything to an HSB string', () => { 54 | expect(colorcolor('chartreuse', 'hsb')).toEqual('hsb(90, 100%, 100%)'); 55 | expect(colorcolor('#abc', 'hsb')).toEqual('hsb(210, 17%, 80%)'); 56 | expect(colorcolor('#abce', 'hsb')).toEqual('hsb(210, 17%, 80%)'); 57 | expect(colorcolor('#abcdef', 'hsb')).toEqual('hsb(210, 28%, 94%)'); 58 | expect(colorcolor('#abcdefa0', 'hsb')).toEqual('hsb(210, 28%, 94%)'); 59 | expect(colorcolor('hsb(109, 100%, 74%)', 'hsb')).toEqual('hsb(109, 100%, 74%)'); 60 | expect(colorcolor('hsl(109, 100%, 37%)', 'hsb')).toEqual('hsb(109, 100%, 74%)'); 61 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hsb')).toEqual('hsb(109, 100%, 74%)'); 62 | expect(colorcolor('hsv(109, 100%, 74%)', 'hsb')).toEqual('hsb(109, 100%, 74%)'); 63 | expect(colorcolor('hwb(180 50% 25%)', 'hsb')).toEqual('hsb(180, 33%, 75%)'); 64 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hsb')).toEqual('hsb(180, 33%, 75%)'); 65 | expect(colorcolor('rgb(0% 75% 100%)', 'hsb')).toEqual('hsb(195, 100%, 100%)'); 66 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hsb')).toEqual('hsb(195, 100%, 100%)'); 67 | }); 68 | 69 | it('converts everything to an HSL string', () => { 70 | expect(colorcolor('chartreuse', 'hsl')).toEqual('hsl(90, 100%, 50%)'); 71 | expect(colorcolor('#abc', 'hsl')).toEqual('hsl(210, 25%, 73%)'); 72 | expect(colorcolor('#abce', 'hsl')).toEqual('hsl(210, 25%, 73%)'); 73 | expect(colorcolor('#abcdef', 'hsl')).toEqual('hsl(210, 68%, 80%)'); 74 | expect(colorcolor('#abcdefa0', 'hsl')).toEqual('hsl(210, 68%, 80%)'); 75 | expect(colorcolor('hsb(109, 100%, 74%)', 'hsl')).toEqual('hsl(109, 100%, 37%)'); 76 | expect(colorcolor('hsl(109, 100%, 37%)', 'hsl')).toEqual('hsl(109, 100%, 37%)'); 77 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hsl')).toEqual('hsl(109, 100%, 37%)'); 78 | expect(colorcolor('hsv(109, 100%, 74%)', 'hsl')).toEqual('hsl(109, 100%, 37%)'); 79 | expect(colorcolor('hwb(180 50% 25%)', 'hsl')).toEqual('hsl(180, 33%, 63%)'); 80 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hsl')).toEqual('hsl(180, 33%, 63%)'); 81 | expect(colorcolor('rgb(0% 75% 100%)', 'hsl')).toEqual('hsl(195, 100%, 50%)'); 82 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hsl')).toEqual('hsl(195, 100%, 50%)'); 83 | }); 84 | 85 | it('converts everything to an HSLA string', () => { 86 | expect(colorcolor('chartreuse', 'hsla')).toEqual('hsla(90, 100%, 50%, 1)'); 87 | expect(colorcolor('#abc', 'hsla')).toEqual('hsla(210, 25%, 73%, 1)'); 88 | expect(colorcolor('#abce', 'hsla')).toEqual('hsla(210, 25%, 73%, 0.93)'); 89 | expect(colorcolor('#abcdef', 'hsla')).toEqual('hsla(210, 68%, 80%, 1)'); 90 | expect(colorcolor('#abcdefa0', 'hsla')).toEqual('hsla(210, 68%, 80%, 0.63)'); 91 | expect(colorcolor('hsb(109, 100%, 74%)', 'hsla')).toEqual('hsla(109, 100%, 37%, 1)'); 92 | expect(colorcolor('hsl(109, 100%, 37%)', 'hsla')).toEqual('hsla(109, 100%, 37%, 1)'); 93 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hsla')).toEqual('hsla(109, 100%, 37%, 0.7)'); 94 | expect(colorcolor('hsv(109, 100%, 74%)', 'hsla')).toEqual('hsla(109, 100%, 37%, 1)'); 95 | expect(colorcolor('hwb(180 50% 25%)', 'hsla')).toEqual('hsla(180, 33%, 63%, 1)'); 96 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hsla')).toEqual('hsla(180, 33%, 63%, 0.7)'); 97 | expect(colorcolor('rgb(0% 75% 100%)', 'hsla')).toEqual('hsla(195, 100%, 50%, 1)'); 98 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hsla')).toEqual('hsla(195, 100%, 50%, 0.5)'); 99 | }); 100 | 101 | it('converts everything to an HSV string', () => { 102 | expect(colorcolor('chartreuse', 'hsv')).toEqual('hsv(90, 100%, 100%)'); 103 | expect(colorcolor('#abc', 'hsv')).toEqual('hsv(210, 17%, 80%)'); 104 | expect(colorcolor('#abce', 'hsv')).toEqual('hsv(210, 17%, 80%)'); 105 | expect(colorcolor('#abcdef', 'hsv')).toEqual('hsv(210, 28%, 94%)'); 106 | expect(colorcolor('#abcdefa0', 'hsv')).toEqual('hsv(210, 28%, 94%)'); 107 | expect(colorcolor('hsb(109, 100%, 74%)', 'hsv')).toEqual('hsv(109, 100%, 74%)'); 108 | expect(colorcolor('hsl(109, 100%, 37%)', 'hsv')).toEqual('hsv(109, 100%, 74%)'); 109 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hsv')).toEqual('hsv(109, 100%, 74%)'); 110 | expect(colorcolor('hsv(109, 100%, 74%)', 'hsv')).toEqual('hsv(109, 100%, 74%)'); 111 | expect(colorcolor('hwb(180 50% 25%)', 'hsv')).toEqual('hsv(180, 33%, 75%)'); 112 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hsv')).toEqual('hsv(180, 33%, 75%)'); 113 | expect(colorcolor('rgb(0% 75% 100%)', 'hsv')).toEqual('hsv(195, 100%, 100%)'); 114 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hsv')).toEqual('hsv(195, 100%, 100%)'); 115 | }); 116 | 117 | it('converts everything to an HWB string', () => { 118 | expect(colorcolor('chartreuse', 'hwb')).toEqual('hwb(90 0% 0% / 1)'); 119 | expect(colorcolor('#abc', 'hwb')).toEqual('hwb(210 67% 20% / 1)'); 120 | expect(colorcolor('#abce', 'hwb')).toEqual('hwb(210 67% 20% / 0.93)'); 121 | expect(colorcolor('#abcdef', 'hwb')).toEqual('hwb(210 67% 6% / 1)'); 122 | expect(colorcolor('#abcdefa0', 'hwb')).toEqual('hwb(210 67% 6% / 0.63)'); 123 | expect(colorcolor('hsb(109, 100%, 74%)', 'hwb')).toEqual('hwb(109 0% 26% / 1)'); 124 | expect(colorcolor('hsl(109, 100%, 37%)', 'hwb')).toEqual('hwb(109 0% 26% / 1)'); 125 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'hwb')).toEqual('hwb(109 0% 26% / 0.7)'); 126 | expect(colorcolor('hsv(109, 100%, 74%)', 'hwb')).toEqual('hwb(109 0% 26% / 1)'); 127 | expect(colorcolor('hwb(180 50% 25%)', 'hwb')).toEqual('hwb(180 50% 25% / 1)'); 128 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'hwb')).toEqual('hwb(180 50% 25% / 0.7)'); 129 | expect(colorcolor('rgb(0% 75% 100%)', 'hwb')).toEqual('hwb(195 0% 0% / 1)'); 130 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'hwb')).toEqual('hwb(195 0% 0% / 0.5)'); 131 | }); 132 | 133 | it('converts everything to an RGB string', () => { 134 | expect(colorcolor('chartreuse', 'rgb')).toEqual('rgb(127, 255, 0)'); 135 | expect(colorcolor('#abc', 'rgb')).toEqual('rgb(170, 187, 204)'); 136 | expect(colorcolor('#abce', 'rgb')).toEqual('rgb(170, 187, 204)'); 137 | expect(colorcolor('#abcdef', 'rgb')).toEqual('rgb(171, 205, 239)'); 138 | expect(colorcolor('#abcdefa0', 'rgb')).toEqual('rgb(171, 205, 239)'); 139 | expect(colorcolor('hsb(109, 100%, 74%)', 'rgb')).toEqual('rgb(35, 189, 0)'); 140 | expect(colorcolor('hsl(109, 100%, 37%)', 'rgb')).toEqual('rgb(35, 189, 0)'); 141 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'rgb')).toEqual('rgb(35, 189, 0)'); 142 | expect(colorcolor('hsv(109, 100%, 74%)', 'rgb')).toEqual('rgb(35, 189, 0)'); 143 | expect(colorcolor('hwb(180 50% 25%)', 'rgb')).toEqual('rgb(128, 191, 191)'); 144 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'rgb')).toEqual('rgb(128, 191, 191)'); 145 | expect(colorcolor('rgb(0% 75% 100%)', 'rgb')).toEqual('rgb(0, 191, 255)'); 146 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'rgb')).toEqual('rgb(0, 191, 255)'); 147 | }); 148 | 149 | it('converts everything to an RGBA string', () => { 150 | expect(colorcolor('chartreuse', 'rgba')).toEqual('rgba(127, 255, 0, 1)'); 151 | expect(colorcolor('#abc', 'rgba')).toEqual('rgba(170, 187, 204, 1)'); 152 | expect(colorcolor('#abce', 'rgba')).toEqual('rgba(170, 187, 204, 0.93)'); 153 | expect(colorcolor('#abcdef', 'rgba')).toEqual('rgba(171, 205, 239, 1)'); 154 | expect(colorcolor('#abcdefa0', 'rgba')).toEqual('rgba(171, 205, 239, 0.63)'); 155 | expect(colorcolor('hsb(109, 100%, 74%)', 'rgba')).toEqual('rgba(35, 189, 0, 1)'); 156 | expect(colorcolor('hsl(109, 100%, 37%)', 'rgba')).toEqual('rgba(35, 189, 0, 1)'); 157 | expect(colorcolor('hsla(109, 100%, 37%, 0.7)', 'rgba')).toEqual('rgba(35, 189, 0, 0.7)'); 158 | expect(colorcolor('hsv(109, 100%, 74%)', 'rgba')).toEqual('rgba(35, 189, 0, 1)'); 159 | expect(colorcolor('hwb(180 50% 25%)', 'rgba')).toEqual('rgba(128, 191, 191, 1)'); 160 | expect(colorcolor('hwb(180 50% 25% / 0.7)', 'rgba')).toEqual('rgba(128, 191, 191, 0.7)'); 161 | expect(colorcolor('rgb(0% 75% 100%)', 'rgba')).toEqual('rgba(0, 191, 255, 1)'); 162 | expect(colorcolor('rgba(0% 75% 100% 0.5)', 'rgba')).toEqual('rgba(0, 191, 255, 0.5)'); 163 | }); 164 | }); 165 | }); 166 | --------------------------------------------------------------------------------