diff --git a/.eslintignore b/.eslintignore index d623911b..ee037a17 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ -node_modules -dist *.js -*.cjs \ No newline at end of file +*.cjs + diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 330586f8..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable prettier/prettier */ - { - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "prettier"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "prettier" - ], - "rules": { - "no-console": 1, - "no-var":0, - "no-explicit-any":0, - "prettier/prettier": 0, - "@typescript-eslint/ban-ts-comment":0, - "no-useless-escape":0 - } -} diff --git a/.gitignore b/.gitignore index c1dc76cd..198b666b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,18 +5,21 @@ /.pnp .pnp.js +lodash-main +lessons-learnt-from-my-first-open-source-project.md +emoji.md # testing - +/temp /color-thief /coverage /experimental # next.js /.next/ /out/ - +source/lib # production /build -/dist +lib # misc .DS_Store @@ -40,5 +43,9 @@ debug.log next-env.d.ts /_site + + app.js -*.code-worspace \ No newline at end of file +*.code-worspace +/leonardo-main +source/lib diff --git a/.husky/pre-commit b/.husky/pre-commit index 151b2508..eadaa672 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -3,3 +3,4 @@ + diff --git a/.husky/pre-push b/.husky/pre-push index 728a507d..f43af809 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -2,4 +2,4 @@ . "$(dirname -- "$0")/_/husky.sh" # We cant push code that doesn't build. - npm run lint && npm run format +# npm run format diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000..8a78e707 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,6 @@ +{ + "MD013": false, + "MD001": false, + "MD0026": true, + "MD0009": false +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 600e365e..00000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -**/node_modules \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 8167e85f..00000000 --- a/.prettierrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "semi": true, - "singleQuote": true, - "printWidth": 80, - "tabWidth": 2, - "useTabs": false, - "trailingComma": "none", - "bracketSpacing": true -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..e409b1ac --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "github.vscode-pull-request-github", + "github.remotehub", + "ms-vscode.remote-repositories", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint" + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a74edcb1..8e0a55e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,23 +32,39 @@ All utilities guarded to convert all input to hex Added better type definitions for richer IntelliSense in the editor Changed docs to docsify -### 1.8.0 (stable) +### 1.7.9 (unstable) - Fully treeshakable - Core-utils directory was split to submodules. -- Added the `load()` to create read-manipulate-output chains like the one presented by the `chroma()` constructor in chroma-js -- Added the `toHex` utility which parses all known color tokens similar to chroma-js +- Added the `load()` utility to create read-manipulate-output chain with all the utilities that take a collection of colos as first input. The chain is lazy. +- Added the Color class which is aliased as `color()` which has all the utilities that take a color as the first argument bound to its prototype as methods. Calling color() simply calls `new Color()` under the hood. +- Added the `toHex` utility which parses all known color tokens similar to chroma-js `chroma()` constructor. - Eliminated dependancy on lodash. - Used treeshaken Culori modules to reduce bundle size. Only 20KB when minified! - Created the `fp` directory with helper functions used in the library grouped by input type. - More modular codebase - Simplified code to make it more readable and easier to understand for other developers who may want to contribute Over 50 utilities in the API now! -- All palette functions have easings for internal computations +- All palette functions (generators) have easing function support. Updated the docs! -- Improved the accuracy of temperature based utilities. - All palette functions now take an optional overrides object to fine tune parameters like easing methods, fixups etc. -- Added introduction guides to help users understand the project - Rewrote the README and CONTRIBUTING. Added missing licenses for borrowed code. -Special thanks to our [contributors!]() +### 1.79.92 + +- Deprecated temperature based utilities due to inconsistent results. +- Rewrote the type declarations to make them simpler and more generic. +- Lightness and chroma channels are now normalized against passed in extremums in the filtering utilities. +- `toHex` now supports RGB channel in the [0,255] range and [0,1] as well. Values above 1 are normalized using a simple formula. `ch / 255`. +- Removed unnecessary submodules and joined them into grouped modules +- Added Typedoc as the documentation generator of choice. +- New simpler docs writen in GFM. +- Added `getNearestColor` which uses the differenceHyab metric to get the nearest color against a collection. +- Collection based utilities that took arrays of colors are now generic! If an object is passed its keys are passsed as an index and the values are treated as valid color tokens. Objects can only have a depth of 1 ie nested properties are not supported (yet). +- Added color vision deficiency simulation support (stable) +- Added a ColorArray class that has all collection based methods on its prototype as methods. The chain is lazy and requires `output()` to be called to retun the final result. Its aliased as `load()` which simply wraps around the class and invoking a new instance under the hood. +-Added some notes to the docs explaining how some of the utilities work as well as other behaviours of the library. +- Slimmer bundle size. +- Fixed import errors. The library is now fully ESM with a UMD build for CDN and minified version for browser. +- Added support for more uniform colorspaces in hueShift. Jch is currently unsupported because of lightness mapping issues. Support is coming soon though. +- Removed cyclic dependencies that caused a stackoverflow error. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b71e7aaf..7c0b9f76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,12 @@ -# Contributing +# Contributing👐🏾🤝 +Thank you for empowering💪🏾🏋🏽‍♂️ open source by using our project! It's people like you that keep the software community thriving🌱 and relevant. Your contribution means a lot to us💙! +Our documentation site runs on NextJS and uses TailwindCSS for styling. -Thank you for empowering open source by using our project! It's people like you that keep the software community thriving and relevant. Your contribution means a lot to us! +## Getting started⛳ - -## Getting started - -Assuming you already have Node installed on your machine run the following commands in your working directory: +Assuming you already have Node installed on your machine💻 run the following commands in your working directory📁: ```bash # Creating our working directory @@ -15,15 +14,15 @@ mkdir huetiful && cd huetiful ``` -On this step its a matter of preference to either use git clone for cloning the repository. Personally, I prefer `npx degit` because it is simpler: +On this step its a matter of preference💁🏽‍♂️ to either use git clone for cloning the repository. Personally, I prefer `npx degit` because it is simpler: ```bash # Cloning the repository. It will populate your current working directory with all the files in the repository -npx degit prjctimg:huetiful +npx degit prjctimg/huetiful ``` -Install the necessary dependencies needed to setup the development environment and then run the build script to have the current builds added to a `dist/` folder +Install the necessary dependencies📦 needed to setup the development environment and then run the build👷🏾‍♂️ script to have the current builds added to a `dist/` folder ```bash npm install --save-dev @@ -32,18 +31,18 @@ npm run build ``` -If you want to contribute to the documentation, [see the guidelines at the docs repository here]() +If you want to contribute to the documentation📜, [see the guidelines at the docs repository here]() -## Coding conventions and task automation +## Coding conventions📐 and task automation🤖 -This project uses [Husky]() for Git hooks. For example, the code can only be committed if it passes linting tests which are done using [ESLint](). For the code to be pushed to the remote branch it must build without errors on your local machine. This helps maintain sanity and reduce chances of introducing buggy code o the project. All code is automatically formatted when you run `git push` as a pre-push hook. +This project uses Husky🐶 for Git hooks. For example, the code can only be committed if it passes linting tests which are done using 🧐ESLint. For the code to be pushed to the remote branch it must build without errors‼️ on your local machine. This helps maintain sanity🧠 and reduce chances of introducing buggy🐞 code o the project. All code is automatically formatted when you run `git push` as a pre-push hook. -## Issue tracking +## Issue tracking🙋🏽‍♂️ -Our issues are partly managed by GitHub Actions. For example when you open a new issue an automated response is sent to you telling you how to proceed. You can also self assign yourself an issue or task if you want to work on it. This helps other maintainers focus on improving other aspects of the project. +Our issues are partly managed by GitHub Actions. For example when you open a new issue an automated🤖 response is sent to you telling you how to proceed🚦. You can also self assign yourself an issue or task if you want to work on it👐🏾. This helps other maintainers focus on improving other aspects of the project. -## Discussions +## Discussions🗣️💭 -If you have a topic you wish to discuss about feel free to checkout our [discussions on GitHub] and join in the chat! +If you have a topic you wish to discuss about feel free to checkout our [discussions on GitHub] and join in the chat😀! -Happy hacking! +Happy hacking 🚀! diff --git a/LICENSE.md b/LICENSE.md index 85acc626..087bea84 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,17 +1,4 @@ - Copyright (c) 2023 - huetiful-js Javascript library for general purpose color manipulations - Dean Tarisai and contributors - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -200,94 +187,18 @@ same "printed page" as the copyright notice for easier identification within third-party archives. + (c) 2023 Dean Tarisai + huetiful-js + -> Open source TypeScript library for general purpose color manipulations. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - Copyright OpenJS Foundation and other contributors - -Based on Underscore.js, copyright Jeremy Ashkenas, -DocumentCloud and Investigative Reporters & Editors - -This software consists of voluntary contributions made by many -individuals. For exact contribution history, see the revision history -available at https://github.com/lodash/lodash - -The following license applies to all parts of this software except as -documented below: - -==== - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -==== - -Copyright and related rights for sample code are waived via CC0. Sample -code is defined as all source code displayed within the prose of the -documentation. - -CC0: http://creativecommons.org/publicdomain/zero/1.0/ + http://www.apache.org/licenses/LICENSE-2.0 -==== - -Files located in the node_modules and vendor directories are externally -maintained libraries used by this software which have their own -licenses; we recommend you read them, as their terms may differ from the -terms above. - - -MIT License - -Copyright (c) 2018 Dan Burzo -Culori - JavaScript library for general purpose color manipulations. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -/** - ColorBrewer colors for chroma.js - - Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The - Pennsylvania State University. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software distributed - under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. See the License for the - specific language governing permissions and limitations under the License. -*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 436c5508..3b507109 100644 --- a/README.md +++ b/README.md @@ -1,419 +1,77 @@ +[![npm version](https://img.shields.io/npm/v/huetiful-js)](https://www.npmjs.com/package/huetiful-js) +[![stability-stable](https://img.shields.io/badge/stability-stable-green.svg)](https://www.npmjs.com/package/huetiful-js) +[![npm minzipped size](https://img.shields.io/bundlephobia/minzip/huetiful-js)](https://bundlephobia.com/package/huetiful-js) +[![types](https://img.shields.io/npm/types/huetiful-js)](https://github.com/microsoft/TypeScript) +[![styled with prettier](https://img.shields.io/badge/styled_with-Prettier-f8bc45.svg?logo=prettier)](https://github.com/prettier/prettier) +[![linted with eslint](https://img.shields.io/badge/linted_with-ES_Lint-4B32C3.svg?logo=eslint)](https://github.com/eslint/eslint) +[![license](https://img.shields.io/github/license/prjctimg/huetiful)](https://github.com/prjctimg/huetiful/blob/main/LICENSE.md) -![Logo](./huetiful-logo.png) +[![paypal](https://img.shields.io/badge/donate-paypal-informational?logo=paypal)](https://www.paypal.com/donate/?hosted_button_id=CRFWNCE6EW5X2) +[![twitter](https://img.shields.io/twitter/follow/deantarisai?style=social)](https://twitter.com/deantarisai) -#### JavaScript library for general purpose color manipulations -The aim of this project is to help designers and developers alike work with color more programatically using utilities based on color theory. Though not necessarily a requirement, a basic background of color spaces, properties of color and any other color theory related information will make the library's use cases appear more simpler. -This project is inspired by projects such a [chroma-js](https://gka.github.io/chroma.js),[colorbrewer](https://colorbrewer2.org),[TailwindCSS](https://tailwindcss.com) and borrows some of the reasoning behind them to build functionality. In fact this library uses [Culori](https://culorijs.org) as its core dependency because it provides a rich API of low level functions written in JavaScript to perform color conversions and other general purpose color manipulations. +![Logo](./huetiful-logo.png) +TypeScript library for general purpose color manipulations and generating custom color scales. -## Getting started +## Getting started⛳ -The library is available on npm as a package: Links to CDNs will be added soon. +### Node -> Note: Assuming you already have NodeJS installed -Use [npm](https://www.npmjs.com/package/huetiful-js) to install the package. +The library🧾 is on npm as a package📦 for use in NodeJS: ```bash npm i huetiful-js ``` -For use in the browser, you can use a CDN [you can use jsdelivr]() to load the library. - -```html -https://cdn.jsdelivr.net/npm/huetiful-js/dist/huetiful.min.js - -# With script tag - - -``` - -## Overview - -Below are short walkthroughs to demonstrate how the functions can be used in example scenerios. - -### Colors - -#### What's a color ? - -A color can be defined in various formats. This gives us more flexibility in how we want to define our color. Below are examples listing all the supported formats of passing in color values and their respective conversion functions: - -```js -import { num2rgb, toHex } from 'huetiful-js' - -let cssNamedColor = 'pink' -let colorNumber = 5000 -let colorObject = { l: 50, c: 20, h: 40, mode: 'lch' } -let colorObjectWithAlpha = { l: 50, c: 20, h: 40, alpha: 0.5, mode: 'lch' } -let arrColor = ['rgb', 120, 80, 50] -let arrColorWithAlpha = ['rgb', 120, 80, 50, 0.1] - - -// Converting CSS named colors to hex -console.log(toHex(cssNamedColor)) -// #ffc0cb - -// Converting a number to an RGB object -console.log(num2rgb(colorNumber, true)) -// #001388 - -// Converting a color object to a 6 character hex (without the alpha value) -console.log(toHex(colorObject)) -// #956d62 - -// Converting a color object with an alpha property to an 8 character hex code (including the alpha channel) -console.log(toHex(colorObjectWithAlpha)) -// #956d6280 - -// Converting an array of channel values to a 6 character hex code. -console.log(toHex(arrColor)) -// #785032 - -// Converting an array of channel values (including the alpha channel) to an 8 character hex -console.log(toHex(arrColorWithAlpha)) -//#7850321a - - -``` - -Note that toHex takes any color format and returns the hex string represantation of it: - -We can even mix different color formats with no problem at all: - -For more information on the color spaces supported by the library and the expected ranges, checkout the [Color Spaces page on the Culori docs](https://culorijs.org/color-spaces) . Or checkout the library's [Color conversions](https://huetiful-docs.vercel.app/blog/color-spaces-and-converters) page. - -> All the functions are internally guarded by `toHex()` so you don't have to worry about converting colors back and fourth. - -#### Tailwind colors - -As a starting point the library comes along with the default TailwindCSS palette included.. This helps you get started easier when you're using [palette functions](https://huetiful-docs.vercel.app/blog/palette-utilities) such as `hueShift()` and `earthtone()` - -The Tailwind colors are in the form of two wrapper functions that both take the same parameters but with a few differences: one is curried by default and the other is a tweaked variant of the same functionality but with a few improvements. Below are some examples showing the differences between the two functions: - -```js - import { tailwindColors,colors } from "huetiful-js"; - -// We pass in red as the target hue. -// It returns a function that can be called with an optional value parameter -let red = tailwindColors("red"); -console.log(red()); -// [ - '#fef2f2', '#fee2e2', - '#fecaca', '#fca5a5', - '#f87171', '#ef4444', - '#dc2626', '#b91c1c', - '#991b1b', '#7f1d1d' -] - - -console.log(red(100)); -// '#fee2e2' - -console.log(red('900')); -// '#7f1d1d' - - ////// example for colors() ////// - -// colors() has a builtin parameter called 'all' that returns all colors at the specified value -let all300 = colors("all", 300); - -console.log(all300) -//[ - '#cbd5e1', '#d1d5db', '#d4d4d8', - '#d4d4d4', '#d6d3d1', '#fca5a5', - '#fdba74', '#fcd34d', '#fde047', - '#bef264', '#86efac', '#6ee7b7', - '#5eead4', '#7dd3fc', '#93c5fd', - '#c4b5fd', '#d8b4fe', '#f0abfc', - '#f9a8d4', '#fda4af' -] - -let red = colors("red"); -console.log(red); - -// [ - '#fef2f2', '#fee2e2', - '#fecaca', '#fca5a5', - '#f87171', '#ef4444', - '#dc2626', '#b91c1c', - '#991b1b', '#7f1d1d' -] - -let red100 = colors("red", 100); - -console.log(red100) -// #fee2e2 - -``` - - - -### Working with collections of colors - -The library has functions for sorting and filtering colors using their property values like saturation,lightness and even temperaure in Kelvins. Below are some examples of using the filtering and sorting functions on an array of colors: - -#### Sorting colors - -Below is an example of sorting colors by the relative luminance as defined by WCAG 2.0. -> Note that you can specify the order as either ascending (`asc`) or descending (`desc`). The default is ascending. : - -```js -import { sortByLuminance } from "huetiful-js"; -let sample = [ - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#ffff00", - "#310000", - "#3e0000", - "#4e0000", - "#600000", - "#720000", -]; - - - -let sorted = sortByLuminance(sample) -console.log(sorted) -// [ - '#310000', '#3e0000', - '#4e0000', '#600000', - '#720000', '#164100', - '#007e00', '#00c000', - '#00ff78', '#00ffdc', - '#ffff00' -] - -let sortedDescending = sortByLuminance(sample, "desc"); -console.log(sortedDescending) -// [ - '#ffff00', '#00ffdc', - '#00ff78', '#00c000', - '#007e00', '#164100', - '#720000', '#600000', - '#4e0000', '#3e0000', - '#310000' -] - - -``` - -#### Filtering colors +You can use a CDN in this example, jsdelivr to load the library remotely: -Below is an example of filtering colors by their hue angle. The function uses LCH internally because its more perceptually uniform than HSL. [George Francis explains this phenomena in detail here.](https://tympanus.net/codrops/2021/12/07/coloring-with-code-a-programmatic-approach-to-design/) - -```js - let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -filterByHue(sample, 20, 80) - -// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] - - // We can even use expressions as the condition e.g '>=50' which means return the colors with a hue angle greater than or equal to 50 - - // Here are some examples -console.log(filterByHue(sample, '>100') -) -// [ '#00ffdc', '#00ff78', '#00c000', '#007e00', '#164100' ] - -console.log(filterByHue(sample, '<=100') -) -// [ '#ffff00', '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] - -``` -[See more about the parameter types and other filtering functions](https://huetiful-docs.vercel.app/blog/sorting-color) - -### Palette functions - -A small collection of simple palette functions are included in the library. One of my favourites is the `hueShift()` (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) . ```js -import { hueShift } from "huetiful-js"; - -let hueShiftedPalette = hueShift("#3e0000", {}, true); - -console.log(hueShiftedPalette); - -// [ - '#ffffe1', '#ffdca5', - '#ca9a70', '#935c40', - '#5c2418', '#3e0000', - '#310000', '#34000f', - '#38001e', '#3b002c', - '#3b0c3a' -] - - +import {...} from 'https://cdn.jsdelivr.net/npm/huetiful-js/lib/huetiful.esm.min.mjs' ``` -There's more where that came from. [See the palettes page]() - -### Predicates - -Is this color cool or warm, is it achromatic (grayscale) or chromatic? Though its easy to tell colors apart visually when they're displayed on the screen it can be a bit confusing to tell colors apart using code. Below is an example of demonstrating how to determine if a color is gray or not: - -```js - -import { isAchromatic } from "huetiful-js"; -import { formatHex8, interpolate, samples } from "culori" - - -isAchromatic('pink') -// false - -let sample = [ - "#164100", - "#ffff00", - "#310000", - 'pink' -]; - -console.log(map(sample, isAchromatic)); - -// [false, false, false,false] - -isAchromatic('gray') -// true - - - -// Here are using some of Culori's functions to demonstrate this example -// we create an interpolation using black and white -let f = interpolate(["black", "white"]); - -//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function. -// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.) -let grays = map(samples(12), (c) => formatHex8(f(c))); -console.log(map(grays, isAchromatic)); - -// The last two colors are false because we can't categorize black and white as achromatic. +### Browser -// - [ false, true, true, - true, true, true, - true, true, true, - true, true, false -] +Or load the library as a UMD glabal (`huetiful`) in your HTML file using a ` ``` -Or maybe we want to know which color has the furthest hue distance in our sample collection against our target color: +## Quickstart -```js +[See the Quickstart section on the Wiki](/notes/quickstart.md) to see some examples and demonstrations of the library. -import { getFarthestHue } from 'huetiful-js' -let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] +## What's next🤷🏽‍♂️ -console.log(getFarthestHue('lime', sample, 'lch')) -// 112.60431681589854 +> The possibilities are limited by the imagination🤯 of the user._ +> ~ me :smile: -``` +[See the full docs here📜](https:prjctimg.github.io/huetiful) -## What's next +## Need help😣 ? -The list of functions goes on beyond this. And since the library is pure JavaScript, you can hook it up with your creative coding library of choice like p5js or runejs. The possibilities are limited by the imagination of the user. +See some unexpected results😖? [Check the issue tracker](https://github.com/prjctimg/huetiful/issues) to open an issue or search for the problem to see if your issue already exists or has been resolved. +Would like to join the chat🗣️ and share ideas💡 and suggestions💭 ? [See the discussions and just say hi, or share a coding meme(whatever breaks the ice🏔️)](https://github.com/prjctimg/huetiful/discussions) -[See the full docs here](https:huetiful-docs.vercel.app) +## Contributing👐🏾 -## Need help ? +This project is fully open source so contributions are welcome! Help make this project better by suggesting improvements or features and patching bugs🐛. See🔍 the [CONTRIBUTING](./CONTRIBUTING.md) file for more information on how to get started. -See some unexpected results? [Check the issue tracker](https://github.com/prjctimg/huetiful/issues) to open an issue or search for the problem to see if your issue already exists or has been resolved. +## Donating -Would like to join the chat and share ideas and suggestions ? [See the discussions and just say hi, or share a coding meme(whatever breaks the ice)](https://github.com/prjctimg/huetiful/discussions) +This project is completely free and will remain so forever. But if you wish to support the development of this project or just buy the developer a coffee, please feel free. -## Contributing - -First of all, thank you for using huetiful-js! Its people like you that make open source software better for the community! -Contributions are welcome! Help make this project better and easier to use for other developers by sharing your ideas and stomping out bugs and feature suggestions. Please see the [CONTRIBUTING](./CONTRIBUTING.md) file for more information on how to get started. - -## References +## References🔗 [Coloring with code: A programmatic approach by George Francis](https://tympanus.net/codrops/2021/12/07/coloring-with-code-a-programmatic-approach-to-design/) -> ## License +> ###### License > -> Copyright (c) 2023 +> Copyright (c) 2023, > Dean Tarisai and contributors > huetiful-js is released under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) license. -> -> This project makes extensive use of open source resources and its inception would have had been null if it wasn't for their pioneering. See the [LICENSES](./LICENSE.md) for the full list of open source licenses. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 5a659370..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,15 +0,0 @@ -# Security Policy - -## Supported Versions - -This project had a lot of errors in previous versions therefore for the sake of keeping the development requirements clean we are supprting the most recent minor version (which is currently v1.7 at the time of writing). - -| Version | Supported | -| ------- | ------------------ | -| 1.7.x | :white_check_mark: | - - -## Reporting a Vulnerability - - -To report a security vulnerability, use [the issue tracker](http://github.com/prjctimg/huetiful/issues), specifying the exact version you encountered the problem and sharing all the necessary environment variables needed to reproduce the error or problem.i diff --git a/build.cjs b/build.cjs index 7fdd2be4..21a40f04 100644 --- a/build.cjs +++ b/build.cjs @@ -1,11 +1,11 @@ +/* eslint-disable no-undef */ /* eslint-disable @typescript-eslint/no-var-requires */ // esbuild script //@ts-nocheck // eslint-disable-next-line no-undef var { build } = require('esbuild'); -var { dependencies } = require('./package.json') - +var { dependencies } = require('./package.json'); const sharedConfig = { entryPoints: ['.//src/index.ts'], @@ -13,82 +13,38 @@ const sharedConfig = { minify: false }; -// ***Modular imports** \\ - -// palettes/ import -build({ - ...sharedConfig, - format: 'esm', - entryPoints: ['.//src/palettes/index.ts'], - outfile: 'dist/palettes/index.esm.mjs', - - -}); - -// filterBy/ import -build({ - ...sharedConfig, - format: 'esm', - outfile: 'dist/filterBy/index.esm.mjs', - - -}); - -// sortBy/ import -build({ - ...sharedConfig, - format: 'esm', - entryPoints: ['.//src/sortBy/index.ts'], - outfile: 'dist/sortBy/index.esm.mjs', - -}); - -// colors/ import -build({ - ...sharedConfig, - format: 'esm', - entryPoints: ['.//src/colors/index.ts'], - outfile: 'dist/colors/index.esm.mjs', -}); - - -// core-utils/ import -build({ - format: 'esm', - ...sharedConfig, - entryPoints: ['.//src/getters_and_setters/index.ts'], - outfile: 'dist/getters_and_setters/index.esm.mjs', - -}); - - +// commonJS import +// build({ +// format: 'cjs', +// ...sharedConfig, +// outfile: './lib/huetiful.cjs', +// external: Object.keys(dependencies) +// }); //Bundled ESM build({ ...sharedConfig, - external: Object.keys(dependencies), + platform: 'browser', format: 'esm', - outfile: 'dist/huetiful.esm.mjs', minifySyntax: true, - -}); - -//Bundled ESM minified -build({ - ...sharedConfig, - format: 'esm', - outfile: 'dist/huetiful.esm.min.mjs', - minify: true, - + outfile: './lib/huetiful.esm.min.mjs' }); //Bundled IIFE build({ ...sharedConfig, + platform: 'browser', format: 'iife', - outfile: 'dist/huetiful.min.js', + outfile: './lib/huetiful.umd.js', globalName: 'huetiful', + minifySyntax: true, minify: true - }); - +// ESM no bundle +build({ + ...sharedConfig, + platform: 'node', + format: 'esm', + outfile: './lib/huetiful.esm.mjs', + external: Object.keys(dependencies) +}); \ No newline at end of file diff --git a/docs.js b/docs.js new file mode 100644 index 00000000..beeaf9b0 --- /dev/null +++ b/docs.js @@ -0,0 +1,194 @@ +#!/usr/bin / env node + +/* + * @license + * docs.js - Custom documentation generator script for huetiful-js. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + + +import { + readFileSync, + writeFileSync, + renameSync, + readdirSync, + appendFileSync, + rmSync, + cpSync, + copyFileSync, + mkdirSync, + rmdirSync, + symlinkSync, + symlink +} from 'fs'; +import { stringOf } from 'github-emoji'; +import { log } from 'console'; + +// Global paths and refs +var pathToContent = 'spacebook/content/pages'; +var rootDir = '.temp_docs'; +var mainIndex = fileContents(rootDir, 'modules'); +var pathToMainIndex = String(rootDir + '/modules.md'); +var pathToTemplates = 'notes' + '/templates'; + +// The filenames of source files and .md files +const pathSegments = [ + 'helpers', + 'colors', + 'filterBy', + 'converters', + 'utils', + 'generators', + 'sortBy', + 'types' +]; +function fileContents(rootDir, filenameSegment, extension = 'md') { + return readFileSync(`${rootDir}/${filenameSegment}.${extension}`, 'utf8'); +} + +const markdownHeadingEmojiMap = { + '### Functions': 'toolbox', + '## Module:': 'package', + 'Module:': 'package', + '## Table of contents': 'scroll', + '### Parameters': 'abacus', + '#### Returns': 'back', + '#### Defined in': 'memo', + '### Modules': 'package', + '### Constructors': 'hammer_and_wrench', + '### Properties': 'microscope', + '# Class:': 'card_file_box', + '### Methods': 'wrench', + '## Constructors': 'hammer_and_wrench', + '### constructor': 'hammer_and_wrench', + '### Classes': 'card_file_box' +}; +// webapp\data\api +//const reHtmlComment = /()[\s\S]*?()$/gm; +//const replace = (data) => `\n${data}\n`; +const re = (pattern) => new RegExp(pattern, 'gi'); +const generateDocs = (filenameSegment) => { + let typeDocMarkdownOutput = fileContents( + rootDir + `/modules`, + filenameSegment, + `md` + ); + // let templateMarkdownFile = fileContents("templates", filenameSegment, `mdx`); + + console.log(`[info] Adding utf8 emojis in ${filenameSegment}.md`); + + for (const heading of Object.keys(markdownHeadingEmojiMap)) { + typeDocMarkdownOutput = typeDocMarkdownOutput.replace( + re(heading), + `${heading}${stringOf(markdownHeadingEmojiMap[heading])}` + ); + } + typeDocMarkdownOutput = typeDocMarkdownOutput.replace( + /\*\*`Description`\*\*/g, + `**\`Description\`** ${stringOf('information_source')}` + ); + + typeDocMarkdownOutput = typeDocMarkdownOutput.replace( + /\*\*`Example`\*\*/g, + `**\`Example\`** ${stringOf('clipboard')}` + ); + + typeDocMarkdownOutput = typeDocMarkdownOutput.replace( + /\*\*`Function`\*\*/g, + '' + ); + + writeFileSync( + rootDir + `/modules/${filenameSegment}.md`, + typeDocMarkdownOutput + ); +}; + +for (const pathSeg of pathSegments) { + generateDocs(pathSeg); +} + +for (const heading of Object.keys(markdownHeadingEmojiMap)) { + writeFileSync( + pathToMainIndex, + fileContents(rootDir, 'modules').replace( + re(heading), + `${heading}${stringOf(markdownHeadingEmojiMap[heading])}` + ) + ); +} +for (const i of ['Color.ColorArray', 'Color.Color']) { + for (const heading of Object.keys(markdownHeadingEmojiMap)) { + writeFileSync( + rootDir + `/classes/${i}.md`, + fileContents(rootDir, 'modules').replace( + re(heading), + `${heading}${stringOf(markdownHeadingEmojiMap[heading])}` + ) + ); + } + log(`Added utf8 emojis to ` + i); +} + +// Insert logo image and parse the emojis +// writeFileSync( +// pathToMainIndex, +// mainIndex.replace('# huetiful-js', `![Logo](./huetiful-logo.png)`) +// ); +// // writeFileSync(pathToMainIndex, rawGfmToGfm('/modules.md')); + +// renameSync(pathToMainIndex, `${rootDir}/index.md`); + +var templates = readdirSync(pathToTemplates); + +// Loop through previous files and delete each +for (const template of templates) { + rmSync(`${pathToContent}/${template}`); + log(`Deleted ${template}.`); +} + +// Loop through each template file and append them into the pages folder +for (let index = 0; index < pathSegments.length; index++) { + var toPath = `${pathToContent}/${pathSegments[index]}.md`, + currentPath = pathSegments[index]; + writeFileSync( + `${pathToContent}/${currentPath}.md`, + fileContents(pathToTemplates, currentPath), + 'utf-8' + ); + + appendFileSync( + toPath, + `\n${fileContents(rootDir, `modules/${currentPath}`)}` + ); + console.log( + `Injected data to ${toPath} from ${rootDir}/modules/${currentPath}.md` + ); +} + +// Copy index file template and append data to it +copyFileSync(pathToTemplates + '/index.md', `${pathToContent}/index.md`); +// appendFileSync(`${pathToContent}/home.md`, fileContents(rootDir, 'index')); +log(`Generated markdown with UTF8 emojis successfully!`); + + +// var notes = readdirSync('notes').map((el) => el.split('.')[0]); + +// for (const note of notes) { +// appendFileSync( +// `spacebook/content/pages/notes/${note}.md`, +// fileContents(`notes`, note) +// ); + +// console.log( +// `Injected data to ${pathToContent}/${note} from notes/${note}.md` +// ); +// } + diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 00000000..e2ac6616 --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +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. \ No newline at end of file diff --git a/docs/assets/highlight.css b/docs/assets/highlight.css new file mode 100644 index 00000000..c8c71c80 --- /dev/null +++ b/docs/assets/highlight.css @@ -0,0 +1,120 @@ +:root { + --light-hl-0: #795E26; + --dark-hl-0: #DCDCAA; + --light-hl-1: #000000; + --dark-hl-1: #D4D4D4; + --light-hl-2: #A31515; + --dark-hl-2: #CE9178; + --light-hl-3: #AF00DB; + --dark-hl-3: #C586C0; + --light-hl-4: #800000; + --dark-hl-4: #808080; + --light-hl-5: #800000; + --dark-hl-5: #569CD6; + --light-hl-6: #000000FF; + --dark-hl-6: #D4D4D4; + --light-hl-7: #E50000; + --dark-hl-7: #9CDCFE; + --light-hl-8: #0000FF; + --dark-hl-8: #CE9178; + --light-hl-9: #001080; + --dark-hl-9: #9CDCFE; + --light-hl-10: #008000; + --dark-hl-10: #6A9955; + --light-hl-11: #0000FF; + --dark-hl-11: #569CD6; + --light-hl-12: #098658; + --dark-hl-12: #B5CEA8; + --light-hl-13: #CD3131; + --dark-hl-13: #F44747; + --light-code-background: #FFFFFF; + --dark-code-background: #1E1E1E; +} + +@media (prefers-color-scheme: light) { :root { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --hl-6: var(--light-hl-6); + --hl-7: var(--light-hl-7); + --hl-8: var(--light-hl-8); + --hl-9: var(--light-hl-9); + --hl-10: var(--light-hl-10); + --hl-11: var(--light-hl-11); + --hl-12: var(--light-hl-12); + --hl-13: var(--light-hl-13); + --code-background: var(--light-code-background); +} } + +@media (prefers-color-scheme: dark) { :root { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --hl-6: var(--dark-hl-6); + --hl-7: var(--dark-hl-7); + --hl-8: var(--dark-hl-8); + --hl-9: var(--dark-hl-9); + --hl-10: var(--dark-hl-10); + --hl-11: var(--dark-hl-11); + --hl-12: var(--dark-hl-12); + --hl-13: var(--dark-hl-13); + --code-background: var(--dark-code-background); +} } + +:root[data-theme='light'] { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --hl-6: var(--light-hl-6); + --hl-7: var(--light-hl-7); + --hl-8: var(--light-hl-8); + --hl-9: var(--light-hl-9); + --hl-10: var(--light-hl-10); + --hl-11: var(--light-hl-11); + --hl-12: var(--light-hl-12); + --hl-13: var(--light-hl-13); + --code-background: var(--light-code-background); +} + +:root[data-theme='dark'] { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --hl-6: var(--dark-hl-6); + --hl-7: var(--dark-hl-7); + --hl-8: var(--dark-hl-8); + --hl-9: var(--dark-hl-9); + --hl-10: var(--dark-hl-10); + --hl-11: var(--dark-hl-11); + --hl-12: var(--dark-hl-12); + --hl-13: var(--dark-hl-13); + --code-background: var(--dark-code-background); +} + +.hl-0 { color: var(--hl-0); } +.hl-1 { color: var(--hl-1); } +.hl-2 { color: var(--hl-2); } +.hl-3 { color: var(--hl-3); } +.hl-4 { color: var(--hl-4); } +.hl-5 { color: var(--hl-5); } +.hl-6 { color: var(--hl-6); } +.hl-7 { color: var(--hl-7); } +.hl-8 { color: var(--hl-8); } +.hl-9 { color: var(--hl-9); } +.hl-10 { color: var(--hl-10); } +.hl-11 { color: var(--hl-11); } +.hl-12 { color: var(--hl-12); } +.hl-13 { color: var(--hl-13); } +pre, code { background: var(--code-background); } diff --git a/docs/assets/main.js b/docs/assets/main.js new file mode 100644 index 00000000..d0aa8d5f --- /dev/null +++ b/docs/assets/main.js @@ -0,0 +1,59 @@ +"use strict"; +"use strict";(()=>{var Pe=Object.create;var ne=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var Oe=Object.getOwnPropertyNames;var _e=Object.getPrototypeOf,Re=Object.prototype.hasOwnProperty;var Me=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Fe=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Oe(e))!Re.call(t,i)&&i!==n&&ne(t,i,{get:()=>e[i],enumerable:!(r=Ie(e,i))||r.enumerable});return t};var De=(t,e,n)=>(n=t!=null?Pe(_e(t)):{},Fe(e||!t||!t.__esModule?ne(n,"default",{value:t,enumerable:!0}):n,t));var ae=Me((se,oe)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,u],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[u+1]*i[d+1],u+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}s.str.length==1&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),v=s.str.charAt(1),f;v in s.node.edges?f=s.node.edges[v]:(f=new t.TokenSet,s.node.edges[v]=f),s.str.length==1&&(f.final=!0),i.push({node:f,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof se=="object"?oe.exports=n():e.lunr=n()}(this,function(){return t})})()});var re=[];function G(t,e){re.push({selector:e,constructor:t})}var U=class{constructor(){this.alwaysVisibleMember=null;this.createComponents(document.body),this.ensureActivePageVisible(),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible())}createComponents(e){re.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r}}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(n&&n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let r=document.createElement("p");r.classList.add("warning"),r.textContent="This member is normally hidden due to your filter settings.",n.prepend(r)}}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent="Copied!",e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent="Copy"},100)},1e3)})})}};var ie=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var de=De(ae());async function le(t,e){if(!window.searchData)return;let n=await fetch(window.searchData),r=new Blob([await n.arrayBuffer()]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();t.data=i,t.index=de.Index.load(i.index),e.classList.remove("loading"),e.classList.add("ready")}function he(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:t.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{le(e,t)}),le(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");let s=!1;i.addEventListener("mousedown",()=>s=!0),i.addEventListener("mouseup",()=>{s=!1,t.classList.remove("has-focus")}),r.addEventListener("focus",()=>t.classList.add("has-focus")),r.addEventListener("blur",()=>{s||(s=!1,t.classList.remove("has-focus"))}),Ae(t,i,r,e)}function Ae(t,e,n,r){n.addEventListener("input",ie(()=>{Ne(t,e,n,r)},200));let i=!1;n.addEventListener("keydown",s=>{i=!0,s.key=="Enter"?Ve(e,n):s.key=="Escape"?n.blur():s.key=="ArrowUp"?ue(e,-1):s.key==="ArrowDown"?ue(e,1):i=!1}),n.addEventListener("keypress",s=>{i&&s.preventDefault()}),document.body.addEventListener("keydown",s=>{s.altKey||s.ctrlKey||s.metaKey||!n.matches(":focus")&&s.key==="/"&&(n.focus(),s.preventDefault())})}function Ne(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=ce(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` + ${ce(l.parent,i)}.${d}`);let v=document.createElement("li");v.classList.value=l.classes??"";let f=document.createElement("a");f.href=r.base+l.url,f.innerHTML=u+d,v.append(f),e.appendChild(v)}}function ue(t,e){let n=t.querySelector(".current");if(!n)n=t.querySelector(e==1?"li:first-child":"li:last-child"),n&&n.classList.add("current");else{let r=n;if(e===1)do r=r.nextElementSibling??void 0;while(r instanceof HTMLElement&&r.offsetParent==null);else do r=r.previousElementSibling??void 0;while(r instanceof HTMLElement&&r.offsetParent==null);r&&(n.classList.remove("current"),r.classList.add("current"))}}function Ve(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),e.blur()}}function ce(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(K(t.substring(s,o)),`${K(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(K(t.substring(s))),i.join("")}var Be={"&":"&","<":"<",">":">","'":"'",'"':"""};function K(t){return t.replace(/[&<>"'"]/g,e=>Be[e])}var C=class{constructor(e){this.el=e.el,this.app=e.app}};var F="mousedown",pe="mousemove",B="mouseup",J={x:0,y:0},fe=!1,ee=!1,He=!1,D=!1,me=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(me?"is-mobile":"not-mobile");me&&"ontouchstart"in document.documentElement&&(He=!0,F="touchstart",pe="touchmove",B="touchend");document.addEventListener(F,t=>{ee=!0,D=!1;let e=F=="touchstart"?t.targetTouches[0]:t;J.y=e.pageY||0,J.x=e.pageX||0});document.addEventListener(pe,t=>{if(ee&&!D){let e=F=="touchstart"?t.targetTouches[0]:t,n=J.x-(e.pageX||0),r=J.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(B,()=>{ee=!1});document.addEventListener("click",t=>{fe&&(t.preventDefault(),t.stopImmediatePropagation(),fe=!1)});var X=class extends C{constructor(n){super(n);this.className=this.el.dataset.toggle||"",this.el.addEventListener(B,r=>this.onPointerUp(r)),this.el.addEventListener("click",r=>r.preventDefault()),document.addEventListener(F,r=>this.onDocumentPointerDown(r)),document.addEventListener(B,r=>this.onDocumentPointerUp(r))}setActive(n){if(this.active==n)return;this.active=n,document.documentElement.classList.toggle("has-"+this.className,n),this.el.classList.toggle("active",n);let r=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(r),setTimeout(()=>document.documentElement.classList.remove(r),500)}onPointerUp(n){D||(this.setActive(!0),n.preventDefault())}onDocumentPointerDown(n){if(this.active){if(n.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(n){if(!D&&this.active&&n.target.closest(".col-sidebar")){let r=n.target.closest("a");if(r){let i=window.location.href;i.indexOf("#")!=-1&&(i=i.substring(0,i.indexOf("#"))),r.href.substring(0,i.length)==i&&setTimeout(()=>this.setActive(!1),250)}}}};var te;try{te=localStorage}catch{te={getItem(){return null},setItem(){}}}var Q=te;var ve=document.head.appendChild(document.createElement("style"));ve.dataset.for="filters";var Y=class extends C{constructor(n){super(n);this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),ve.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } +`}fromLocalStorage(){let n=Q.getItem(this.key);return n?n==="true":this.el.checked}setLocalStorage(n){Q.setItem(this.key,n.toString()),this.value=n,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),document.querySelectorAll(".tsd-index-section").forEach(n=>{n.style.display="block";let r=Array.from(n.querySelectorAll(".tsd-index-link")).every(i=>i.offsetParent==null);n.style.display=r?"none":"block"})}};var Z=class extends C{constructor(n){super(n);this.summary=this.el.querySelector(".tsd-accordion-summary"),this.icon=this.summary.querySelector("svg"),this.key=`tsd-accordion-${this.summary.dataset.key??this.summary.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`;let r=Q.getItem(this.key);this.el.open=r?r==="true":this.el.open,this.el.addEventListener("toggle",()=>this.update());let i=this.summary.querySelector("a");i&&i.addEventListener("click",()=>{location.assign(i.href)}),this.update()}update(){this.icon.style.transform=`rotate(${this.el.open?0:-90}deg)`,Q.setItem(this.key,this.el.open.toString())}};function ge(t){let e=Q.getItem("tsd-theme")||"os";t.value=e,ye(e),t.addEventListener("change",()=>{Q.setItem("tsd-theme",t.value),ye(t.value)})}function ye(t){document.documentElement.dataset.theme=t}var Le;function be(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",xe),xe())}async function xe(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let n=await(await fetch(window.navigationData)).arrayBuffer(),r=new Blob([n]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();Le=t.dataset.base+"/",t.innerHTML="";for(let s of i)we(s,t,[]);window.app.createComponents(t),window.app.ensureActivePageVisible()}function we(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-index-accordion`:"tsd-index-accordion",s.dataset.key=i.join("$");let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.innerHTML='',Ee(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let u of t.children)we(u,l,i)}else Ee(t,r,t.class)}function Ee(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));r.href=Le+t.path,n&&(r.className=n),location.href===r.href&&r.classList.add("current"),t.kind&&(r.innerHTML=``),r.appendChild(document.createElement("span")).textContent=t.text}else e.appendChild(document.createElement("span")).textContent=t.text}G(X,"a[data-toggle]");G(Z,".tsd-index-accordion");G(Y,".tsd-filter-item input[type=checkbox]");var Se=document.getElementById("tsd-theme");Se&&ge(Se);var je=new U;Object.defineProperty(window,"app",{value:je});he();be();})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/docs/assets/material-style.css b/docs/assets/material-style.css new file mode 100644 index 00000000..b7e1d318 --- /dev/null +++ b/docs/assets/material-style.css @@ -0,0 +1,247 @@ +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap"); + +:root, +:root[data-theme="light"], +:root[data-theme="dark"] { + --font-sans: "Space Grotesk", sans-serif; + --font-mono: "Space Mono", monospace; + + --color-background: var(--md-sys-color-surface-container); + --color-background-secondary: var(--md-sys-color-surface-container-high); + --color-background-warning: var(--md-sys-color-error-container); + --color-warning-text: var(--md-sys-color-on-error-container); + --color-icon-background: var(--md-sys-color-on-primary); + --color-accent: var(--md-sys-color-secondary-container); + --color-active-menu-item: var(--md-sys-color-surface-container-highest); + --color-text: var(--md-sys-color-on-surface); + --color-text-aside: var(--md-sys-color-on-surface-variant); + --color-link: var(--md-sys-color-primary); + + --color-ts-project: var(--md-sys-color-secondary); + --color-ts-module: var(--color-ts-project); + --color-ts-namespace: var(--color-ts-project); + + --color-ts-enum: var(--md-sys-color-tertiary); + --color-ts-enum-member: var(--color-ts-enum); + + --color-ts-variable: var(--md-sys-color-primary); + --color-ts-function: var(--md-sys-color-secondary); + --color-ts-class: var(--md-sys-color-tertiary); + --color-ts-interface: var(--md-sys-color-tertiary); + + --color-ts-constructor: var(--md-sys-color-inverse-primary); + + --color-ts-property: var(--md-sys-color-on-background); + --color-ts-method: var(--color-ts-function); + + --color-ts-call-signature: var(--color-ts-method); + --color-ts-index-signature: var(--color-ts-property); /* ? */ + --color-ts-constructor-signature: var(--color-ts-function); + --color-ts-parameter: var(--md-sys-color-primary); + + --color-ts-type-parameter: var(--md-sys-color-tertiary); + --color-ts-accessor: var(--color-ts-property); + --color-ts-get-signature: var(--color-ts-accessor); + --color-ts-set-signature: var(--color-ts-accessor); + --color-ts-type-alias: var(--md-sys-color-tertiary); + + /* --external-icon: var(--md-sys-external-icon); + --color-scheme: var(--md-sys-color-scheme); */ + + --top-app-bar-height: 4.5rem; + --footer-height: 3.5rem; +} + +body { + font-family: var(--font-sans); +} +code, +pre { + font-family: var(--font-mono); +} + +img { + max-width: 100%; +} + +*::-webkit-scrollbar { + width: 8px; + height: 8px; +} +*::-webkit-scrollbar-track { + background: none; +} +*::-webkit-scrollbar-thumb { + border: none; +} + +.container-main { + min-height: calc(100vh - var(--top-app-bar-height) - var(--footer-height)); +} +.col-content { + overflow: hidden; + box-sizing: border-box; + padding: 1.75rem; + border-radius: 28px; + background-color: var(--md-sys-color-surface); +} +.page-menu { + height: fit-content; + padding: 0.75rem 1.75rem; + border-radius: 28px; + background-color: var(--md-sys-color-surface); +} +.site-menu > *, +.page-menu > * { + position: relative; +} +.title { + display: block; + max-width: calc(100% - 5rem); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 22px; +} + +.tsd-page-toolbar { + padding: 8px 0; + height: calc(var(--top-app-bar-height) - 16px); + background-color: var(--color-background); + border-bottom: none; +} +.tsd-page-toolbar .tsd-toolbar-contents { + height: 56px; +} +.tsd-page-toolbar .table-cell { + height: 56px; + margin-left: 1.5rem; +} +.tsd-page-toolbar .tsd-toolbar-icon { + padding: 20px 0; +} +#tsd-search { + line-height: 56px; + border-radius: 22px; +} +#tsd-search .results { + z-index: -1; + top: calc(56px - 22px); + padding-top: 22px; + box-shadow: 0px 4px 2px rgba(0, 0, 0, 0.125); + background-color: var(--color-background-secondary); + border-bottom-left-radius: 22px; + border-bottom-right-radius: 22px; + overflow: hidden; +} +#tsd-search .results li { + background: none; +} +#tsd-search .results a { + padding: 1rem 0.25rem; +} +.col-sidebar { + padding-top: 0; + margin-right: 1rem; +} + +.tsd-signature { + padding: 1rem 1.5rem; + border-radius: 24px; + background-color: var(--md-sys-color-surface-container); +} + +.tsd-page-navigation ul { + padding-left: 0.44rem; +} +.tsd-navigation a, +.tsd-navigation summary > span, +.tsd-page-navigation a { + padding: 0.88rem; + border-radius: 24px; +} +.tsd-navigation a:hover, +.tsd-page-navigation a:hover { + text-decoration: none; + background-color: var(--md-sys-color-surface-container-high); +} +.page-menu .tsd-accordion-summary svg { + position: absolute; + right: 0; +} +.site-menu .tsd-navigation .tsd-accordion-summary { + display: flex; + flex-direction: row-reverse; + width: 100%; +} + +.tsd-small-nested-navigation { + margin-left: 1rem; +} +.tsd-nested-navigation { + margin-left: 2.5rem; +} +.tsd-nested-navigation > li > a, +.tsd-nested-navigation > li > span { + width: 100%; +} +.tsd-navigation > a, +.tsd-navigation .tsd-accordion-summary { + width: 100%; +} +.tsd-index-accordion .tsd-accordion-summary > svg { + position: absolute; + right: 1.5rem; + margin-top: 1rem; +} +.tsd-accordion-summary .tsd-kind-icon ~ span { + margin-right: 2.5rem; +} +.tsd-accordion-summary .tsd-nested-navigation > li > a, +.tsd-nested-navigation > li > span { + width: calc(100% - 0.44rem); +} +.tsd-kind-icon ~ span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.tsd-generator { + padding: 0; + border-top: none; + height: var(--footer-height); + line-height: var(--footer-height); +} +.tsd-generator > p { + padding: 0 2rem; +} + +@media (max-width: 769px) { + .container { + padding: 1rem; + } + .col-sidebar { + margin-right: 0; + } +} +@media (min-width: 770px) { + .container-main { + margin: 0 auto; + } + .site-menu { + margin-right: 0.5rem; + } +} +@media (min-width: 1200px) { + .page-menu, + .site-menu { + max-height: calc(100vh - var(--footer-height) - var(--top-app-bar-height)); + top: var(--top-app-bar-height); + } + .page-menu { + margin-left: 1rem; + } + .col-sidebar { + margin-right: 0; + } +} diff --git a/docs/assets/navigation.js b/docs/assets/navigation.js new file mode 100644 index 00000000..260832dd --- /dev/null +++ b/docs/assets/navigation.js @@ -0,0 +1 @@ +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAACpWaS3PbNhRG/4u6tdtYcR/xzpXriadN41Zuu8h4AZOQiBh8GABdu53890KESAIk+OF2lYlwcAjieS/oT/+uDH8xq4tVVsta6dXJqmGmsP8v67yVXH/jfv+6MKW0hY+iylcX65NVVgiZK16tLj4Nis0BHQ2ZZFqPhq409Jytf/hyEla/VIq9YkeHIFEWtmPXVpkRdTVYun9Oz0LFd+dTg04pNDDk4pmrvaj2QDIwwCNrlgPFoRjUfmqZFIYZ+yAg8Sjg0vyp5ZURTALVCAGTYUL+bX/epHo5BGfG+2DEKtuZhsencF9GmsZVW67V/iHersF0pMBr2tK1pRKeI4W6i5fNerM8pwfVCCJb3RF3bSMX5sTo81BofM9fkirLAEeb6U1PJ1Q+CqfETkiL/Pg6nxB9CWk69LB9qFFMm1jzBuEUBq/co1dCG1Zl0cGYeXuY4H3f0pSWI9h+EfvCVFxHl+vMOdAUc1uKitwBA00wb5lpFTuISOoRh9NqzytusehhOZaRplZ81i/IvprQznx+9u787Zvw5NFZbalbJrkxPDpgnnqKg37lTJnC1FV0pDzlwAFX0fJtIXbR1eS/+BEDJlHZ/mhqyQzfNlIkWzfjSe745hvVwu23YULxfJsVvEw11EehURsePZMD1wFCpzulRTreFn91FFw20UP4WEBaF35f2im/E14U9cyUYA++cU6HD3m79trH8s+tNgt7Y28cIBQiFqyquLwSux23zY9vX71wBkMxzx4vVTRwHH2OSYWwHx8+Q82RIWhs0E0xWQzJbK/WpR2jjEUX/mDzuKTu2hb8zF/TviM4FZ64JOMQyOj8VOjTRolnuzmsZk/a1orQ7AOFNtEnpOBPqOpLo+yZaivcMqXjgdIgmrDJ45LniSH2MCSTtV2FXEQ3pEHVQ0C05+ZDnfONWznIFpJICUdvj0Ztb+D6tsXwFPmdVXsoOCJIom/sPrfHoz5AKKOEvSBRL0jcCxL2QslMVmwKVZeMMKhzOqmuy4appZM69A5oSnol9sJEQ6hA6LCUbIiKqe8/rQAfEE3BRh3KvmxIDSuLeUQ8Vq5qVTIp/oFTY4BQvsyqvI6my73FESiKsXtvciMbIBjIHKhY7uh+J4UxDkV541EWgon3wxljYCRkiw5ciIcCGQ6HHAOzxMBGyRCPJMoPQychN3QkzgwDKTErNK8Nj0S93c+kyXKZs+ZwA3fMwz42XVtGY2c6+uLs5DFv3n1/9q0f+naXOf2UgP4YSbLbCJBnBkgdQHMlW/h/WnZXP/IKuLpymim8OZub5tdlCybdsIyjF3RAynXFdyITNqt4vbNV47qQSRr7i+lpnhooQyjl/Km/FIDjOqVS1muWBQe+73JlNMMH1jQcehyRstlNshu5LRjakCEYr1kp5OuizBUTPN1NCuz+CZRy3kSvR3zhzeKtCLbBVkbAlBv6qA6VL02RrihV/9a7z4HtiYAp92/jJxy0ZmdYyrvN7BHzJ5Pt0mT2gKRr+DaEmjilUtY7/xtRtxTEUlujaNIf3HcGutkN57z2H5XY2dA3uSfMuZTZ9rt8YNlj3NeXxi1+4NIaISOBS/czKXBhsilYLIxyiq4YBGQP6hAJ8mgg5gw9kbqlGs+4ZdcERB9wmXpErXLl+AID5HlOMjIJkU1YpSjtumBqKVgfjRM4pV5OTzxlMjWx1PXh8ObauLQdCkOUqKW0dArT1Kku9TiaECZCMy0lGbJ4JBSISBcigqkMplWDjJJSWexXzhRt4AOSJl368j1zJr58+yRhJk1Ykjg1j0aMpKPNoikMLxEvs67njciWnT4FZZu6BpubK4eCv5iKXvj0gkM5EBy+Wy59j3SKnoB/15LepzVln9akRaXRorr/cv8fphCr05IlAAA=" \ No newline at end of file diff --git a/docs/assets/search.js b/docs/assets/search.js new file mode 100644 index 00000000..bb44c27b --- /dev/null +++ b/docs/assets/search.js @@ -0,0 +1 @@ +window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAACsVdW5PbtpL+LzOvkzkCePebj7OupPZkT3btPfsw5UrRGo6GsW6mKNvZVP774sJLo9kNkqLG+2JrxG50A1+j0fgIkX/eVIevp5tXD3/efCr3jzev5N3NPt8VN69uNsW+qPL6UJ1u7m7O1VZ9tTs8nrfF6W/9pfvnerdV19fb/HQqVEM3N3/dtW3FYdfYY3laH74U1a/5tqjrom/y6bxf1+Vh7zSKpQkrdzfHvCr2tesoafv5XLx7Lp9qv81WapmtY15WxeO79XOh/vTag5JLbZ7qYjtmTcsss3Oa0KvTxf2JoiDuTP32W/3Hcbqp2+ZzpzZmuFVuukR2t9zXRXU8bPO6eHfclvsRdwbiywa7b+5QTTV8qJbZLPKqfq4PYz3txC6w1qeXc11uh5nFfDs1qWyK+j+UM8WpfnPYksNkm0NyfretX6S9tdb+sXgq12WxX//B2kNyM+1Nmwi8pVv85cisoFvCfSUH5GNVbp7rYs/61wpcPOTl6fX6uTrs8rpcs1ag0MWW8u3xOWdNmKsXt/2YV588o2QvX9y6Cu+3ekqq+P6HHu59caLW124uDISXWG4m1iTDWPZiu7o2YPKUNdYKXGNM35jQmjKgVvIKozlu0hG82OKpUC3k+z1ZPVhbvcgLpjFk5Bb8PSl5AXnQJa7L/zjvyn2+X3v96YSWwDk2uJvvMbjIyO1m5uAC+c3I4G6mDO7mWoN72NeVKmu9HW9krpIHJtjDstfIBRPMItEFq+2bw4EPV3t5Qev/k1c7T+v68pIR++lcvM135ZavzKDQstjbqRp/p0Tz6g/Voj8GXdlrxOKISSB2hQgcMdZLzbMl3bp6uBewX09nGNSavyn3G8LXpqVOxO9o4w5p5fM535a1KjS/UGPS2AFCl1s6FZ/PSqrMqfnYGOplLreDBn9gg0VhYvt1Xm6/qi/fjNhx5ebam7Yg8qZu0XcjKyPdEOpq756Qaeecufi6qvI+SzU2Wvd6iZmjEAk4p/anujqvIX3gt3Prqng7DjoBJvQq7LvJsycjXlCKC31hyccRVwi9hZ4wKXzEj4HWci+I1D7uhKN0NR+GW8fJnkDV6yFziUOU7kKPnsqtmgl//+NdXp+rXCewqf6QmlfyZvbgUIpX8uXH8lQ7W42JrgC9K3kyKNInegL0ruTJjBntqlwrPgb7v6nxARQX+nI6VPUFkTpUu4ofc6N0oHWd0ZiLy1DtKn7Mz2aE3lU8mTtnB1pX8WLGfIUKC20fzvXxPLnnnfR8q2AjsD3kj3xNrq/OLHcHVbW3P9+hlr6sjOYwcm9I8PZaucstEUwlb84RvtwmQT3zNk/XsfmlnDKeVupyK4ObYrwpIHq5PXRzibfWCV5uqz78VHybYKqVu9wSOtTAm+oEl9giDm34LDril9sdHEzhbQLRZfOcIy79E36odbkXw1MGvG0oe7nF9ZSVvkve05d3zt52Uq1lDW5nFVgL1vPZSzmbuadVcE3mnle2sWwSdQyAt4rEl9h1bln4LDaCS2w5N198thrBBWvG8BCLZ92Awouyz1Tu62LaaznjdSnZNW4Z3dSfYrxTuYb9afvhZRTbBOznDMNA5yoezByIy6g9NlN/KSaufb3kol5PDvilkX6aWj+dLqqc8C0rfitp/vtBzNzwOds9fXyoIG9Jtpem3pbcn3ey2nwkve3aaoTGPO7cIi2pBqRqyG+pEVpmqS52R/mGBaEz1ssttHcwjbw/H7f0Xb3eIpBcZvO8VqtsI+K3CSWX9hPurpgOKpELrPTR3fKtg9huL0yNbJb77h3vmsSy/h50LnrtDnhVj91W9hp2fSecBoYnnnSaZpngTj2me+lr2KYPgQyMjp4CmTjGnuOkwzGedp6UsNzPCsutDuaE/XrqjGB45d71pjlXzu9145jHnmceOPamzYFxez5sHIMTcRm36I17x+TUmB+3Sce7Y2w01ieMpSeXuGM5LY8MLPYRrs+zDAsa8603vuUqS0TUt0MsxaaRpqmp6691Z8TMPz/+Xqxr3o69Ps+Q96iQz8LtyJmggRa8VbGSPfRq7GdZvG8UJpptzy4x1t3bClPM+28wTLBPY3s0Ye7xxAq8KLrAxBx4G9cvHOGh0RlD3Cj7Md4OUvQkL7ajO9zZnugtgP4V2LmaB8O9q3glb+ym75ivZzrT6/0grojSIPlPQ2mUMZ7tCUHpTnJlArt7Wez+4k+RXOz+MjVRTvJE3y+b7wjQulrUooJyYsyO3Mm4bPa8u3D2tHqX+0KuIm1RO7qaIMGXXFUoUzNWF9wlBo2vhY74mT1uIemV57ozEiP+2sbn0tQiZ9QfHCevH/OjPorfnOL1RQot+kKx4jE2LVqYjjHIfMzXnzbV4dwcT7/QpfthM/NdHGJ2xUHDHjbGRhdur6uDbvsWr2s7ft+2ejX3Jyx8V+9E0+j36sOXsvha7jdvDvvH8qIJ33aIaugaIT9cWv++1RcudNNp4Rr+4TT6M3jMhC+HEnLLmACfsZfN0PNT8kgOLvKTCqW35/16qr17R2WC6QW51W9+ah5Fsxb4zwzK87n4mXrYyZhrQ72Lhsedh+ZG80XukKrLPVK9fFt+Ox/nDEur8P3DpTXeBkswL1g638d4jYsQ4rSXgwQPlo554TteOjMw3tXFrLho5Jfb7o7ljWflPpEAleUenPLdcTvDfC+/3Pau3A/vgYw5gJSu4EX+7QIvXKWLvMDr86/gOKxvrSbklhUG/9aGlM8qFlpm8qfmGK7PIpJZZnA00f08+elfF5dBAxPTaiHH9Sua7bcUzQc5w5F7pw3PwL+Hh7SgW+9HHys0hmn/VK33XN9dkcUxi56KgaJ1yuMwRvvUPloBnaJ3OuXKLDP4n/0zFjwmB1LLjL7rnrfgsYmFrnAf9P0B/sIDGusvLzPzNl9zGcZeWriHqx4LplDTV5Y1vs63W00OkO23F68xPL/kxyPTDSjwQml4YGJaGnZcv6LZ2WnYaWhqGv7vffl0qHZvulsI9Jo7FLvCrDvx9sD1xcl5rG+uyMIEpqZD8a98e2ZsgevLDL2Hj0IxC0zJmCQlZxrvN0XF9kid222+n3qQq/h2rFSFrMq3X/PqRB4AbVvEon7PW/9Iq7u8Xj935Tn/7LjWNCm/1L49aj/RuCO8xHJJnezqTJUji9tIr6gjtX03Rk7Tetten0/1YffuUFGH/VoTvdBsS9OeasQYugV/j6wURAuga/wZ/H9+/P115ZsdQOqlO48s3cIvpnYfqMD+sQf4ikd/9zuZF+68a+e2/3Nix3uFvlee87lj3QZSL9xxbOkWfjGx81AF9s8z49+qr/+9oB4z6M6kRm44CPXp8Yfy9MOxKr/kdTE93zRBOWEefKfp5s61uROt+/BCjsyoUVnX2FJ1eAPvem7et+0tcpYg93Ekvzns1/n46mXFFqyU5f6/8v3GB2QjscBGle8fD9Tvn1oTVmBJL8ytg423IOxkFth52h4O1Zui9BVgncwCO/nj72fu+Z6tnU5mSZ1kK8Qfy6cnlV3p4+5dwGHZl85kpL3b4ddTc9tAcdh7cpC2vmm4XTL5Nr6WN8ta9g2turqg7a237e2itovPvi3d5wUt7w/VLt+W/+vzvZNZNKWK9afXFfW02z4SrcgSfAtzpnZ8O+gKzrUYSLBQ9GS9WnWeyr6HX/KqzD/C7fxQ+KrJYrLBMSaOV59xUmSuN1OOjnhamncwYIFz4wdLRr2ccMrEc7Jjtu8zjnrMGWD/uY/ZXo4eBPn/A3/6MZFx6MfOjHDHiy5x+mUh959ume3vvOMus4IBc4aHnWqHeVOVSxh2kkt5yh/LTVlTP0F17FmpubZCkYXBauRZAMy78G6RMG0Tvhvrw51a9B6Lbzev/rxRappKVhLyPrjPlMpTWWwflfqDdUY1eNjpB3HdfGiu/atYm3ZePViRv61u7h5Wd9HqPo7TDx/uHloNc8F8YcRUun0Qd2F0H8WOlHCk1Ib5QRJS0pFS8/chIKQCRypUf4WEVOhIReqviJCKHKlY/RXfSXEfpcIRix2xRP2VEI0ljlSq/koJqdSRUmg8ZIRU5g6rHmVBjr9AABgEBCnpgiD0aAtJSrpAiIAbF+FiIfSgCwoz4cIh9LgLCjbhIiL0yIvoLsju41S6ki4oQo++iKkmXVyEBkAo/OS9yCJX0sVGaBBESkq6+EiDT0aNpXTxkRoEuaKCH80RM0mouSRddKTGQJLTyUVHhhyM0kVHagxkQPbGhUdqDCSFo3TRkQlr20VHaghkRNp20ZEaAhmTki46gYZAJpRk4KITGHRSUtKFJzDwUDM3QFnMpDEK8MCFJ9AgBOTMDVx8Ag1CQCEeuPAEGoQgoAI4cPEJNApBSEq6AAUahYDKpYGLT6BBCEh8AhefUIMQkPiELj6hBiEg8QldfELJuRm6+IQGH3LqhmilYedP6OITahRCMluHLkChRiGkJnno4hNya07oohOmzAIcuuCEGRsZoQtOtGIjI3LBiQSbLiMXnEiyKThy0Yk0BCEV6pELTmTqAGrpiVAlYMChUlbkYhMZbMiiwcUm0hCE1MoTueBEGoOQQjFy0Yk0BCFVPEQuOLFGIKSyUOxiE2sAIioLxS40sR7/iIqg2EUm1uMfUcjELjKxHv+IQiZ2kYlNkUYhE6MyTY9/RK4RsQtNrAGIYirQYhebOOVmYuxiE2dsRRK74CQrbnFMXHASwa3giQtOYsAhS08XnCTgCrHEBScJuaopccFJDDhUSCYuOImZNpQcKqLNikPJucAkpiCg5FxckoxbFBMXllQPPjVlUheV1EwZUtBFJdVjH1NzK3VRSQMuwFMXldSsNFQwpi4qKVtHpy4qacxVIakLS5pwdU2Ktjd6+GNy+5C6yKT8WpO60GT8WpO54GRmylAjlLngZJJdlDIXnczU0dQQZS46WciuXpkLT6ZBiMlCOnPxyczWMyQlXYAyk9IoL118spRNfRnagmoQYjKdZngbqlGIyZrOXoOyGoiYrOrsNShrphBZ19lrUNbsRsmCzV6DshqPhMLUXoKibIKzl6CoRiSh99grtCtdJVwVYS9B0ZSrTOwlKJpxlYS9BEQNP0BWUGJAHQiukBGYOzAMAVluCUweGI4goXkGzB8YmoAsKgRmEAxRQJY+AnMIhikgyymBSQTDFZDlj8A0giELyLpGYB7BsAVk9SUQkSAMXZCQKUMgKkEYxiAhk4aQmOzRqCTkJBeIUBCS53sQpSAMc5CQIYNIBWGoA4bKQbSCMOxBQucOxCwInloQiFsQhkHgXECYGQ4hoVMS4heEYRESOiUhhkEYHiElYwFRDCJgCwsRYIYu4CoGgWgGYciElM5eiGgQhk9I6bmLuAZhGAWyXhKIbBCGUmDGAEFmSIWUng+IcBCGVmB4SoSY4RVSeuogzkEYZoGOcUQ6CMMtpPQsQ7yDCPlZFmJa1UBGL7uIexCGYWCaRYgZjiGlZxniH4ThGVJ6OiAOQhiuIaWnA+IhhGEbMprZRkyEMHxDRkcu4iKEYRwyOnIRGyEM50APGaIjhGEdMjLpI0JCRCxdJCLMhmtgMpoOR5gZ7iEj6wlESwjDPmRk3CJiQhj+gfEVAWYYiIwsPRA5IQwHQW5XBaInhGEhMjq8EEMhDBGR0eGFSAphuAhVmtLCCDHDR6jalBZGmBlOQhWntDC+i2FuY6zoNIY4C2GYCVWf0sIIOcNOqAqVFkbYGYKCrIMQdSEMQ0EHBCIvhOEoGFGEnGEpVIlMOosoDGE5jBWdnhCNISyPsaIDCFEZIrHQ0RGE6AxhaAvB3HtL8D0oAx1z+w0RG8IQGIK5A4fYDWFIDFUv08IIPcNkqIqZFEY0h0jtHUM6ghDVIQyjoapmWhghmPIrHCI8RMonS0R5CMt5CDouEO0hUoseHReI+hCpRY+OC0R/CENyqJKcFkboGZ5D1eS0MELPUB2qKCeFEQ8iDN0h6Jt1AnEhwpIhko4LRIcIw3oI+lacQJSIsJwIfTdOIFZEGO5D0DfkBCJGhKE/BH1PTiBuRGT8XgDRIyKz+NFgI4ZEZHxliSgSaWgQVfXT94LRzeAVmzslokikoUEEfX9QIo5ErtiZJxFFIlf2nj0ZbRKRJNIwIWpHQQuje8Mr9jiFRCyJXPG3hxFLIlc8ryURTSINF6I2NbS7CDh7xiKgbz0jqkTaYxb03U2JyBJpT1rQNzglokuksNQ9Ge8S8SWyOXBBHzxAjIlsDl3QkYk4Eyl48BBnIu3Ji5COTMSaSHv6IqTDDfEm0h7ACOlww0cw7BmMkAYbn8Iw9IjaENHC+CSGwS+kwcanMQxDorZEtDDCz3Akak9ECyP8LIES0mDjgxmSxw8fzTAsCbmnkfhwhmT3CRKfzjAcCblRkYg+kYYiITcqErEnMuATJmJPpKFIyE2VROyJNBQJuaeRiD2RhiFRu00SBESfyMBSXvTMQPyJNCQJyVXLhj8xZ/rMGcDHn+3ZvoeH7tTsnze/NQf+uhO1f97E6p+/7m5U2jD/q4iy/yf2/7D5W9N6zYek1ciaD2F7KYyaD2pP3Hxo1dUe0X5Qe4Pmg2g+pK162gpnrYmsMaGXuuZD6+cqbj80wjrp2A+y+9AY1cCrD3/1hxj1X3r88uahlEf7UMpD+3ilfqjSqB8rTdBw7egfKz2fnVHWWbfT1bmW0bXP4Ad60GTTl7gdbNmOse0c0V7/vNLmx3ygMzHsDOdQ/3Zg2BeoaV1IAqaB/sEooAGnVykHSPObpUfwiy3QhlqZwIBGbBvF+lOuf6ICVROgKlnPzQn70jm43DeSwS5EXZRlXGN4+CM475qwzdoYXXF4mmZy835z0BYcipAdCa360T6cFXRjBbH0eg9fwQkGU4IGuClh9ZuHZlMTC0ZU4O38Qf/yGMYiUNU1pV81ryrXeTArRTZm2LwHptdOYAhIb9+JPicJ1I592o5eAKwGTcCwuQi+bAIMNwQ9aHNvkIw2cnJncQpnsbcHtX14FFSGcZN4R762L/oBQxfCoWPThxrwujqv0cRVlWc/WdqMyttvX2AAZi6ImbQbPL77/YscwaILQMzYWWd+9rxufh0NYz6EMe9Xf1JKnwp3ygYS6nOp3+qfzINF4JSBqGdct+2TtkHIQa2Qi7T27fDAHFylmjSZcB5rdfsIf2AYxlnA5Qc6t8VAN+VCpFfFlZVmI4Hz3Dqj8uL6oGKkqTvcOQYaaKY660f7ZDsnWYDhC7hA6TTbl6LCLsAEmXDDB16RDpYWEOLW93Q1pk/kSbGCsz3mQgc+2xX4AN2PuSxpf7n3ZH6kCHThohS1taZkHfjs1hhghks2R/VPbjo2D3mCMw06kHFNPOU4wwmnQEs42K3irnmeHFSHcy7lBs0+puSjM2ESGK8rLje0mlRyhcPWVlVixY1529Jj9/pB0BIIm6gtlNnCqm0Jle0hGIqo3YkIFoqmEfAOK9AUAKXbCrEVU9dU/6Il0BTIK91mil0H2qbgi5JAWyDHtUGuj/r42ioecR0VCLiisJ3Sj4tYm0dKQF2wNEjBZUnwazWgDAoZzuVNUa/bn473ihIuoU2cJb4mDrvjttTf5hUOEricBk2ksbPOtDWMewkCjd1EKN2n5kXg6+Zt5KAFZz9kvcj40exborwBARZ4RqVtAw8ICPWgGdywXQMmdI6cPwIupk3Qt9xCxlUEqlHkWwyAZ6sXq/bUPLEWdAyEeeCxSc5aCVcELg8pZV3BEBErQjhPpMf6Xq2FdIjAWdoOHZfMQEN47ypgmp+iT0QY6AzLyPRN4AADIAZNWo4aMinzxGvTGh1fTl3btNnOIy6jbdzaGCZTKViMa8RkwC2s4OZH91hJWCHA4pbtuBo8dgsH66OUW2bJuaBvn4Mqg4tHrWufAAAqK1hgtMuqZBONaoJlYWBt2K7ymrBlWzrpx6I7kQgGv8ndHHCtOlmlOkSdrwXzAgTQBTiMMTcVyn1ln10FF06YUNglux+64qSWMLdGBzm1JXm5IOAgAO63pUjMxTBsY908WAVOBMiRSm6zARshkMhgcorYUTnlNj/W5dr1AWo3Sxbridq4HdyyAvQgaMKJnVblqWyf5AVRheQkWx+Wp695tXNyIlj/g6aQY0mZ5m1eAEW4uoZetcEOGw44uyqSOTeBsc+WPZ0qmwWg81F7nyDgip/tIXc40AjMo5jtOsr0cCkVHMJbnOlh1Sm52CSrhxjAm7a3VNhEZx48YQOcKiVgKSLYItE2Ap6rAW9LwGqTBd208Ng8JQNqw9FjSRmj3YFP9gPOVnaRNk/fhVowXDLe+je6TljBRTdm1UuXx4IFvmDrVaXFGIWTLGbV0dRMoNH2dp4+hkyrg+eGwaCFWxOW5t6fd7LafHRCFlpfcdFOpXA4whHn7cE+zx9WMzAcWLLpcK6PZ5fRBXpxm/S5WaGpOkx1wTKKndZKEevFoPJjA/Fo3lkz5ObgJqspXLg8BJsgixdIGLEp8KhqeHcCgnFr7wNyyp/7t2A4nQBzMeRGDugSFKWAdzNY++2DOeFKCwOb3ceooJYquB3YYAJZcYrd657gQENfYy7EaKYmBimguz3OLnIn/RT9L81T9mEegh6wpPZwmCEvZ21nXLSdupePOEiDtZ7NQL0qBTTMgWysn2i+B5b57XThvWB28HCjxk1XfcMEsaIwnbEEl9UjbzgB/bij6Lg1wLZDMqIgS8Xt0Qv2zrltB228IzACcUdislFkmqDZUBANbcUmBDd5m4YoTGAhErd349hNtG2Inl+wKInbCost7exDygc8KCyL2CWvbl43YXblT92LKeAshaHOUmZOO+5NYgB1yEVcXeyOckDuxLAmZ6lcrav52HPlFhyQT2A39PWBvq8bQ+0V6/bhuXAKugh4nHRMPxfXg7V75dxiZDts3vcBuwrXTHbRP69P6/5JbEAbpuFVM5Mkexf5bN8tw9I4DjfK9fxcl1u0HEEnGC3zrkgw2JAyaGdJzCubFxevwYuLwcYRws3e1+xeEw8UYZ1HVw0f7m6O5bEwfMerhw9//fV/SOYOG2W+AAA="; \ No newline at end of file diff --git a/docs/assets/style.css b/docs/assets/style.css new file mode 100644 index 00000000..108428c3 --- /dev/null +++ b/docs/assets/style.css @@ -0,0 +1,1383 @@ +:root { + /* Light */ + --light-color-background: #f2f4f8; + --light-color-background-secondary: #eff0f1; + --light-color-warning-text: #222; + --light-color-background-warning: #e6e600; + --light-color-icon-background: var(--light-color-background); + --light-color-accent: #c5c7c9; + --light-color-active-menu-item: var(--light-color-accent); + --light-color-text: #222; + --light-color-text-aside: #6e6e6e; + --light-color-link: #1f70c2; + + --light-color-ts-project: #b111c9; + --light-color-ts-module: var(--light-color-ts-project); + --light-color-ts-namespace: var(--light-color-ts-project); + --light-color-ts-enum: #7e6f15; + --light-color-ts-enum-member: var(--light-color-ts-enum); + --light-color-ts-variable: #4760ec; + --light-color-ts-function: #572be7; + --light-color-ts-class: #1f70c2; + --light-color-ts-interface: #108024; + --light-color-ts-constructor: var(--light-color-ts-class); + --light-color-ts-property: var(--light-color-ts-variable); + --light-color-ts-method: var(--light-color-ts-function); + --light-color-ts-call-signature: var(--light-color-ts-method); + --light-color-ts-index-signature: var(--light-color-ts-property); + --light-color-ts-constructor-signature: var(--light-color-ts-constructor); + --light-color-ts-parameter: var(--light-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --light-color-ts-type-parameter: var(--light-color-ts-type-alias); + --light-color-ts-accessor: var(--light-color-ts-property); + --light-color-ts-get-signature: var(--light-color-ts-accessor); + --light-color-ts-set-signature: var(--light-color-ts-accessor); + --light-color-ts-type-alias: #d51270; + /* reference not included as links will be colored with the kind that it points to */ + + --light-external-icon: url("data:image/svg+xml;utf8,"); + --light-color-scheme: light; + + /* Dark */ + --dark-color-background: #2b2e33; + --dark-color-background-secondary: #1e2024; + --dark-color-background-warning: #bebe00; + --dark-color-warning-text: #222; + --dark-color-icon-background: var(--dark-color-background-secondary); + --dark-color-accent: #9096a2; + --dark-color-active-menu-item: #5d5d6a; + --dark-color-text: #f5f5f5; + --dark-color-text-aside: #dddddd; + --dark-color-link: #00aff4; + + --dark-color-ts-project: #e358ff; + --dark-color-ts-module: var(--dark-color-ts-project); + --dark-color-ts-namespace: var(--dark-color-ts-project); + --dark-color-ts-enum: #f4d93e; + --dark-color-ts-enum-member: var(--dark-color-ts-enum); + --dark-color-ts-variable: #798dff; + --dark-color-ts-function: #a280ff; + --dark-color-ts-class: #8ac4ff; + --dark-color-ts-interface: #6cff87; + --dark-color-ts-constructor: var(--dark-color-ts-class); + --dark-color-ts-property: var(--dark-color-ts-variable); + --dark-color-ts-method: var(--dark-color-ts-function); + --dark-color-ts-call-signature: var(--dark-color-ts-method); + --dark-color-ts-index-signature: var(--dark-color-ts-property); + --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); + --dark-color-ts-parameter: var(--dark-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --dark-color-ts-type-parameter: var(--dark-color-ts-type-alias); + --dark-color-ts-accessor: var(--dark-color-ts-property); + --dark-color-ts-get-signature: var(--dark-color-ts-accessor); + --dark-color-ts-set-signature: var(--dark-color-ts-accessor); + --dark-color-ts-type-alias: #ff6492; + /* reference not included as links will be colored with the kind that it points to */ + + --dark-external-icon: url("data:image/svg+xml;utf8,"); + --dark-color-scheme: dark; +} + +@media (prefers-color-scheme: light) { + :root { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + --color-link: var(--light-color-link); + + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + --color-link: var(--dark-color-link); + + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } +} + +html { + color-scheme: var(--color-scheme); +} + +body { + margin: 0; +} + +:root[data-theme="light"] { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + --color-link: var(--light-color-link); + + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); +} + +:root[data-theme="dark"] { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + --color-link: var(--dark-color-link); + + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); +} + +.always-visible, +.always-visible .tsd-signatures { + display: inherit !important; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; +} + +h1 > a, +h2 > a, +h3 > a, +h4 > a, +h5 > a, +h6 > a { + text-decoration: none; + color: var(--color-text); +} + +h1 { + font-size: 1.875rem; + margin: 0.67rem 0; +} + +h2 { + font-size: 1.5rem; + margin: 0.83rem 0; +} + +h3 { + font-size: 1.25rem; + margin: 1rem 0; +} + +h4 { + font-size: 1.05rem; + margin: 1.33rem 0; +} + +h5 { + font-size: 1rem; + margin: 1.5rem 0; +} + +h6 { + font-size: 0.875rem; + margin: 2.33rem 0; +} + +.uppercase { + text-transform: uppercase; +} + +dl, +menu, +ol, +ul { + margin: 1em 0; +} + +dd { + margin: 0 0 0 40px; +} + +.container { + max-width: 1700px; + padding: 0 2rem; +} + +/* Footer */ +.tsd-generator { + border-top: 1px solid var(--color-accent); + padding-top: 1rem; + padding-bottom: 1rem; + max-height: 3.5rem; +} + +.tsd-generator > p { + margin-top: 0; + margin-bottom: 0; + padding: 0 1rem; +} + +.container-main { + margin: 0 auto; + /* toolbar, footer, margin */ + min-height: calc(100vh - 41px - 56px - 4rem); +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes fade-out { + from { + opacity: 1; + visibility: visible; + } + to { + opacity: 0; + } +} +@keyframes fade-in-delayed { + 0% { + opacity: 0; + } + 33% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@keyframes fade-out-delayed { + 0% { + opacity: 1; + visibility: visible; + } + 66% { + opacity: 0; + } + 100% { + opacity: 0; + } +} +@keyframes pop-in-from-right { + from { + transform: translate(100%, 0); + } + to { + transform: translate(0, 0); + } +} +@keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; + } + to { + transform: translate(100%, 0); + } +} +body { + background: var(--color-background); + font-family: "Segoe UI", sans-serif; + font-size: 16px; + color: var(--color-text); +} + +a { + color: var(--color-link); + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +a.external[target="_blank"] { + background-image: var(--external-icon); + background-position: top 3px right; + background-repeat: no-repeat; + padding-right: 13px; +} + +code, +pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 0.875rem; + border-radius: 0.8em; +} + +pre { + position: relative; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + padding: 10px; + border: 1px solid var(--color-accent); +} +pre code { + padding: 0; + font-size: 100%; +} +pre > button { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.1s; + box-sizing: border-box; +} +pre:hover > button, +pre > button.visible { + opacity: 1; +} + +blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid gray; +} + +.tsd-typography { + line-height: 1.333em; +} +.tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; +} +.tsd-typography .tsd-index-panel h3, +.tsd-index-panel .tsd-typography h3, +.tsd-typography h4, +.tsd-typography h5, +.tsd-typography h6 { + font-size: 1em; +} +.tsd-typography h5, +.tsd-typography h6 { + font-weight: normal; +} +.tsd-typography p, +.tsd-typography ul, +.tsd-typography ol { + margin: 1em 0; +} +.tsd-typography table { + border-collapse: collapse; + border: none; +} +.tsd-typography td, +.tsd-typography th { + padding: 6px 13px; + border: 1px solid var(--color-accent); +} +.tsd-typography thead, +.tsd-typography tr:nth-child(even) { + background-color: var(--color-background-secondary); +} + +.tsd-breadcrumb { + margin: 0; + padding: 0; + color: var(--color-text-aside); +} +.tsd-breadcrumb a { + color: var(--color-text-aside); + text-decoration: none; +} +.tsd-breadcrumb a:hover { + text-decoration: underline; +} +.tsd-breadcrumb li { + display: inline; +} +.tsd-breadcrumb li:after { + content: " / "; +} + +.tsd-comment-tags { + display: flex; + flex-direction: column; +} +dl.tsd-comment-tag-group { + display: flex; + align-items: center; + overflow: hidden; + margin: 0.5em 0; +} +dl.tsd-comment-tag-group dt { + display: flex; + margin-right: 0.5em; + font-size: 0.875em; + font-weight: normal; +} +dl.tsd-comment-tag-group dd { + margin: 0; +} +code.tsd-tag { + padding: 0.25em 0.4em; + border: 0.1em solid var(--color-accent); + margin-right: 0.25em; + font-size: 70%; +} +h1 code.tsd-tag:first-of-type { + margin-left: 0.25em; +} + +dl.tsd-comment-tag-group dd:before, +dl.tsd-comment-tag-group dd:after { + content: " "; +} +dl.tsd-comment-tag-group dd pre, +dl.tsd-comment-tag-group dd:after { + clear: both; +} +dl.tsd-comment-tag-group p { + margin: 0; +} + +.tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; +} +.tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; +} + +.tsd-filter-visibility h4 { + font-size: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.5rem; + margin: 0; +} +.tsd-filter-item:not(:last-child) { + margin-bottom: 0.5rem; +} +.tsd-filter-input { + display: flex; + width: fit-content; + width: -moz-fit-content; + align-items: center; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + cursor: pointer; +} +.tsd-filter-input input[type="checkbox"] { + cursor: pointer; + position: absolute; + width: 1.5em; + height: 1.5em; + opacity: 0; +} +.tsd-filter-input input[type="checkbox"]:disabled { + pointer-events: none; +} +.tsd-filter-input svg { + cursor: pointer; + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; + border-radius: 0.33em; + /* Leaving this at full opacity breaks event listeners on Firefox. + Don't remove unless you know what you're doing. */ + opacity: 0.99; +} +.tsd-filter-input input[type="checkbox"]:focus + svg { + transform: scale(0.95); +} +.tsd-filter-input input[type="checkbox"]:focus:not(:focus-visible) + svg { + transform: scale(1); +} +.tsd-checkbox-background { + fill: var(--color-accent); +} +input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { + stroke: var(--color-text); +} +.tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { + fill: var(--color-background); + stroke: var(--color-accent); + stroke-width: 0.25rem; +} +.tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { + stroke: var(--color-accent); +} + +.tsd-theme-toggle { + padding-top: 0.75rem; +} +.tsd-theme-toggle > h4 { + display: inline; + vertical-align: middle; + margin-right: 0.75rem; +} + +.tsd-hierarchy { + list-style: square; + margin: 0; +} +.tsd-hierarchy .target { + font-weight: bold; +} + +.tsd-panel-group.tsd-index-group { + margin-bottom: 0; +} +.tsd-index-panel .tsd-index-list { + list-style: none; + line-height: 1.333em; + margin: 0; + padding: 0.25rem 0 0 0; + overflow: hidden; + display: grid; + grid-template-columns: repeat(3, 1fr); + column-gap: 1rem; + grid-template-rows: auto; +} +@media (max-width: 1024px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 768px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(1, 1fr); + } +} +.tsd-index-panel .tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; +} + +.tsd-flag { + display: inline-block; + padding: 0.25em 0.4em; + border-radius: 4px; + color: var(--color-comment-tag-text); + background-color: var(--color-comment-tag); + text-indent: 0; + font-size: 75%; + line-height: 1; + font-weight: normal; +} + +.tsd-anchor { + position: relative; + top: -100px; +} + +.tsd-member { + position: relative; +} +.tsd-member .tsd-anchor + h3 { + display: flex; + align-items: center; + margin-top: 0; + margin-bottom: 0; + border-bottom: none; +} + +.tsd-navigation.settings { + margin: 1rem 0; +} +.tsd-navigation > a, +.tsd-navigation .tsd-accordion-summary { + width: calc(100% - 0.5rem); +} +.tsd-navigation a, +.tsd-navigation summary > span, +.tsd-page-navigation a { + display: inline-flex; + align-items: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + box-sizing: border-box; +} +.tsd-navigation a.current, +.tsd-page-navigation a.current { + background: var(--color-active-menu-item); +} +.tsd-navigation a:hover, +.tsd-page-navigation a:hover { + text-decoration: underline; +} +.tsd-navigation ul, +.tsd-page-navigation ul { + margin-top: 0; + margin-bottom: 0; + padding: 0; + list-style: none; +} +.tsd-navigation li, +.tsd-page-navigation li { + padding: 0; + max-width: 100%; +} +.tsd-nested-navigation { + margin-left: 3rem; +} +.tsd-nested-navigation > li > details { + margin-left: -1.5rem; +} +.tsd-small-nested-navigation { + margin-left: 1.5rem; +} +.tsd-small-nested-navigation > li > details { + margin-left: -1.5rem; +} + +.tsd-nested-navigation > li > a, +.tsd-nested-navigation > li > span { + width: calc(100% - 1.75rem - 0.5rem); +} + +.tsd-page-navigation ul { + padding-left: 1.75rem; +} + +#tsd-sidebar-links a { + margin-top: 0; + margin-bottom: 0.5rem; + line-height: 1.25rem; +} +#tsd-sidebar-links a:last-of-type { + margin-bottom: 0; +} + +a.tsd-index-link { + padding: 0.25rem 0 !important; + font-size: 1rem; + line-height: 1.25rem; + display: inline-flex; + align-items: center; + color: var(--color-text); +} +.tsd-accordion-summary { + list-style-type: none; /* hide marker on non-safari */ + outline: none; /* broken on safari, so just hide it */ +} +.tsd-accordion-summary::-webkit-details-marker { + display: none; /* hide marker on safari */ +} +.tsd-accordion-summary, +.tsd-accordion-summary a { + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + + cursor: pointer; +} +.tsd-accordion-summary a { + width: calc(100% - 1.5rem); +} +.tsd-accordion-summary > * { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; +} +.tsd-index-accordion .tsd-accordion-summary > svg { + margin-left: 0.25rem; +} +.tsd-index-content > :not(:first-child) { + margin-top: 0.75rem; +} +.tsd-index-heading { + margin-top: 1.5rem; + margin-bottom: 0.75rem; +} + +.tsd-kind-icon { + margin-right: 0.5rem; + width: 1.25rem; + height: 1.25rem; + min-width: 1.25rem; + min-height: 1.25rem; +} +.tsd-kind-icon path { + transform-origin: center; + transform: scale(1.1); +} +.tsd-signature > .tsd-kind-icon { + margin-right: 0.8rem; +} + +.tsd-panel { + margin-bottom: 2.5rem; +} +.tsd-panel.tsd-member { + margin-bottom: 4rem; +} +.tsd-panel:empty { + display: none; +} +.tsd-panel > h1, +.tsd-panel > h2, +.tsd-panel > h3 { + margin: 1.5rem -1.5rem 0.75rem -1.5rem; + padding: 0 1.5rem 0.75rem 1.5rem; +} +.tsd-panel > h1.tsd-before-signature, +.tsd-panel > h2.tsd-before-signature, +.tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: none; +} + +.tsd-panel-group { + margin: 4rem 0; +} +.tsd-panel-group.tsd-index-group { + margin: 2rem 0; +} +.tsd-panel-group.tsd-index-group details { + margin: 2rem 0; +} + +#tsd-search { + transition: background-color 0.2s; +} +#tsd-search .title { + position: relative; + z-index: 2; +} +#tsd-search .field { + position: absolute; + left: 0; + top: 0; + right: 2.5rem; + height: 100%; +} +#tsd-search .field input { + box-sizing: border-box; + position: relative; + top: -50px; + z-index: 1; + width: 100%; + padding: 0 10px; + opacity: 0; + outline: 0; + border: 0; + background: transparent; + color: var(--color-text); +} +#tsd-search .field label { + position: absolute; + overflow: hidden; + right: -40px; +} +#tsd-search .field input, +#tsd-search .title, +#tsd-toolbar-links a { + transition: opacity 0.2s; +} +#tsd-search .results { + position: absolute; + visibility: hidden; + top: 40px; + width: 100%; + margin: 0; + padding: 0; + list-style: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); +} +#tsd-search .results li { + background-color: var(--color-background); + line-height: initial; + padding: 4px; +} +#tsd-search .results li:nth-child(even) { + background-color: var(--color-background-secondary); +} +#tsd-search .results li.state { + display: none; +} +#tsd-search .results li.current:not(.no-results), +#tsd-search .results li:hover:not(.no-results) { + background-color: var(--color-accent); +} +#tsd-search .results a { + display: flex; + align-items: center; + padding: 0.25rem; + box-sizing: border-box; +} +#tsd-search .results a:before { + top: 10px; +} +#tsd-search .results span.parent { + color: var(--color-text-aside); + font-weight: normal; +} +#tsd-search.has-focus { + background-color: var(--color-accent); +} +#tsd-search.has-focus .field input { + top: 0; + opacity: 1; +} +#tsd-search.has-focus .title, +#tsd-search.has-focus #tsd-toolbar-links a { + z-index: 0; + opacity: 0; +} +#tsd-search.has-focus .results { + visibility: visible; +} +#tsd-search.loading .results li.state.loading { + display: block; +} +#tsd-search.failure .results li.state.failure { + display: block; +} + +#tsd-toolbar-links { + position: absolute; + top: 0; + right: 2rem; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; +} +#tsd-toolbar-links a { + margin-left: 1.5rem; +} +#tsd-toolbar-links a:hover { + text-decoration: underline; +} + +.tsd-signature { + margin: 0 0 1rem 0; + padding: 1rem 0.5rem; + border: 1px solid var(--color-accent); + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; + overflow-x: auto; +} + +.tsd-signature-symbol { + color: var(--color-text-aside); + font-weight: normal; +} + +.tsd-signature-type { + font-style: italic; + font-weight: normal; +} + +.tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + list-style-type: none; +} +.tsd-signatures .tsd-signature { + margin: 0; + border-color: var(--color-accent); + border-width: 1px 0; + transition: background-color 0.1s; +} +.tsd-description .tsd-signatures .tsd-signature { + border-width: 1px; +} + +ul.tsd-parameter-list, +ul.tsd-type-parameter-list { + list-style: square; + margin: 0; + padding-left: 20px; +} +ul.tsd-parameter-list > li.tsd-parameter-signature, +ul.tsd-type-parameter-list > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; +} +ul.tsd-parameter-list h5, +ul.tsd-type-parameter-list h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; +} +.tsd-sources { + margin-top: 1rem; + font-size: 0.875em; +} +.tsd-sources a { + color: var(--color-text-aside); + text-decoration: underline; +} +.tsd-sources ul { + list-style: none; + padding: 0; +} + +.tsd-page-toolbar { + position: sticky; + z-index: 1; + top: 0; + left: 0; + width: 100%; + color: var(--color-text); + background: var(--color-background-secondary); + border-bottom: 1px var(--color-accent) solid; + transition: transform 0.3s ease-in-out; +} +.tsd-page-toolbar a { + color: var(--color-text); + text-decoration: none; +} +.tsd-page-toolbar a.title { + font-weight: bold; +} +.tsd-page-toolbar a.title:hover { + text-decoration: underline; +} +.tsd-page-toolbar .tsd-toolbar-contents { + display: flex; + justify-content: space-between; + height: 2.5rem; + margin: 0 auto; +} +.tsd-page-toolbar .table-cell { + position: relative; + white-space: nowrap; + line-height: 40px; +} +.tsd-page-toolbar .table-cell:first-child { + width: 100%; +} +.tsd-page-toolbar .tsd-toolbar-icon { + box-sizing: border-box; + line-height: 0; + padding: 12px 0; +} + +.tsd-widget { + display: inline-block; + overflow: hidden; + opacity: 0.8; + height: 40px; + transition: + opacity 0.1s, + background-color 0.2s; + vertical-align: bottom; + cursor: pointer; +} +.tsd-widget:hover { + opacity: 0.9; +} +.tsd-widget.active { + opacity: 1; + background-color: var(--color-accent); +} +.tsd-widget.no-caption { + width: 40px; +} +.tsd-widget.no-caption:before { + margin: 0; +} + +.tsd-widget.options, +.tsd-widget.menu { + display: none; +} +input[type="checkbox"] + .tsd-widget:before { + background-position: -120px 0; +} +input[type="checkbox"]:checked + .tsd-widget:before { + background-position: -160px 0; +} + +img { + max-width: 100%; +} + +.tsd-anchor-icon { + display: inline-flex; + align-items: center; + margin-left: 0.5rem; + vertical-align: middle; + color: var(--color-text); +} + +.tsd-anchor-icon svg { + width: 1em; + height: 1em; + visibility: hidden; +} + +.tsd-anchor-link:hover > .tsd-anchor-icon svg { + visibility: visible; +} + +.deprecated { + text-decoration: line-through !important; +} + +.warning { + padding: 1rem; + color: var(--color-warning-text); + background: var(--color-background-warning); +} + +.tsd-kind-project { + color: var(--color-ts-project); +} +.tsd-kind-module { + color: var(--color-ts-module); +} +.tsd-kind-namespace { + color: var(--color-ts-namespace); +} +.tsd-kind-enum { + color: var(--color-ts-enum); +} +.tsd-kind-enum-member { + color: var(--color-ts-enum-member); +} +.tsd-kind-variable { + color: var(--color-ts-variable); +} +.tsd-kind-function { + color: var(--color-ts-function); +} +.tsd-kind-class { + color: var(--color-ts-class); +} +.tsd-kind-interface { + color: var(--color-ts-interface); +} +.tsd-kind-constructor { + color: var(--color-ts-constructor); +} +.tsd-kind-property { + color: var(--color-ts-property); +} +.tsd-kind-method { + color: var(--color-ts-method); +} +.tsd-kind-call-signature { + color: var(--color-ts-call-signature); +} +.tsd-kind-index-signature { + color: var(--color-ts-index-signature); +} +.tsd-kind-constructor-signature { + color: var(--color-ts-constructor-signature); +} +.tsd-kind-parameter { + color: var(--color-ts-parameter); +} +.tsd-kind-type-literal { + color: var(--color-ts-type-literal); +} +.tsd-kind-type-parameter { + color: var(--color-ts-type-parameter); +} +.tsd-kind-accessor { + color: var(--color-ts-accessor); +} +.tsd-kind-get-signature { + color: var(--color-ts-get-signature); +} +.tsd-kind-set-signature { + color: var(--color-ts-set-signature); +} +.tsd-kind-type-alias { + color: var(--color-ts-type-alias); +} + +/* if we have a kind icon, don't color the text by kind */ +.tsd-kind-icon ~ span { + color: var(--color-text); +} + +* { + scrollbar-width: thin; + scrollbar-color: var(--color-accent) var(--color-icon-background); +} + +*::-webkit-scrollbar { + width: 0.75rem; +} + +*::-webkit-scrollbar-track { + background: var(--color-icon-background); +} + +*::-webkit-scrollbar-thumb { + background-color: var(--color-accent); + border-radius: 999rem; + border: 0.25rem solid var(--color-icon-background); +} + +/* mobile */ +@media (max-width: 769px) { + .tsd-widget.options, + .tsd-widget.menu { + display: inline-block; + } + + .container-main { + display: flex; + } + html .col-content { + float: none; + max-width: 100%; + width: 100%; + } + html .col-sidebar { + position: fixed !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + padding: 1.5rem 1.5rem 0 0; + width: 75vw; + visibility: hidden; + background-color: var(--color-background); + transform: translate(100%, 0); + } + html .col-sidebar > *:last-child { + padding-bottom: 20px; + } + html .overlay { + content: ""; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; + } + + .to-has-menu .overlay { + animation: fade-in 0.4s; + } + + .to-has-menu .col-sidebar { + animation: pop-in-from-right 0.4s; + } + + .from-has-menu .overlay { + animation: fade-out 0.4s; + } + + .from-has-menu .col-sidebar { + animation: pop-out-to-right 0.4s; + } + + .has-menu body { + overflow: hidden; + } + .has-menu .overlay { + visibility: visible; + } + .has-menu .col-sidebar { + visibility: visible; + transform: translate(0, 0); + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 100vh; + padding: 1rem 2rem; + } + .has-menu .tsd-navigation { + max-height: 100%; + } +} + +/* one sidebar */ +@media (min-width: 770px) { + .container-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + grid-template-areas: "sidebar content"; + margin: 2rem auto; + } + + .col-sidebar { + grid-area: sidebar; + } + .col-content { + grid-area: content; + padding: 0 1rem; + } +} +@media (min-width: 770px) and (max-width: 1399px) { + .col-sidebar { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + padding-top: 1rem; + } + .site-menu { + margin-top: 1rem; + } +} + +/* two sidebars */ +@media (min-width: 1200px) { + .container-main { + grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax(0, 20rem); + grid-template-areas: "sidebar content toc"; + } + + .col-sidebar { + display: contents; + } + + .page-menu { + grid-area: toc; + padding-left: 1rem; + } + .site-menu { + grid-area: sidebar; + } + + .site-menu { + margin-top: 1rem 0; + } + + .page-menu, + .site-menu { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + } +} diff --git a/docs/classes/colors.Color.html b/docs/classes/colors.Color.html new file mode 100644 index 00000000..b0c92ea7 --- /dev/null +++ b/docs/classes/colors.Color.html @@ -0,0 +1,214 @@ +Color | huetiful-js

Hierarchy

  • Color

Constructors

Methods

  • Parameters

    • Optional amount: string | number

    Returns number | Color

  • Parameters

    • amount: string | number
    • colorspace: any

    Returns Color

  • Parameters

    • against: Color | "lightMode" | "darkMode"

    Returns number

  • Parameters

    • amount: string | number

    Returns Color

  • Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value.

    +

    Parameters

    • Optional deficiencyType: "red" | "green" | "blue" | "monochromacy"

      The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value.

      +
    • severity: number = 1

      The intensity of the filter. The exepected value is between [0,1]. For example 0.5

      +

    Returns ColorToken

    The color as its simulated variant as a hexadecimal string.

    +

    See

    For a deep dive on color vision deficiency go to

    +

    Example

    import { colorDeficiency, toHex } from 'huetiful-js'

    // Here we are simulating color blindness of tritanomaly or we can't see 'blue'.
    // We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1
    let tritanomaly = colorDeficiency('blue')
    console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1))
    // #dd663680

    // Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1
    let protanopia = colorDeficiency('red')
    console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' }))
    // #9f9f9f +
    +
  • Parameters

    • channel: string

    Returns number

  • Parameters

    Returns number | {
        color: ColorToken;
        factor: number;
    }

  • Parameters

    Returns number | {
        color: ColorToken;
        factor: number;
    }

  • Parameters

    Returns number | {
        color: ColorToken;
        factor: number;
    }

  • Parameters

    Returns number | {
        color: ColorToken;
        factor: number;
    }

  • Parameters

    Returns number | {
        color: ColorToken;
        factor: number;
    }

  • Parameters

    Returns number | {
        color: ColorToken;
        factor: number;
    }

  • Returns boolean

  • Returns boolean

  • Returns boolean

  • Parameters

    • Optional amount: number

    Returns number

  • Returns any

  • Returns string | boolean

  • Parameters

    • Optional amount: string | number

    Returns any

  • Parameters

    • scheme: "analogous" | "triadic" | "tetradic" | "complementary"
    • Optional easingFunc: ((t) => number)
        • (t): number
        • Parameters

          • t: number

          Returns number

    Returns ColorToken[] | ColorArray

  • Parameters

    • modeChannel: string
    • value: string | number

    Returns Color

  • Parameters

    • origin: ColorToken
    • Optional t: number
    • Optional options: {
          chromaInterpolator: Interpolator;
          easingFunc: ((t) => number);
          hueFixup: ((arr) => number[]);
          hueInterpolator: Interpolator;
          lightnessInterpolator: Interpolator;
      }
      • chromaInterpolator: Interpolator
      • easingFunc: ((t) => number)
          • (t): number
          • The easing function to use.

            +

            Parameters

            • t: number

              Any value between 0 and 1

              +

            Returns number

            A number.

            +
      • hueFixup: ((arr) => number[])
          • (arr): number[]
          • Parameters

            • arr: number[]

            Returns number[]

      • hueInterpolator: Interpolator
      • lightnessInterpolator: Interpolator

    Returns string

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/colors.ColorArray.html b/docs/classes/colors.ColorArray.html new file mode 100644 index 00000000..cd06c0bd --- /dev/null +++ b/docs/classes/colors.ColorArray.html @@ -0,0 +1,298 @@ +ColorArray | huetiful-js

Hierarchy

  • ColorArray

Constructors

Methods

  • Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function.

    +

    Parameters

    • Optional schemeType: "analogous" | "triadic" | "tetradic" | "complementary"

      (Optional) The palette type you want to return.

      +

    Returns object | ColorToken[]

    An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results.

    +

    Example

    import { discoverPalettes } from 'huetiful-js'

    let sample = [
    "#ffff00",
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#720000",
    "#600000",
    "#4e0000",
    "#3e0000",
    "#310000",
    ]

    console.log(load(sample).discoverPalettes(sample, "tetradic").output())
    // [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ] +
    +
  • Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges.

    +

    Parameters

    • against: ColorToken
    • startContrast: number = 0.05

      The minimum end of the contrast range.

      +
    • Optional endContrast: number

      The maximum end of the contrast range.

      +

    Returns ColorArray

    Array of filtered colors.

    +

    Example

    import { filterByContrast } from 'huetiful-js'

    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    console.log(filterByContrast(sample, 'green', '>=3'))
    // [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] +
    +
  • Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges.

    +

    Parameters

    • against: ColorToken
    • startDistance: number = 0.05

      The minimum end of the distance range.

      +
    • Optional endDistance: number

      The maximum end of the distance range.

      +

    Returns ColorArray

    Array of filtered colors.

    +

    Example

    import { filterByDistance } from 'huetiful-js'

    let sample = [
    "#ffff00",
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#720000",
    "#600000",
    ]

    console.log(filterByDistance(sample, "yellow", 0.1))
    // [ '#ffff00' ] +
    +
  • Returns colors in the specified hue ranges between 0 to 360.

    +

    Parameters

    • startHue: number = 0

      The minimum end of the hue range.

      +
    • endHue: number = 360

      The maximum end of the hue range.

      +

    Returns ColorArray

    Array of the filtered colors.

    +

    Example

    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    filterByHue(sample, 20, 80)

    // [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] +
    +
  • Returns an array of colors in the specified lightness range. The range is between 0 and 100.

    +

    Parameters

    • startLightness: number = 5

      The minimum end of the lightness range.

      +
    • endLightness: number = 100

      The maximum end of the lightness range.

      +

    Returns ColorArray

    Array of filtered colors.

    +

    Example

    import { filterByLightness } from 'huetiful-js'
    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    filterByLightness(sample, 20, 80)

    // [ '#00c000', '#007e00', '#164100', '#720000' ] +
    +
  • Returns an array of colors in the specified luminance range. The range is normalised to [0,1].

    +

    Parameters

    • startLuminance: number = 0.05

      The minimum end of the luminance range.

      +
    • endLuminance: number = 1

      The maximum end of the luminance range.

      +

    Returns ColorArray

    Array of filtered colors.

    +

    Example

    import { filterByLuminance } from 'huetiful-js'
    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    filterByLuminance(sample, 0.4, 0.9)

    // [ '#00ffdc', '#00ff78' ] +
    +
  • Returns an array of colors in the specified saturation range. The range is normalised to [0,1].

    +

    Parameters

    • startSaturation: number = 0.05

      The minimum end of the saturation range.

      +
    • endSaturation: number = 1

      The maximum end of the saturation range.

      +
    • Optional mode: HueColorSpaces

      The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do.

      +

    Returns ColorArray

    Array of filtered colors.

    +

    Example

    import { filterByContrast } from 'huetiful-js'

    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    console.log(filterByContrast(sample, 'green', '>=3'))
    // [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] +
    +
  • Gets the largest hue value from the passed in colors.

    +

    Parameters

    • Optional colorSpace: HueColorSpaces

      The mode color space to perform the computation in.

      +
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The largest hue value in the colors passed in or a custom object.

    +

    Example

    import { getFarthestHue } from 'huetiful-js'
    let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']

    console.log(load(output).getFarthestHue('lch'))
    // 273.54920266436477 +
    +
  • Gets the largest lightness value from the passed in colors.

    +

    Parameters

    • Optional colorspace: HueColorSpaces
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The largest lightness value in the colors passed in or a custom object.

    +

    Example

    import { maxLightness } from 'huetiful-js'

    let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]

    console.log(load(sample).getFarthestLightness('lch', true))

    // { lightness: 80.94668903360088, name: '#f3bac1' } +
    +
  • Gets the smallest hue value from the passed in colors.

    +

    Parameters

    • Optional colorSpace: HueColorSpaces

      The mode color space to perform the computation in.

      +
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The smallest hue value in the colors passed in or a custom object.

    +

    Example

    import { getNearestHue } from 'huetiful-js'

    let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']

    console.log(load(sample).getNearestHue('lch'))
    // 12.462831644544274 +
    +
  • Gets the smallest lightness value from the passed in colors.

    +

    Parameters

    • Optional colorspace: HueColorSpaces
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The smallest lightness value in the colors passed in or a custom object.

    +

    Example

    import { minLightness } from 'huetiful-js'

    let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]

    console.log(load(sample).getNearestLightness('lch', true))

    // { lightness: 72.61647882089876, name: '#a1bd2f' } +
    +
  • Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides.If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result.

    +

    Parameters

    • Optional colorspace: HueColorSpaces

      The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch.

      +
    • Optional samples: number
    • Optional kind: "natural" | "monotone" | "basis"

      The type of the spline interpolation method. Default is basis.

      +
    • Optional closed: boolean

      Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false

      +
    • Optional options: InterpolatorOptions

      Optional channel specific overrides.

      +

    Returns ColorToken[]

    A hexadecimal representation of the resultant color.

    +
  • Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param)

    +

    Parameters

    • against: ColorToken
    • Optional order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +

    Returns ColorArray

    An array of the sorted color values.

    +

    Example

    import { sortByContrast } from 'huetiful-js'

    let sample = ['purple', 'green', 'red', 'brown']
    console.log(sortByContrast(sample, 'yellow'))
    // [ 'red', 'green', 'brown', 'purple' ]

    console.log(sortByContrast(sample, 'yellow', 'desc'))
    // [ 'purple', 'brown', 'green', 'red' ] +
    +
  • Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist

    +

    Parameters

    • against: ColorToken

      The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array.

      +
    • Optional order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • Optional options: ColorDistanceOptions

    Returns ColorArray

    An array of the sorted color values.

    +

    Example

    import { sortByDistance } from 'huetiful-js'

    let sample = ['purple', 'green', 'red', 'brown']
    console.log(
    sortByDistance(sample, 'yellow', 'asc', {
    mode: 'lch',
    })
    )

    // [ 'brown', 'red', 'green', 'purple' ]

    let sample = ['purple', 'green', 'red', 'brown']
    console.log(
    sortByDistance(sample, 'yellow', 'asc', {
    mode: 'lch',
    })
    )

    // [ 'green', 'brown', 'red', 'purple' ] +
    +
  • Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported

    +

    Parameters

    • order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • colorspace: HueColorSpaces

      The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results.

      +

    Returns ColorArray

    An array of the sorted color values.

    +

    Example

    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ];


    let sorted = sortByHue(sample);
    console.log(sorted)
    // [
    '#310000', '#3e0000',
    '#4e0000', '#600000',
    '#720000', '#ffff00',
    '#164100', '#00c000',
    '#007e00', '#00ff78',
    '#00ffdc'
    ]

    let sortedDescending = sortByHue(sample,'desc');
    console.log(sortedDescending)
    // [
    '#00ffdc', '#00ff78',
    '#007e00', '#00c000',
    '#164100', '#ffff00',
    '#720000', '#600000',
    '#4e0000', '#3e0000',
    '#310000'
    ] +
    +
  • Sorts colors according to their lightness.

    +

    Parameters

    • Optional order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +

    Returns ColorArray

    An array of the sorted color values.

    +

    Example

    import { sortByLightness } from "huetiful-js";

    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ]

    sortByLightness(sample)

    // [
    '#310000', '#3e0000',
    '#4e0000', '#600000',
    '#720000', '#164100',
    '#007e00', '#00c000',
    '#00ff78', '#00ffdc',
    '#ffff00'
    ]


    sortByLightness(sample,'desc')

    // [
    '#ffff00', '#00ffdc',
    '#00ff78', '#00c000',
    '#007e00', '#164100',
    '#720000', '#600000',
    '#4e0000', '#3e0000',
    '#310000'
    ] +
    +
  • Sorts colors according to the relative brightness as defined by WCAG definition.

    +

    Parameters

    • Optional order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +

    Returns ColorArray

    An array of the sorted color values.

    +

    Example

    import { sortByLuminance } from "huetiful-js";
    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ];



    let sorted = sortByLuminance(sample)
    console.log(sorted)
    // [
    '#310000', '#3e0000',
    '#4e0000', '#600000',
    '#720000', '#164100',
    '#007e00', '#00c000',
    '#00ff78', '#00ffdc',
    '#ffff00'
    ]

    let sortedDescending = sortByLuminance(sample, "desc");
    console.log(sortedDescending)
    // [
    '#ffff00', '#00ffdc',
    '#00ff78', '#00c000',
    '#007e00', '#164100',
    '#720000', '#600000',
    '#4e0000', '#3e0000',
    '#310000'
    ] +
    +
  • Sorts colors according to their saturation.

    +

    Parameters

    • order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • Optional mode: HueColorSpaces

      The mode color space to compute the saturation value in. The default is jch .

      +

    Returns ColorArray

    An array of the sorted color values.

    +

    Example

    import { sortBySaturation } from "huetiful-js";
    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ];

    let sorted = sortBySaturation(sample);
    console.log(sorted);

    // [
    '#310000', '#3e0000',
    '#164100', '#4e0000',
    '#600000', '#720000',
    '#00ffdc', '#007e00',
    '#00ff78', '#00c000',
    '#ffff00'
    ]

    let sortedDescending = sortBySaturation(sample,'desc');
    console.log(sortedDescending)
    // [
    '#ffff00', '#00c000',
    '#00ff78', '#007e00',
    '#00ffdc', '#720000',
    '#600000', '#4e0000',
    '#164100', '#3e0000',
    '#310000'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.color-1.html b/docs/functions/colors.color-1.html new file mode 100644 index 00000000..f1e822c2 --- /dev/null +++ b/docs/functions/colors.color-1.html @@ -0,0 +1,177 @@ +color | huetiful-js
  • Wrapper function over the Color class that returns a new Color method chain.

    +

    Parameters

    Returns Color

    A new Color class with all the utilities that take a single color as the first parameter bound to its prototype.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.colors.html b/docs/functions/colors.colors.html new file mode 100644 index 00000000..4cc92bb8 --- /dev/null +++ b/docs/functions/colors.colors.html @@ -0,0 +1,180 @@ +colors | huetiful-js
  • A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade.

    +

    Parameters

    • shade: TailwindColorFamilies | "all"

      Any shade in the default TailwindCSS palette e.g amber,blue.

      +
    • Optional value: ScaleValues

      Any value from 100 to 900 in increments of 100 e.g "200".

      +

    Returns ColorToken | ColorToken[]

    color Returns a hex code string or array of hex codes depending on how the function is called.

    +

    Example

    import { colors } from "huetiful-js";

    let all300 = colors("all", 300);

    console.log(all300)
    //[
    '#cbd5e1', '#d1d5db', '#d4d4d8',
    '#d4d4d4', '#d6d3d1', '#fca5a5',
    '#fdba74', '#fcd34d', '#fde047',
    '#bef264', '#86efac', '#6ee7b7',
    '#5eead4', '#7dd3fc', '#93c5fd',
    '#c4b5fd', '#d8b4fe', '#f0abfc',
    '#f9a8d4', '#fda4af'
    ]

    let red = colors("red");
    console.log(red);

    // [
    '#fef2f2', '#fee2e2',
    '#fecaca', '#fca5a5',
    '#f87171', '#ef4444',
    '#dc2626', '#b91c1c',
    '#991b1b', '#7f1d1d'
    ]

    let red100 = colors("red", 100);

    console.log(red100)
    // #fee2e2 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.diverging.html b/docs/functions/colors.diverging.html new file mode 100644 index 00000000..76b7c141 --- /dev/null +++ b/docs/functions/colors.diverging.html @@ -0,0 +1,179 @@ +diverging | huetiful-js

Function diverging

  • A wrapper function for ColorBrewer's map of diverging color schemes.

    +

    Parameters

    Returns ColorToken[]

    An array of colors in hex represantation.

    +

    Example

    import { diverging } from 'huetiful-js'



    console.log(diverging("Spectral"))
    //[
    '#7fc97f', '#beaed4',
    '#fdc086', '#ffff99',
    '#386cb0', '#f0027f',
    '#bf5b17', '#666666'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.load.html b/docs/functions/colors.load.html new file mode 100644 index 00000000..f900e2ec --- /dev/null +++ b/docs/functions/colors.load.html @@ -0,0 +1,175 @@ +load | huetiful-js
  • Parameters

    • colors: ColorToken[]

      An array of colors to chain the array methods on. Every element in the array will be parsed as a color token.

      +

    Returns ColorArray

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.qualitative.html b/docs/functions/colors.qualitative.html new file mode 100644 index 00000000..ccbc7def --- /dev/null +++ b/docs/functions/colors.qualitative.html @@ -0,0 +1,179 @@ +qualitative | huetiful-js

Function qualitative

  • A wrapper function for ColorBrewer's map of qualitative color schemes.

    +

    Parameters

    Returns ColorToken[]

    An array of colors in hex represantation.

    +

    Example

    import { qualitative } from 'huetiful-js'


    console.log(qualitative("Accent"))
    // [
    '#7fc97f', '#beaed4',
    '#fdc086', '#ffff99',
    '#386cb0', '#f0027f',
    '#bf5b17', '#666666'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.sequential.html b/docs/functions/colors.sequential.html new file mode 100644 index 00000000..f42018d1 --- /dev/null +++ b/docs/functions/colors.sequential.html @@ -0,0 +1,179 @@ +sequential | huetiful-js

Function sequential

  • A wrapper function for ColorBrewer's map of sequential color schemes.

    +

    Parameters

    Returns ColorToken[]

    An array of colors in hex represantation.

    +

    Example

    import { sequential } from 'huetiful-js


    console.log(sequential("OrRd"))

    // [
    '#fff7ec', '#fee8c8',
    '#fdd49e', '#fdbb84',
    '#fc8d59', '#ef6548',
    '#d7301f', '#b30000',
    '#7f0000'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/colors.tailwindColors.html b/docs/functions/colors.tailwindColors.html new file mode 100644 index 00000000..42368cd8 --- /dev/null +++ b/docs/functions/colors.tailwindColors.html @@ -0,0 +1,178 @@ +tailwindColors | huetiful-js

Function tailwindColors

  • Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale,

    +

    Parameters

    • shade: number | typeof iterator | "length" | "toString" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "codePointAt" | "includes" | "endsWith" | "normalize" | "repeat" | "startsWith" | "anchor" | "big" | "blink" | "bold" | "fixed" | "fontcolor" | "fontsize" | "italics" | "link" | "small" | "strike" | "sub" | "sup" | "valueOf"

    Returns ((val?) => string | string[])

    color A hex string value or array of hex strings.

    +
      • (val?): string | string[]
      • Parameters

        Returns string | string[]

    Example

    import { tailwindColors } from "huetiful-js";

    // We pass in red as the target hue.
    // It returns a function that can be called with an optional value parameter
    let red = tailwindColors("red");
    console.log(red());
    // [
    '#fef2f2', '#fee2e2',
    '#fecaca', '#fca5a5',
    '#f87171', '#ef4444',
    '#dc2626', '#b91c1c',
    '#991b1b', '#7f1d1d'
    ]


    console.log(red(100));
    // '#fee2e2'

    console.log(red('900'));
    // '#7f1d1d' +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/converters.num2rgb.html b/docs/functions/converters.num2rgb.html new file mode 100644 index 00000000..db2d316a --- /dev/null +++ b/docs/functions/converters.num2rgb.html @@ -0,0 +1,179 @@ +num2rgb | huetiful-js
  • Returns the RGB color equivalent of any number between 0 and 16,777,215.

    +

    Parameters

    • num: number

      The number to convert to RGB

      +
    • hex: boolean = false

    Returns ColorToken

    color An RGB color object or hex string.

    +

    Example

    import { num2rgb } from 'huetiful-js'

    console.log(num2rgb(900, true))
    // #000384 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/converters.rgb2num.html b/docs/functions/converters.rgb2num.html new file mode 100644 index 00000000..aba10a7a --- /dev/null +++ b/docs/functions/converters.rgb2num.html @@ -0,0 +1,179 @@ +rgb2num | huetiful-js
  • Returns the numerical equivalent of a color.

    +

    Parameters

    • color: ColorToken

      The color to convert to its numerical equivalent.

      +

    Returns number

    value The numerical value of the color from 0 to 16,777,215.

    +

    Example

    import { rgb2num } from 'huetiful-js'

    console.log(rgb2num("b2c3f1"))
    // 11715569 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/converters.temp2Color.html b/docs/functions/converters.temp2Color.html new file mode 100644 index 00000000..577b029a --- /dev/null +++ b/docs/functions/converters.temp2Color.html @@ -0,0 +1,180 @@ +temp2Color | huetiful-js
  • Converts the temperature value (in Kelvins) to an RGB color.

    +

    Parameters

    • kelvin: number

      The number of Kelvins. From 0 to 30,000 .

      +
    • hex: boolean = false

      Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true.

      +

    Returns ColorToken

    color The color as a hexadecimal or RGB color object.

    +

    Example

    import { temp2Color } from 'huetiful-js'

    console.log(temp2Color(2542))
    // #ffa44a +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/converters.toColorTuple.html b/docs/functions/converters.toColorTuple.html new file mode 100644 index 00000000..88ed8018 --- /dev/null +++ b/docs/functions/converters.toColorTuple.html @@ -0,0 +1,180 @@ +toColorTuple | huetiful-js
  • Returns an array of channel values in the mode color space. It does not mutate the values of the passed in color token.

    +

    Parameters

    • color: string | object

      Expects the color to be in hexadecimal represantation or as a plain color object.

      +
    • mode: Colorspaces

      The mode color space to return channel values for

      +

    Returns any[]

    An array of channel values with the colorspace as first element and the alpha channel if its explicitly defined in the passed in color.

    +

    Example

    let rgbColor = {
    r: 0.4,
    g: 0.3,
    b: 0.7,
    mode: "rgb",
    };
    console.log(toColorTuple(rgbColor,'rgb'));

    // [ 'rgb', 0.4, 0.3, 0.7 ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/converters.toHex.html b/docs/functions/converters.toHex.html new file mode 100644 index 00000000..866af787 --- /dev/null +++ b/docs/functions/converters.toHex.html @@ -0,0 +1,179 @@ +toHex | huetiful-js
  • Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal.

    +

    Parameters

    • color: ColorToken

      The color to convert to hexadecimal. Works on color objects and CSS named colors.

      +

    Returns string

    A hexadecimal representation of the passed in color.

    +

    Example

    import { toHex } from "huetiful-js";

    console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" }))
    // #7b794180

    console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" }))
    // #7b7941 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/converters.ucsConverter.html b/docs/functions/converters.ucsConverter.html new file mode 100644 index 00000000..1294fd54 --- /dev/null +++ b/docs/functions/converters.ucsConverter.html @@ -0,0 +1,177 @@ +ucsConverter | huetiful-js
  • Converter function with mode definitions for uniform color spaces. The function is curried to return a converter in the passed colospace.

    +

    Parameters

    Returns ConvertFn<any>

    The converter function in the mode colorspace.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/filterBy.filterByContrast.html b/docs/functions/filterBy.filterByContrast.html new file mode 100644 index 00000000..6d5c33ef --- /dev/null +++ b/docs/functions/filterBy.filterByContrast.html @@ -0,0 +1,180 @@ +filterByContrast | huetiful-js

Function filterByContrast

  • Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges.

    +

    Parameters

    • collection: object | ColorToken[]
    • against: ColorToken
    • startContrast: number = 1

      The minimum end of the contrast range.

      +
    • endContrast: number = 21

      The maximum end of the contrast range.

      +

    Returns ColorToken[]

    Array of filtered colors.

    +

    Example

    import { filterByContrast } from 'huetiful-js'

    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    console.log(filterByContrast(sample, 'green', '>=3'))
    // [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/filterBy.filterByDistance.html b/docs/functions/filterBy.filterByDistance.html new file mode 100644 index 00000000..a2510a5e --- /dev/null +++ b/docs/functions/filterBy.filterByDistance.html @@ -0,0 +1,180 @@ +filterByDistance | huetiful-js

Function filterByDistance

  • Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. Uses the differenceHyab metric for calculating the distances.

    +

    Parameters

    • collection: object | ColorToken[]
    • against: ColorToken
    • startDistance: number = 0.05

      The minimum end of the distance range.

      +
    • Optional endDistance: number

      The maximum end of the distance range.

      +

    Returns ColorToken[]

    Array of filtered colors.

    +

    Example

    import { filterByDistance } from 'huetiful-js'

    let sample = [
    "#ffff00",
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#720000",
    "#600000",
    ]

    console.log(filterByDistance(sample, "yellow", 0.1))
    // [ '#ffff00' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/filterBy.filterByHue.html b/docs/functions/filterBy.filterByHue.html new file mode 100644 index 00000000..34dd2aa1 --- /dev/null +++ b/docs/functions/filterBy.filterByHue.html @@ -0,0 +1,180 @@ +filterByHue | huetiful-js

Function filterByHue

  • Returns colors in the specified hue ranges between 0 to 360.

    +

    Parameters

    • collection: object | ColorToken[]
    • startHue: number = 0

      The minimum end of the hue range.

      +
    • endHue: number = 360

      The maximum end of the hue range.

      +
    • Optional colorspace: HueColorSpaces

    Returns ColorToken[]

    Array of the filtered colors.

    +

    Example

    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    filterByHue(sample, 20, 80)

    // [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/filterBy.filterByLightness.html b/docs/functions/filterBy.filterByLightness.html new file mode 100644 index 00000000..4277bc50 --- /dev/null +++ b/docs/functions/filterBy.filterByLightness.html @@ -0,0 +1,181 @@ +filterByLightness | huetiful-js

Function filterByLightness

  • Returns an array of colors in the specified lightness range. The range is between 0 and 100.

    +

    Parameters

    • collection: object | ColorToken[]
    • startLightness: number = 5

      The minimum end of the lightness range.

      +
    • endLightness: number = 100

      The maximum end of the lightness range.

      +
    • Optional colorspace: HueColorSpaces

      The mode colorspace to retrieve the lightness value from. The default is lch65

      +

    Returns ColorToken[]

    Array of filtered colors.

    +

    Example

    import { filterByLightness } from 'huetiful-js'
    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    filterByLightness(sample, 20, 80)

    // [ '#00c000', '#007e00', '#164100', '#720000' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/filterBy.filterByLuminance.html b/docs/functions/filterBy.filterByLuminance.html new file mode 100644 index 00000000..d80a8f5e --- /dev/null +++ b/docs/functions/filterBy.filterByLuminance.html @@ -0,0 +1,180 @@ +filterByLuminance | huetiful-js

Function filterByLuminance

  • Returns an array of colors in the specified luminance range. The range is normalised to [0,1].

    +

    Parameters

    • collection: object | ColorToken[]
    • startLuminance: number = 0.05

      The minimum end of the luminance range.

      +
    • endLuminance: number = 1

      The maximum end of the luminance range.

      +

    Returns ColorToken[]

    Array of filtered colors.

    +

    Example

    import { filterByLuminance } from 'huetiful-js'
    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    filterByLuminance(sample, 0.4, 0.9)

    // [ '#00ffdc', '#00ff78' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/filterBy.filterBySaturation.html b/docs/functions/filterBy.filterBySaturation.html new file mode 100644 index 00000000..133eeac0 --- /dev/null +++ b/docs/functions/filterBy.filterBySaturation.html @@ -0,0 +1,181 @@ +filterBySaturation | huetiful-js

Function filterBySaturation

  • Returns an array of colors in the specified saturation range. The range is normalised to [0,1].

    +

    Parameters

    • collection: object | ColorToken[]
    • startSaturation: number = 0.05

      The minimum end of the saturation range.

      +
    • endSaturation: number = 1

      The maximum end of the saturation range.

      +
    • Optional colorspace: HueColorSpaces

      The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do.

      +

    Returns ColorToken[]

    Array of filtered colors.

    +

    Example

    import { filterByContrast } from 'huetiful-js'

    let sample = [
    '#00ffdc',
    '#00ff78',
    '#00c000',
    '#007e00',
    '#164100',
    '#ffff00',
    '#310000',
    '#3e0000',
    '#4e0000',
    '#600000',
    '#720000',
    ]

    console.log(filterByContrast(sample, 'green', '>=3'))
    // [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.discoverPalettes.html b/docs/functions/generators.discoverPalettes.html new file mode 100644 index 00000000..22c3f586 --- /dev/null +++ b/docs/functions/generators.discoverPalettes.html @@ -0,0 +1,180 @@ +discoverPalettes | huetiful-js

Function discoverPalettes

  • Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function.

    +

    Parameters

    • colors: ColorToken[]

      The array of colors to create palettes from. Preferably use 5 or more colors for better results.

      +
    • Optional schemeType: "analogous" | "triadic" | "tetradic" | "complementary"

      (Optional) The palette type you want to return.

      +

    Returns ColorToken[] | object

    An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results.

    +

    Example

    import { discoverPalettes } from 'huetiful-js'

    let sample = [
    "#ffff00",
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#720000",
    "#600000",
    "#4e0000",
    "#3e0000",
    "#310000",
    ]

    console.log(discoverPalettes(sample, "tetradic"))
    // [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.earthtone.html b/docs/functions/generators.earthtone.html new file mode 100644 index 00000000..1e71e2a1 --- /dev/null +++ b/docs/functions/generators.earthtone.html @@ -0,0 +1,181 @@ +earthtone | huetiful-js
  • Creates a scale of a spline based interpolation between an earthtone and a color.

    +

    Parameters

    • color: ColorToken

      The color to interpolate an earth tone with. + *

      +
    • Optional colorspace: HueColorSpaces
    • Optional options: EarthtoneOptions

      Optional overrides for customising interpolation and easing functions.

      +

    Returns ColorToken[]

    The array of colors resulting from the earthtone interpolation as hex codes.

    +

    Example

    import { earthtone } from 'huetiful-js'


    console.log(earthtone("pink",{earthtones:'clay',iterations:5 }))
    // [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.hueShift.html b/docs/functions/generators.hueShift.html new file mode 100644 index 00000000..b6d11a79 --- /dev/null +++ b/docs/functions/generators.hueShift.html @@ -0,0 +1,180 @@ +hueShift | huetiful-js
  • Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme.

    +

    Parameters

    • color: ColorToken

      The color to use as the base of the hueshift. Colors are internally converted to LCH.

      +
    • Optional colorspace: UniformColorSpaces
    • Optional options: HueShiftOptions

      The optional overrides object to customize per channel options like interpolation methods and channel fixups.

      +

    Returns ColorToken[]

    An array of colors in hex. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1

    +

    Example

    import { hueShift } from "huetiful-js";

    let hueShiftedPalette = hueShift("#3e0000");

    console.log(hueShiftedPalette);

    // [
    '#ffffe1', '#ffdca5',
    '#ca9a70', '#935c40',
    '#5c2418', '#3e0000',
    '#310000', '#34000f',
    '#38001e', '#3b002c',
    '#3b0c3a'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.interpolateSpline.html b/docs/functions/generators.interpolateSpline.html new file mode 100644 index 00000000..ff8e15ac --- /dev/null +++ b/docs/functions/generators.interpolateSpline.html @@ -0,0 +1,181 @@ +interpolateSpline | huetiful-js

Function interpolateSpline

  • Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides.

    +

    Parameters

    • colors: ColorToken[]

      The array of colors to interpolate. If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result.

      +
    • Optional colorspace: HueColorSpaces

      The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch.

      +
    • Optional samples: number
    • Optional kind: "natural" | "monotone" | "basis"

      The type of the spline interpolation method. Default is basis.

      +
    • closed: boolean = false

      Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false

      +
    • Optional options: InterpolatorOptions

      Optional channel specific overrides.

      +

    Returns ColorToken[]

    A hexadecimal representation of the resultant color.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.interpolator.html b/docs/functions/generators.interpolator.html new file mode 100644 index 00000000..14b00843 --- /dev/null +++ b/docs/functions/generators.interpolator.html @@ -0,0 +1,174 @@ +interpolator | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.pairedScheme.html b/docs/functions/generators.pairedScheme.html new file mode 100644 index 00000000..3743dab4 --- /dev/null +++ b/docs/functions/generators.pairedScheme.html @@ -0,0 +1,181 @@ +pairedScheme | huetiful-js
  • pairedScheme + Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black.

    +

    Parameters

    • color: ColorToken

      The color to return a paired color scheme from.

      +
    • Optional options: PairedSchemeOptions

      The optional overrides object to customize per channel options like interpolation methods and channel fixups.

      +

    Returns ColorToken[] | ColorToken

    An array containing the paired scheme.

    +

    Example

    import { pairedScheme } from 'huetiful-js'

    console.log(pairedScheme("green",{hueStep:6,iterations:4,tone:'dark'}))
    // [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.pastel.html b/docs/functions/generators.pastel.html new file mode 100644 index 00000000..b51b2c93 --- /dev/null +++ b/docs/functions/generators.pastel.html @@ -0,0 +1,179 @@ +pastel | huetiful-js
  • Returns a random pastel variant of the passed in color.

    +

    Parameters

    • color: ColorToken

      The color to return a pastel variant of.

      +

    Returns ColorToken

    A random pastel color.

    +

    Example

    import { pastel } from 'huetiful-js'

    console.log(pastel("green"))
    // #036103ff +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/generators.scheme.html b/docs/functions/generators.scheme.html new file mode 100644 index 00000000..15d00c93 --- /dev/null +++ b/docs/functions/generators.scheme.html @@ -0,0 +1,179 @@ +scheme | huetiful-js
  • Generates a randomised classic color scheme from a single base color.

    +

    Parameters

    • schemeType: string

      Any classic color scheme either "analogous"|"triadic"|"tetradic"|"complementary"|"splitComplementary".

      +

    Returns ((color, easingFunc?) => ColorToken[])

    An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme.

    +
      • (color, easingFunc?): ColorToken[]
      • Parameters

        • color: ColorToken
        • Optional easingFunc: ((t) => number)
            • (t): number
            • Parameters

              • t: number

              Returns number

        Returns ColorToken[]

    Example

    import { base } from 'huetiful-js'

    console.log(base("triadic")("#a1bd2f", true))
    // [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.adjustHue.html b/docs/functions/helpers.adjustHue.html new file mode 100644 index 00000000..92fb80bc --- /dev/null +++ b/docs/functions/helpers.adjustHue.html @@ -0,0 +1,178 @@ +adjustHue | huetiful-js

Function adjustHue

  • Internal

    Parameters

    • value: number

      The hue angle to normalize.

      +

    Returns number

    The normalized hue angle or passed in value if it was within [0,360]

    +

    Example

    console.log(adjustHue(4));
    // 4

    console.log(adjustHue(444));
    // 84 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.channelDifference.html b/docs/functions/helpers.channelDifference.html new file mode 100644 index 00000000..406f3c8a --- /dev/null +++ b/docs/functions/helpers.channelDifference.html @@ -0,0 +1,180 @@ +channelDifference | huetiful-js

Function channelDifference

  • Internal

    Returns the channel value difference between the passed in colors. They are both converted to the colorspace in the modeChannel parameter before values are computed.

    +

    Parameters

    • color: ColorToken

      The color to subtract values from/

      +
    • modeChannel: string

      The colorspace and channel string to perform the operation in.

      +

    Returns ((subtrahend) => number)

    The difference between the color channel(s)

    +
      • (subtrahend): number
      • Parameters

        Returns number

    Example

    
    +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.checkArg.html b/docs/functions/helpers.checkArg.html new file mode 100644 index 00000000..a276c0e5 --- /dev/null +++ b/docs/functions/helpers.checkArg.html @@ -0,0 +1,178 @@ +checkArg | huetiful-js
  • Internal

    Returns the first truthy value.

    +

    Parameters

    • arg: unknown

      The value to check

      +
    • def: unknown

      The value to cast if arg is falsy

      +

    Returns unknown

    The first truthy value

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.colorObj.html b/docs/functions/helpers.colorObj.html new file mode 100644 index 00000000..bab0507e --- /dev/null +++ b/docs/functions/helpers.colorObj.html @@ -0,0 +1,174 @@ +colorObj | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.colorObjArr.html b/docs/functions/helpers.colorObjArr.html new file mode 100644 index 00000000..3a7f896b --- /dev/null +++ b/docs/functions/helpers.colorObjArr.html @@ -0,0 +1,174 @@ +colorObjArr | huetiful-js

Function colorObjArr

  • Parameters

    Returns ((collection) => {
        color: ColorToken;
        factor: Factor;
    }[])

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.customConcat.html b/docs/functions/helpers.customConcat.html new file mode 100644 index 00000000..ea4c8c9e --- /dev/null +++ b/docs/functions/helpers.customConcat.html @@ -0,0 +1,174 @@ +customConcat | huetiful-js

Function customConcat

  • Parameters

    • hue: object

    Returns number[]

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.customFindKey.html b/docs/functions/helpers.customFindKey.html new file mode 100644 index 00000000..f21c4750 --- /dev/null +++ b/docs/functions/helpers.customFindKey.html @@ -0,0 +1,177 @@ +customFindKey | huetiful-js

Function customFindKeyPrivate

  • Private Internal

    Parameters

    • collection: object

      The collection to inspect.

      +
    • factor: number

      The value to compare against

      +

    Returns string

    Returns the found element or its key, else undefined.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.customSort.html b/docs/functions/helpers.customSort.html new file mode 100644 index 00000000..1ef8ce76 --- /dev/null +++ b/docs/functions/helpers.customSort.html @@ -0,0 +1,178 @@ +customSort | huetiful-js

Function customSort

  • Internal

    Helper function for native sorting method for arrays.

    +

    Parameters

    • order: Order

      Either ascending or descending.

      +
    • Optional factor: string

      The property to query.

      +

    Returns ((a, b) => number)

    A sorted array.

    +
      • (a, b): number
      • Parameters

        • a: any
        • b: any

        Returns number

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.eq.html b/docs/functions/helpers.eq.html new file mode 100644 index 00000000..91efd7f1 --- /dev/null +++ b/docs/functions/helpers.eq.html @@ -0,0 +1,174 @@ +eq | huetiful-js
  • Parameters

    • x: number
    • y: number

    Returns boolean

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.expressionParser.html b/docs/functions/helpers.expressionParser.html new file mode 100644 index 00000000..529588da --- /dev/null +++ b/docs/functions/helpers.expressionParser.html @@ -0,0 +1,180 @@ +expressionParser | huetiful-js

Function expressionParser

  • Internal

    Takes an arithmetic operator followed by a value and passes the result of the expression to the specified channel. Currently supports addition,subtraction,division and multiplication symbols only.

    +

    Parameters

    • color: ColorToken

      The color.

      +
    • modeChannel: string

      The colorspace channel to set.

      +
    • expression: string

      The expression assignment as a string.

      +

    Returns number

    Example

    console.log(lch('blue'));
    // { mode: 'lch',l: 29.568297153444703,c: 131.2014771995311,h: 301.36428148973533}

    console.log(expressionParser('blue', 'lch.l', '*0.3'));
    // { mode: 'lch',l: 8.87048914603341,c: 131.2014771995311,h: 301.36428148973533 } +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.filteredArr.html b/docs/functions/helpers.filteredArr.html new file mode 100644 index 00000000..78946e5a --- /dev/null +++ b/docs/functions/helpers.filteredArr.html @@ -0,0 +1,178 @@ +filteredArr | huetiful-js

Function filteredArr

  • Internal

    Filters an array according to the value of a color's queried factor

    +

    Parameters

    • factor: Factor

      The property to query and use as filtering criteria

      +
    • Optional cb: unknown

      The function to use for comparison

      +

    Returns ((collection, start, end?) => ColorToken[])

    The filtered array

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.floorCeil.html b/docs/functions/helpers.floorCeil.html new file mode 100644 index 00000000..38b71605 --- /dev/null +++ b/docs/functions/helpers.floorCeil.html @@ -0,0 +1,179 @@ +floorCeil | huetiful-js

Function floorCeil

  • Internal

    Parameters

    • num: number

      The number to round up or down.

      +

    Returns number

    An integer

    +

    Function

    Rounds up or down a number based on the float value.

    +

    Example

    console.log(floorCeil(1.45));
    // 1
    console.log(floorCeil(1.501));
    // 2 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.getModeChannel.html b/docs/functions/helpers.getModeChannel.html new file mode 100644 index 00000000..10bee863 --- /dev/null +++ b/docs/functions/helpers.getModeChannel.html @@ -0,0 +1,180 @@ +getModeChannel | huetiful-js

Function getModeChannel

  • Internal

    Gets the clipped string of a passed in colorspace by removing non-channel characters.

    +

    Parameters

    • colorspace: string

      The colorspace to get the channel keys.

      +
    • Optional index: number

      Optional index to return a single specified channel.

      +

    Returns string

    A string.

    +

    Example

    console.log(getModeChannel("oklch"));
    // lch

    console.log(getModeChannel("okhsl", 2));
    // l +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.gt.html b/docs/functions/helpers.gt.html new file mode 100644 index 00000000..b4ae6e54 --- /dev/null +++ b/docs/functions/helpers.gt.html @@ -0,0 +1,174 @@ +gt | huetiful-js
  • Parameters

    • x: number
    • y: number

    Returns boolean

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.gte.html b/docs/functions/helpers.gte.html new file mode 100644 index 00000000..bd4a2b17 --- /dev/null +++ b/docs/functions/helpers.gte.html @@ -0,0 +1,174 @@ +gte | huetiful-js
  • Parameters

    • x: number
    • y: number

    Returns boolean

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.inRange.html b/docs/functions/helpers.inRange.html new file mode 100644 index 00000000..d9f929a2 --- /dev/null +++ b/docs/functions/helpers.inRange.html @@ -0,0 +1,179 @@ +inRange | huetiful-js
  • Internal

    Parameters

    • number: number

      The number to check.

      +
    • start: number

      The minimum or starting value.

      +
    • Optional end: number

      The maximum or starting value.

      +

    Returns boolean

    True if the number is in range else false.

    +

    Function

    Checks if a value is within the start and end range.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.isInteger.html b/docs/functions/helpers.isInteger.html new file mode 100644 index 00000000..22ec9568 --- /dev/null +++ b/docs/functions/helpers.isInteger.html @@ -0,0 +1,177 @@ +isInteger | huetiful-js

Function isInteger

  • Internal

    Checks if a number is an integer or float.

    +

    Parameters

    • num: string | number

      The number to query

      +

    Returns boolean

    True if the number is an integer else false if it is a float.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.lt.html b/docs/functions/helpers.lt.html new file mode 100644 index 00000000..d0fa8e88 --- /dev/null +++ b/docs/functions/helpers.lt.html @@ -0,0 +1,174 @@ +lt | huetiful-js
  • Parameters

    • x: number
    • y: number

    Returns boolean

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.lte.html b/docs/functions/helpers.lte.html new file mode 100644 index 00000000..f6a86471 --- /dev/null +++ b/docs/functions/helpers.lte.html @@ -0,0 +1,174 @@ +lte | huetiful-js
  • Parameters

    • x: number
    • y: number

    Returns boolean

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.matchChromaChannel.html b/docs/functions/helpers.matchChromaChannel.html new file mode 100644 index 00000000..29231744 --- /dev/null +++ b/docs/functions/helpers.matchChromaChannel.html @@ -0,0 +1,179 @@ +matchChromaChannel | huetiful-js

Function matchChromaChannel

  • Internal

    Parameters

    • colorspace: string

      The color space to match saturation/chroma channel.

      +

    Returns string

    The mode channel string passed to getChannel()

    +

    Function

    Matches the chroma/saturation channel of any compliant color space

    +

    Example

    import { matchChromaChannel } from 'huetiful-js'
    console.log(matchChromaChannel("jch"));
    // jch.c

    console.log(matchChromaChannel("okhsl"));
    // okhsl.s +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.matchComparator.html b/docs/functions/helpers.matchComparator.html new file mode 100644 index 00000000..efba0ca5 --- /dev/null +++ b/docs/functions/helpers.matchComparator.html @@ -0,0 +1,177 @@ +matchComparator | huetiful-js

Function matchComparator

  • Internal

    Matches the comparison symbols used in the expression string.

    +

    Parameters

    • s: string

      The string to match.

      +

    Returns string

    The matched comparator, if any, as a string.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.matchDigits.html b/docs/functions/helpers.matchDigits.html new file mode 100644 index 00000000..008419cf --- /dev/null +++ b/docs/functions/helpers.matchDigits.html @@ -0,0 +1,177 @@ +matchDigits | huetiful-js

Function matchDigits

  • Internal

    Gets the digits in the expression string

    +

    Parameters

    • s: string

      Thestring to match

      +

    Returns string

    The matched digits, if any, as a string.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.matchLightnessChannel.html b/docs/functions/helpers.matchLightnessChannel.html new file mode 100644 index 00000000..de96369d --- /dev/null +++ b/docs/functions/helpers.matchLightnessChannel.html @@ -0,0 +1,179 @@ +matchLightnessChannel | huetiful-js

Function matchLightnessChannel

  • Internal

    Parameters

    • colorspace: string

      The color space to match lightness channel.

      +

    Returns string

    The mode channel string passed to getChannel

    +

    Function

    Matches the lightness channel of any compliant color space

    +

    Example

    console.log(matchLightnessChannel("jch"));
    // jch.j

    console.log(matchLightnessChannel("okhsl"));
    // okhsl.l +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.max.html b/docs/functions/helpers.max.html new file mode 100644 index 00000000..b6160bf4 --- /dev/null +++ b/docs/functions/helpers.max.html @@ -0,0 +1,179 @@ +max | huetiful-js
  • Internal

    Gets the largest value in an array

    +

    Parameters

    • array: number[]

      The array to retrieve maximum value

      +

    Returns number

    The largest number in the array

    +

    Example

    console.log(max([0, 3, 4]));
    // 4 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.min.html b/docs/functions/helpers.min.html new file mode 100644 index 00000000..79e8b4fd --- /dev/null +++ b/docs/functions/helpers.min.html @@ -0,0 +1,179 @@ +min | huetiful-js
  • Internal

    Gets the smallest value in an array

    +

    Parameters

    • array: number[]

      The array to retrieve minimum value

      +

    Returns number

    The smallest number in the array

    +

    Example

    console.log(min([0, 3, 4]));
    // 0 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.normalize.html b/docs/functions/helpers.normalize.html new file mode 100644 index 00000000..5819bc7d --- /dev/null +++ b/docs/functions/helpers.normalize.html @@ -0,0 +1,178 @@ +normalize | huetiful-js

Function normalize

  • Internal

    Parameters

    • value: number

      The value to chec if its in the accepted range for the passed in mode channel

      +
    • modeChannel: string

      A string defining the mode and channel ranges to use for comparison

      +

    Returns number

    The normalized channel value or the passed in value if it was within range

    +

    Function

    Normalizes passed in channel value to a range accepted by color spaces as defined in Culori.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.random.html b/docs/functions/helpers.random.html new file mode 100644 index 00000000..32511b1f --- /dev/null +++ b/docs/functions/helpers.random.html @@ -0,0 +1,178 @@ +random | huetiful-js
  • Internal

    Parameters

    • min: number

      The lower bound.

      +
    • max: number

      The upper bound.

      +

    Returns number

    A number.

    +

    Function

    Returns a random number between minimum and maximum bounds.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/helpers.sortedArr.html b/docs/functions/helpers.sortedArr.html new file mode 100644 index 00000000..ed822b31 --- /dev/null +++ b/docs/functions/helpers.sortedArr.html @@ -0,0 +1,178 @@ +sortedArr | huetiful-js

Function sortedArr

  • Internal

    Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param.

    +

    Parameters

    • factor: Factor

      The property to query

      +
    • callback: unknown

      The function to use for comparison.

      +
    • order: Order
    • colorObj: boolean = false

    Returns ((collection) => any[])

    An array of colors or color objects.

    +
      • (collection): any[]
      • Parameters

        Returns any[]

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/sortBy.sortByContrast.html b/docs/functions/sortBy.sortByContrast.html new file mode 100644 index 00000000..e70c4fea --- /dev/null +++ b/docs/functions/sortBy.sortByContrast.html @@ -0,0 +1,179 @@ +sortByContrast | huetiful-js

Function sortByContrast

  • Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param)

    +

    Parameters

    • collection: object | ColorToken[]
    • against: ColorToken
    • Optional order: Order

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +

    Returns ColorToken[]

    An array of the sorted color values.

    +

    Example

    import { sortByContrast } from 'huetiful-js'

    let sample = ['purple', 'green', 'red', 'brown']
    console.log(sortByContrast(sample, 'yellow'))
    // [ 'red', 'green', 'brown', 'purple' ]

    console.log(sortByContrast(sample, 'yellow', 'desc'))
    // [ 'purple', 'brown', 'green', 'red' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/sortBy.sortByDistance.html b/docs/functions/sortBy.sortByDistance.html new file mode 100644 index 00000000..69bca21e --- /dev/null +++ b/docs/functions/sortBy.sortByDistance.html @@ -0,0 +1,180 @@ +sortByDistance | huetiful-js

Function sortByDistance

  • Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist

    +

    Parameters

    • collection: object | ColorToken[]
    • against: ColorToken

      The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array.

      +
    • Optional order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • Optional options: ColorDistanceOptions

    Returns ColorToken[]

    An array of the sorted color values.

    +

    Example

    import { sortByDistance } from 'huetiful-js'

    let sample = ['purple', 'green', 'red', 'brown']
    console.log(
    sortByDistance(sample, 'yellow', 'asc', {
    mode: 'lch',
    })
    )

    // [ 'brown', 'red', 'green', 'purple' ]

    let sample = ['purple', 'green', 'red', 'brown']
    console.log(
    sortByDistance(sample, 'yellow', 'asc', {
    mode: 'lch',
    })
    )

    // [ 'green', 'brown', 'red', 'purple' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/sortBy.sortByHue.html b/docs/functions/sortBy.sortByHue.html new file mode 100644 index 00000000..7f628d7d --- /dev/null +++ b/docs/functions/sortBy.sortByHue.html @@ -0,0 +1,180 @@ +sortByHue | huetiful-js

Function sortByHue

  • Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional order: Order

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • Optional colorspace: HueColorSpaces

      The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results.

      +

    Returns ColorToken[]

    An array of the sorted color values.

    +

    Example

    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ];


    let sorted = sortByHue(sample);
    console.log(sorted)
    // [
    '#310000', '#3e0000',
    '#4e0000', '#600000',
    '#720000', '#ffff00',
    '#164100', '#00c000',
    '#007e00', '#00ff78',
    '#00ffdc'
    ]

    let sortedDescending = sortByHue(sample,'desc');
    console.log(sortedDescending)
    // [
    '#00ffdc', '#00ff78',
    '#007e00', '#00c000',
    '#164100', '#ffff00',
    '#720000', '#600000',
    '#4e0000', '#3e0000',
    '#310000'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/sortBy.sortByLightness.html b/docs/functions/sortBy.sortByLightness.html new file mode 100644 index 00000000..87b6be26 --- /dev/null +++ b/docs/functions/sortBy.sortByLightness.html @@ -0,0 +1,180 @@ +sortByLightness | huetiful-js

Function sortByLightness

  • Sorts colors according to their lightness.

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional order: Order

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • Optional colorspace: HueColorSpaces

      The mode colorspace to use for filtering color lightness. Defaut is lch65

      +

    Returns ColorToken[]

    An array of the sorted color values.

    +

    Example

    import { sortByLightness } from "huetiful-js";

    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ]

    sortByLightness(sample)

    // [
    '#310000', '#3e0000',
    '#4e0000', '#600000',
    '#720000', '#164100',
    '#007e00', '#00c000',
    '#00ff78', '#00ffdc',
    '#ffff00'
    ]


    sortByLightness(sample,'desc')

    // [
    '#ffff00', '#00ffdc',
    '#00ff78', '#00c000',
    '#007e00', '#164100',
    '#720000', '#600000',
    '#4e0000', '#3e0000',
    '#310000'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/sortBy.sortByLuminance.html b/docs/functions/sortBy.sortByLuminance.html new file mode 100644 index 00000000..90cebc16 --- /dev/null +++ b/docs/functions/sortBy.sortByLuminance.html @@ -0,0 +1,179 @@ +sortByLuminance | huetiful-js

Function sortByLuminance

  • Sorts colors according to the relative brightness as defined by WCAG definition.

    +

    Parameters

    • collection: object | ColorToken[]
    • order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +

    Returns ColorToken[]

    An array of the sorted color values.

    +

    Example

    import { sortByLuminance } from "huetiful-js";
    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ];



    let sorted = sortByLuminance(sample)
    console.log(sorted)
    // [
    '#310000', '#3e0000',
    '#4e0000', '#600000',
    '#720000', '#164100',
    '#007e00', '#00c000',
    '#00ff78', '#00ffdc',
    '#ffff00'
    ]

    let sortedDescending = sortByLuminance(sample, "desc");
    console.log(sortedDescending)
    // [
    '#ffff00', '#00ffdc',
    '#00ff78', '#00c000',
    '#007e00', '#164100',
    '#720000', '#600000',
    '#4e0000', '#3e0000',
    '#310000'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/sortBy.sortBySaturation.html b/docs/functions/sortBy.sortBySaturation.html new file mode 100644 index 00000000..f7f439ea --- /dev/null +++ b/docs/functions/sortBy.sortBySaturation.html @@ -0,0 +1,180 @@ +sortBySaturation | huetiful-js

Function sortBySaturation

  • Sorts colors according to their saturation.

    +

    Parameters

    • collection: object | ColorToken[]
    • order: "asc" | "desc"

      The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc')

      +
    • Optional mode: HueColorSpaces

      The mode color space to compute the saturation value in. The default is jch .

      +

    Returns ColorToken[]

    An array of the sorted color values.

    +

    Example

    import { sortBySaturation } from "huetiful-js";
    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000",
    "#007e00",
    "#164100",
    "#ffff00",
    "#310000",
    "#3e0000",
    "#4e0000",
    "#600000",
    "#720000",
    ];

    let sorted = sortBySaturation(sample);
    console.log(sorted);

    // [
    '#310000', '#3e0000',
    '#164100', '#4e0000',
    '#600000', '#720000',
    '#00ffdc', '#007e00',
    '#00ff78', '#00c000',
    '#ffff00'
    ]

    let sortedDescending = sortBySaturation(sample,'desc');
    console.log(sortedDescending)
    // [
    '#ffff00', '#00c000',
    '#00ff78', '#007e00',
    '#00ffdc', '#720000',
    '#600000', '#4e0000',
    '#164100', '#3e0000',
    '#310000'
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.alpha.html b/docs/functions/utils.alpha.html new file mode 100644 index 00000000..dc803da1 --- /dev/null +++ b/docs/functions/utils.alpha.html @@ -0,0 +1,180 @@ +alpha | huetiful-js

Function alpha

  • Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted

    +

    Parameters

    • color: ColorToken

      The color with the targeted opacity/alpha channel.

      +
    • Optional value: string | number

      The value to apply to the opacity channel. The value is between [0,1]

      +

    Returns number

    color The resulting color. Returns an 8 character hex code.

    +

    Example

    // Getting the alpha
    console.log(alpha('#a1bd2f0d'))
    // 0.050980392156862744

    // Setting the alpha

    let myColor = alpha('b2c3f1', 0.5)

    console.log(myColor)

    // #b2c3f180 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.brighten.html b/docs/functions/utils.brighten.html new file mode 100644 index 00000000..17d50576 --- /dev/null +++ b/docs/functions/utils.brighten.html @@ -0,0 +1,176 @@ +brighten | huetiful-js

Function brighten

  • Parameters

    • color: ColorToken

      The color to brighten.

      +
    • value: string | number

      The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5")

      +
    • colorspace: any

    Returns ColorToken

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.colorDeficiency.html b/docs/functions/utils.colorDeficiency.html new file mode 100644 index 00000000..c9327c68 --- /dev/null +++ b/docs/functions/utils.colorDeficiency.html @@ -0,0 +1,180 @@ +colorDeficiency | huetiful-js

Function colorDeficiency

  • Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value.

    +

    Parameters

    • Optional deficiencyType: DeficiencyType

      The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value.

      +

    Returns ((color, severity?) => string)

    The color as its simulated variant as a hexadecimal string.

    +
      • (color, severity?): string
      • Parameters

        Returns string

    See

    For a deep dive on color vision deficiency go to

    +

    Example

    import { colorDeficiency, toHex } from 'huetiful-js'

    // Here we are simulating color blindness of tritanomaly or we can't see 'blue'.
    // We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1
    let tritanomaly = colorDeficiency('blue')
    console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1))
    // #dd663680

    // Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1
    let protanopia = colorDeficiency('red')
    console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' }))
    // #9f9f9f +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.darken.html b/docs/functions/utils.darken.html new file mode 100644 index 00000000..25a0dd93 --- /dev/null +++ b/docs/functions/utils.darken.html @@ -0,0 +1,180 @@ +darken | huetiful-js

Function darken

  • Darkens the color by reducing the lightness channel. .

    +

    Parameters

    • color: ColorToken

      The color to darken.

      +
    • value: string | number

      The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5")

      +

    Returns ColorToken

    color The darkened color.

    +

    Example

    
    +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getChannel.html b/docs/functions/utils.getChannel.html new file mode 100644 index 00000000..407c91c2 --- /dev/null +++ b/docs/functions/utils.getChannel.html @@ -0,0 +1,179 @@ +getChannel | huetiful-js

Function getChannel

  • Gets the value specifified channel on the color.

    +

    Parameters

    • mc: string

      The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color.

      +

    Returns ((color) => number)

    value The value of the queried channel.

    +
      • (color): number
      • Parameters

        Returns number

    Example

    import { getChannel } from 'huetiful-js'

    console.log(getChannel('rgb.g')('#a1bd2f'))
    // 0.7411764705882353 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getComplimentaryHue.html b/docs/functions/utils.getComplimentaryHue.html new file mode 100644 index 00000000..d267c076 --- /dev/null +++ b/docs/functions/utils.getComplimentaryHue.html @@ -0,0 +1,180 @@ +getComplimentaryHue | huetiful-js

Function getComplimentaryHue

  • Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors.

    +

    Parameters

    • color: ColorToken

      The color to retrieve its complimentary hue.

      +
    • Optional colorspace: HueColorSpaces
    • colorObj: boolean = false

      Optional boolean whether to return an object with the result color hue family or just the result color. Default is false.

      +

    Returns {
        color: ColorToken;
        hue: string;
    } | ColorToken

    An object with the hue family and complimentary color as keys.

    +

    Example

    import { getComplimentaryHue } from "huetiful-js";


    console.log(getComplimentaryHue("pink", true))
    //// { hue: 'blue-green', color: '#97dfd7ff' }

    console.log(getComplimentaryHue("purple"))
    // #005700ff +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getContrast.html b/docs/functions/utils.getContrast.html new file mode 100644 index 00000000..a83df700 --- /dev/null +++ b/docs/functions/utils.getContrast.html @@ -0,0 +1,178 @@ +getContrast | huetiful-js

Function getContrast

  • Gets the contrast between the passed in colors.

    +

    Parameters

    Returns number

    The relative luminance of the lightest color.

    +

    Example

    import { getContrast } from 'huetiful-js'

    console.log(getContrast("black", "white"));
    // 21 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getFarthestChroma.html b/docs/functions/utils.getFarthestChroma.html new file mode 100644 index 00000000..e4c5bccf --- /dev/null +++ b/docs/functions/utils.getFarthestChroma.html @@ -0,0 +1,179 @@ +getFarthestChroma | huetiful-js

Function getFarthestChroma

  • Gets the largest saturation value from the passed in colors.

    +

    Parameters

    • collection: object | ColorToken[]
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The largest saturation value in the colors passed in or a custom object.

    +

    Example

    import { getFarthestChroma } from 'huetiful-js'

    let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']

    console.log(getFarthestChroma(sample, 'lch'))
    // 67.22120855010492 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getFarthestContrast.html b/docs/functions/utils.getFarthestContrast.html new file mode 100644 index 00000000..3e6f2aeb --- /dev/null +++ b/docs/functions/utils.getFarthestContrast.html @@ -0,0 +1,179 @@ +getFarthestContrast | huetiful-js

Function getFarthestContrast

  • Gets the largest contrast value from the passed in colors compared against a sample color.

    +

    Parameters

    • collection: object | ColorToken[]
    • against: ColorToken
    • Optional colorObj: boolean

      Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false.

      +

    Returns number | {
        factor: number;
        name: ColorToken;
    }

    The largest contrast value in the colors passed in or a custom object.

    +

    Example

    import { getFarthestContrast } from 'huetiful-js'

    console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green"));
    // 3.08355493246362

    console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true));
    // { contrast: 3.08355493246362, name: '#f3bac1' } +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getFarthestHue.html b/docs/functions/utils.getFarthestHue.html new file mode 100644 index 00000000..59ec156c --- /dev/null +++ b/docs/functions/utils.getFarthestHue.html @@ -0,0 +1,180 @@ +getFarthestHue | huetiful-js

Function getFarthestHue

  • Gets the largest hue value from the passed in colors.

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional colorspace: HueColorSpaces

      The mode color space to perform the computation in.

      +
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The largest hue value in the colors passed in or a custom object.

    +

    Example

    import { getFarthestHue } from 'huetiful-js'
    let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']

    console.log(getFarthestHue(sample, 'lch'))
    // 273.54920266436477 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getFarthestLightness.html b/docs/functions/utils.getFarthestLightness.html new file mode 100644 index 00000000..4ce52aea --- /dev/null +++ b/docs/functions/utils.getFarthestLightness.html @@ -0,0 +1,180 @@ +getFarthestLightness | huetiful-js

Function getFarthestLightness

  • Gets the largest lightness value from the passed in colors.

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional colorspace: HueColorSpaces

      THe mode colorspace to retrieve the lightness value from.

      +
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The largest lightness value in the colors passed in or a custom object.

    +

    Example

    import { getFarthestLightness } from 'huetiful-js'

    let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]

    console.log(getFarthestLightness(sample, true))

    // { lightness: 80.94668903360088, name: '#f3bac1' } +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getHueFamily.html b/docs/functions/utils.getHueFamily.html new file mode 100644 index 00000000..e92679bb --- /dev/null +++ b/docs/functions/utils.getHueFamily.html @@ -0,0 +1,179 @@ +getHueFamily | huetiful-js

Function getHueFamily

  • Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray".

    +

    Parameters

    Returns HueFamily

    The name of the hue family for example red or green.

    +

    Example

    import { getHue } from 'huetiful-js'


    console.log(getHue("#310000"))
    // red +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getLuminance.html b/docs/functions/utils.getLuminance.html new file mode 100644 index 00000000..4532e13b --- /dev/null +++ b/docs/functions/utils.getLuminance.html @@ -0,0 +1,179 @@ +getLuminance | huetiful-js

Function getLuminance

  • Parameters

    Returns number

    value The color's luminance value.

    +

    Alias

    Gets the luminance value of that color as defined by WCAG.

    +

    Example

    import { getLuminance } from 'huetiful-js'

    console.log(getLuminance('#a1bd2f'))
    // 0.4417749513730954 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getNearestChroma.html b/docs/functions/utils.getNearestChroma.html new file mode 100644 index 00000000..46be475f --- /dev/null +++ b/docs/functions/utils.getNearestChroma.html @@ -0,0 +1,180 @@ +getNearestChroma | huetiful-js

Function getNearestChroma

  • Gets the smallest chroma/saturation value from the passed in colors.

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional colorspace: HueColorSpaces

      The mode color space to perform the computation in.

      +
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The smallest chroma/saturation value in the colors passed in or a custom object.

    +

    Example

    import { getNearestChroma } from 'huetiful-js'

    let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']

    console.log(getNearestChroma(sample, 'lch'))
    // 22.45669293295522 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getNearestColor.html b/docs/functions/utils.getNearestColor.html new file mode 100644 index 00000000..c588b5d6 --- /dev/null +++ b/docs/functions/utils.getNearestColor.html @@ -0,0 +1,180 @@ +getNearestColor | huetiful-js

Function getNearestColor

  • Parameters

    • collection: ColorToken[] | "tailwind"

      The collection of colors to search for nearest colors

      +
    • color: ColorToken

      The color to use for distance comparison

      +
    • num: number = 1

      The number of colors to return, if the value is above the colors in the available sample, the entire collection is returned with colors ordered in ascending order using the differenceHyab metric.

      +

    Returns ColorToken | ColorToken[]

    An array of colors.

    +

    Example

    let cols = colors('all', '500')

    console.log(getNearestColor(cols, 'blue', 3));
    // [ '#a855f7', '#8b5cf6', '#d946ef' ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getNearestContrast.html b/docs/functions/utils.getNearestContrast.html new file mode 100644 index 00000000..1a1e68f0 --- /dev/null +++ b/docs/functions/utils.getNearestContrast.html @@ -0,0 +1,179 @@ +getNearestContrast | huetiful-js

Function getNearestContrast

  • Gets the smallest contrast value from the passed in colors compared against a sample color.

    +

    Parameters

    • collection: object | ColorToken[]
    • against: ColorToken
    • Optional colorObj: boolean

      Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false.

      +

    Returns any

    The smallest contrast value in the colors passed in or a custom object.

    +

    Example

    import { getNearestContrast } from 'huetiful-js'

    console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green"));
    // 2.4061390502133424

    console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true));
    // { contrast: 2.4061390502133424, name: '#a1bd2f' } +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getNearestHue.html b/docs/functions/utils.getNearestHue.html new file mode 100644 index 00000000..0466e2e0 --- /dev/null +++ b/docs/functions/utils.getNearestHue.html @@ -0,0 +1,180 @@ +getNearestHue | huetiful-js

Function getNearestHue

  • Gets the smallest hue value from the passed in colors.

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional colorspace: string

      The mode color space to perform the computation in.

      +
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The smallest hue value in the colors passed in or a custom object.

    +

    Example

    import { getNearestHue } from 'huetiful-js'

    let sample = ['b2c3f1', '#a1bd2f', '#f3bac1']

    console.log(getNearestHue(sample, 'lch'))
    // 12.462831644544274 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.getNearestLightness.html b/docs/functions/utils.getNearestLightness.html new file mode 100644 index 00000000..9ae1df8c --- /dev/null +++ b/docs/functions/utils.getNearestLightness.html @@ -0,0 +1,179 @@ +getNearestLightness | huetiful-js

Function getNearestLightness

  • Gets the smallest lightness value from the passed in colors.

    +

    Parameters

    • collection: object | ColorToken[]
    • Optional colorspace: HueColorSpaces
    • colorObj: boolean = false

      Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false.

      +

    Returns number | {
        color: ColorToken;
        factor: number;
    }

    The smallest lightness value in the colors passed in or a custom object.

    +

    Example

    import { getNearestLightness } from 'huetiful-js'

    let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"]

    console.log(getNearestLightness(sample, true))

    // { lightness: 72.61647882089876, name: '#a1bd2f' } +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.isAchromatic.html b/docs/functions/utils.isAchromatic.html new file mode 100644 index 00000000..24b0cb62 --- /dev/null +++ b/docs/functions/utils.isAchromatic.html @@ -0,0 +1,179 @@ +isAchromatic | huetiful-js

Function isAchromatic

  • Checks if a color is achromatic(without hue or simply grayscale).

    +

    Parameters

    Returns boolean

    boolean Returns true if the color is achromatic else false

    +

    Example

    import { isAchromatic } from "huetiful-js";
    import { formatHex8, interpolate, samples } from "culori"


    isAchromatic('pink')
    // false

    let sample = [
    "#164100",
    "#ffff00",
    "#310000",
    'pink'
    ];

    console.log(map(sample, isAchromatic));

    // [false, false, false,false]

    isAchromatic('gray')
    // Returns true


    console.log(map(sample, isAchromatic));


    // we create an interpolation using black and white
    let f = interpolate(["black", "white"]);

    //We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function.
    // Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.)
    let grays = map(samples(12), (c) => formatHex8(f(c)));
    console.log(map(grays, isAchromatic));

    //
    [ false, true, true,
    true, true, true,
    true, true, true,
    true, true, false
    ] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.isCool.html b/docs/functions/utils.isCool.html new file mode 100644 index 00000000..1545b28f --- /dev/null +++ b/docs/functions/utils.isCool.html @@ -0,0 +1,179 @@ +isCool | huetiful-js

Function isCool

  • Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false.

    +

    Parameters

    • color: ColorToken

      The color to check the temperature.

      +

    Returns boolean

    True or false.

    +

    Example

    import { isCool } from 'huetiful-js'

    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000"
    ];


    console.log(isCool(sample[2]));
    // false

    console.log(map(sample, isCool));

    // [ true, false, true] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.isWarm.html b/docs/functions/utils.isWarm.html new file mode 100644 index 00000000..de033359 --- /dev/null +++ b/docs/functions/utils.isWarm.html @@ -0,0 +1,179 @@ +isWarm | huetiful-js

Function isWarm

  • Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false.

    +

    Parameters

    • color: ColorToken

      The color to check the temperature.

      +

    Returns boolean

    True or false.

    +

    Example

    import { isWarm } from 'huetiful-js'

    let sample = [
    "#00ffdc",
    "#00ff78",
    "#00c000"
    ];



    console.log(isWarm(sample[2]));
    //true

    console.log(map(sample, isWarm));


    // [ false, true, false] +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.overtone.html b/docs/functions/utils.overtone.html new file mode 100644 index 00000000..02e539f6 --- /dev/null +++ b/docs/functions/utils.overtone.html @@ -0,0 +1,179 @@ +overtone | huetiful-js

Function overtone

  • Returns the hue which is biasing the passed in color

    +

    Parameters

    • color: ColorToken

      The color to query its overtone.

      +

    Returns string | boolean

    The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false.

    +

    Example

    import { overtone } from "huetiful-js";

    console.log(overtone("fefefe"))
    // 'gray'

    console.log(overtone("cyan"))
    // 'green'

    console.log(overtone("blue"))
    // false +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.setChannel.html b/docs/functions/utils.setChannel.html new file mode 100644 index 00000000..9c4d8621 --- /dev/null +++ b/docs/functions/utils.setChannel.html @@ -0,0 +1,179 @@ +setChannel | huetiful-js

Function setChannel

  • Sets the value for the specified channel in a color.

    +

    Parameters

    • mc: string

      The mode and channel to work with. For example 'rgb.b'.

      +

    Returns ((color, value) => ColorToken)

    color The mutated color.

    +

    Example

    import { setChannel } from 'huetiful-js'

    let myColor = setChannel('lch.h')('green',10)

    console.log(getChannel('lch.h')(myColor))
    // 10 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/utils.setLuminance.html b/docs/functions/utils.setLuminance.html new file mode 100644 index 00000000..5ebef5b8 --- /dev/null +++ b/docs/functions/utils.setLuminance.html @@ -0,0 +1,180 @@ +setLuminance | huetiful-js

Function setLuminance

  • Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance).

    +

    Parameters

    • color: ColorToken

      The color to set luminance

      +
    • lum: number

      The amount of luminance to set. The value range is normalised between [0,1]

      +

    Returns ColorToken

    The mutated color with the modified properties.

    +

    Example

    import { setLuminance, getLuminance } from 'huetiful-js'

    let myColor = setLuminance('#a1bd2f', 0.5)

    console.log(getLuminance(myColor))
    // 0.4999999136285792 +
    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/huetiful-logo.png b/docs/huetiful-logo.png new file mode 100644 index 00000000..8348f68d Binary files /dev/null and b/docs/huetiful-logo.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..4ec08d6a --- /dev/null +++ b/docs/index.html @@ -0,0 +1,210 @@ +huetiful-js

huetiful-js

npm version +stability-stable +npm minzipped size +types +styled with prettier +linted with eslint +license

+

paypal +twitter

+

Logo +TypeScript library for general purpose color manipulations and generating custom color scales.

+

Getting started⛳

Node

The library🧾 is on npm as a package📦 for use in NodeJS:

+
npm i huetiful-js
+
+

You can use a CDN in this example, jsdelivr to load the library remotely:

+
import {...} from 'https://cdn.jsdelivr.net/npm/huetiful-js/lib/huetiful.esm.min.mjs'
+
+

Browser

Or load the library as a UMD glabal (huetiful) in your HTML file using a <script> tag:

+
# With script tag

<script src='https://cdn.jsdelivr.net/npm/huetiful-js/dist/huetiful.umd.js'></script> +
+

Quickstart

See the Quickstart section on the Wiki to see some examples and demonstrations of the library.

+

What's next🤷🏽‍♂️

+

The possibilities are limited by the imagination🤯 of the user._ +~ me :smile:

+
+

See the full docs here📜

+

Need help😣 ?

See some unexpected results😖? Check the issue tracker to open an issue or search for the problem to see if your issue already exists or has been resolved.

+

Would like to join the chat🗣️ and share ideas💡 and suggestions💭 ? See the discussions and just say hi, or share a coding meme(whatever breaks the ice🏔️)

+

Contributing👐🏾

This project is fully open source so contributions are welcome! Help make this project better by suggesting improvements or features and patching bugs🐛. See🔍 the CONTRIBUTING file for more information on how to get started.

+

Donating

This project is completely free and will remain so forever. But if you wish to support the development of this project or just buy the developer a coffee, please feel free.

+

References🔗

Coloring with code: A programmatic approach by George Francis

+
+
License

Copyright (c) 2023, +Dean Tarisai and contributors +huetiful-js is released under the Apache 2.0 license.

+
+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/colors.html b/docs/modules/colors.html new file mode 100644 index 00000000..b62fb554 --- /dev/null +++ b/docs/modules/colors.html @@ -0,0 +1,183 @@ +colors | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/converters.html b/docs/modules/converters.html new file mode 100644 index 00000000..68f8256e --- /dev/null +++ b/docs/modules/converters.html @@ -0,0 +1,180 @@ +converters | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/filterBy.html b/docs/modules/filterBy.html new file mode 100644 index 00000000..e7e94419 --- /dev/null +++ b/docs/modules/filterBy.html @@ -0,0 +1,180 @@ +filterBy | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/generators.html b/docs/modules/generators.html new file mode 100644 index 00000000..c4215a6a --- /dev/null +++ b/docs/modules/generators.html @@ -0,0 +1,183 @@ +generators | huetiful-js

Module generators

References

Re-exports ucsConverter

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/helpers.html b/docs/modules/helpers.html new file mode 100644 index 00000000..fa85ac34 --- /dev/null +++ b/docs/modules/helpers.html @@ -0,0 +1,203 @@ +helpers | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/sortBy.html b/docs/modules/sortBy.html new file mode 100644 index 00000000..41047170 --- /dev/null +++ b/docs/modules/sortBy.html @@ -0,0 +1,180 @@ +sortBy | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/types.html b/docs/modules/types.html new file mode 100644 index 00000000..2f186e2c --- /dev/null +++ b/docs/modules/types.html @@ -0,0 +1,201 @@ +types | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules/utils.html b/docs/modules/utils.html new file mode 100644 index 00000000..4a3a947a --- /dev/null +++ b/docs/modules/utils.html @@ -0,0 +1,198 @@ +utils | huetiful-js

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.AdaptivePaletteOptions.html b/docs/types/types.AdaptivePaletteOptions.html new file mode 100644 index 00000000..97502060 --- /dev/null +++ b/docs/types/types.AdaptivePaletteOptions.html @@ -0,0 +1,175 @@ +AdaptivePaletteOptions | huetiful-js

Type alias AdaptivePaletteOptions

AdaptivePaletteOptions: {
    backgroundColor?: {
        dark?: ColorToken;
        light?: ColorToken;
    };
    colorBlind?: boolean;
    viewingConditions?: ViewingConditions;
}

Type declaration

  • Optional backgroundColor?: {
        dark?: ColorToken;
        light?: ColorToken;
    }
  • Optional colorBlind?: boolean
  • Optional viewingConditions?: ViewingConditions

Description

This object returns the lightMode and darkMode optimized version of a color with support to add color vision deficiency simulation to the final color result.

+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.ColorDistanceOptions.html b/docs/types/types.ColorDistanceOptions.html new file mode 100644 index 00000000..2e37582d --- /dev/null +++ b/docs/types/types.ColorDistanceOptions.html @@ -0,0 +1,174 @@ +ColorDistanceOptions | huetiful-js

Type alias ColorDistanceOptions

ColorDistanceOptions: {
    mode?: Colorspaces;
    weights?: [number, number, number, number];
}

Type declaration

  • Optional mode?: Colorspaces
  • Optional weights?: [number, number, number, number]

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.ColorObject.html b/docs/types/types.ColorObject.html new file mode 100644 index 00000000..03d0c341 --- /dev/null +++ b/docs/types/types.ColorObject.html @@ -0,0 +1,174 @@ +ColorObject | huetiful-js

Type alias ColorObject

ColorObject: {
    alpha?: number;
    mode: Colorspaces;
}

Type declaration

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.ColorOptions.html b/docs/types/types.ColorOptions.html new file mode 100644 index 00000000..a61df631 --- /dev/null +++ b/docs/types/types.ColorOptions.html @@ -0,0 +1,174 @@ +ColorOptions | huetiful-js

Type alias ColorOptions

ColorOptions: {
    alpha?: number;
    colorSpace?: HueColorSpaces;
    colorspace?: HueColorSpaces;
    contrast?: number;
    darkMode?: ColorToken;
    lightMode?: ColorToken;
    lightness?: number;
    luminance?: number;
    saturation?: number;
    temperature?: number;
}

Type declaration

  • Optional alpha?: number
  • Optional colorSpace?: HueColorSpaces
  • Optional colorspace?: HueColorSpaces
  • Optional contrast?: number
  • Optional darkMode?: ColorToken
  • Optional lightMode?: ColorToken
  • Optional lightness?: number
  • Optional luminance?: number
  • Optional saturation?: number
  • Optional temperature?: number

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.ColorToken.html b/docs/types/types.ColorToken.html new file mode 100644 index 00000000..375f81c6 --- /dev/null +++ b/docs/types/types.ColorToken.html @@ -0,0 +1,175 @@ +ColorToken | huetiful-js

Type alias ColorToken

ColorToken: number | string | object | ColorTuple

Description

Any recognizable color token.

+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.ColorTuple.html b/docs/types/types.ColorTuple.html new file mode 100644 index 00000000..e75c330c --- /dev/null +++ b/docs/types/types.ColorTuple.html @@ -0,0 +1,174 @@ +ColorTuple | huetiful-js

Type alias ColorTuple

ColorTuple: [string, number, number, number, number?]

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.Colorspaces.html b/docs/types/types.Colorspaces.html new file mode 100644 index 00000000..44db716e --- /dev/null +++ b/docs/types/types.Colorspaces.html @@ -0,0 +1,174 @@ +Colorspaces | huetiful-js

Type alias Colorspaces

Colorspaces: "a98" | "cubehelix" | "dlab" | "jab" | "lab" | "lab65" | "lrgb" | "luv" | "oklab" | "rgb" | HueColorSpaces

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.DeficiencyType.html b/docs/types/types.DeficiencyType.html new file mode 100644 index 00000000..e224832b --- /dev/null +++ b/docs/types/types.DeficiencyType.html @@ -0,0 +1,174 @@ +DeficiencyType | huetiful-js

Type alias DeficiencyType

DeficiencyType: "red" | "blue" | "green" | "monochromacy"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.DivergingScheme.html b/docs/types/types.DivergingScheme.html new file mode 100644 index 00000000..de36da7a --- /dev/null +++ b/docs/types/types.DivergingScheme.html @@ -0,0 +1,174 @@ +DivergingScheme | huetiful-js

Type alias DivergingScheme

DivergingScheme: "Spectral" | "RdYlGn" | "RdBu" | "PiYG" | "PRGn" | "RdYlBu" | "BrBG" | "RdGy" | "PuOr"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.EarthtoneOptions.html b/docs/types/types.EarthtoneOptions.html new file mode 100644 index 00000000..04e6c533 --- /dev/null +++ b/docs/types/types.EarthtoneOptions.html @@ -0,0 +1,174 @@ +EarthtoneOptions | huetiful-js

Type alias EarthtoneOptions

EarthtoneOptions: Omit<Options, "hueStep" | "via" | "maxLightness" | "minLightness">

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.Factor.html b/docs/types/types.Factor.html new file mode 100644 index 00000000..8b97bb62 --- /dev/null +++ b/docs/types/types.Factor.html @@ -0,0 +1,174 @@ +Factor | huetiful-js

Type alias Factor

Factor: "luminance" | "temp" | "saturation" | "contrast" | "distance" | "lightness" | "hue"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.FactorMapper.html b/docs/types/types.FactorMapper.html new file mode 100644 index 00000000..9d8a6354 --- /dev/null +++ b/docs/types/types.FactorMapper.html @@ -0,0 +1,174 @@ +FactorMapper | huetiful-js

Type alias FactorMapper

FactorMapper: ((factor, callback, order?, colorObj?) => ((colors) => ColorToken[]))

Type declaration

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.HueColorSpaces.html b/docs/types/types.HueColorSpaces.html new file mode 100644 index 00000000..c3170040 --- /dev/null +++ b/docs/types/types.HueColorSpaces.html @@ -0,0 +1,174 @@ +HueColorSpaces | huetiful-js

Type alias HueColorSpaces

HueColorSpaces: UniformColorSpaces | "hsl" | "hsv" | "hsi" | "hwb" | "okhsl" | "okhsv"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.HueFamily.html b/docs/types/types.HueFamily.html new file mode 100644 index 00000000..e024f0d2 --- /dev/null +++ b/docs/types/types.HueFamily.html @@ -0,0 +1,174 @@ +HueFamily | huetiful-js

Type alias HueFamily

HueFamily: "red-purple" | "red" | "yellow-red" | "yellow" | "green-yellow" | "green" | "blue-green" | "blue" | "purple-blue" | "purple"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.HueShiftOptions.html b/docs/types/types.HueShiftOptions.html new file mode 100644 index 00000000..2b70aab1 --- /dev/null +++ b/docs/types/types.HueShiftOptions.html @@ -0,0 +1,174 @@ +HueShiftOptions | huetiful-js

Type alias HueShiftOptions

HueShiftOptions: Omit<Options, "via" | "earthtones" | ""> & InterpolatorOptions

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.Interpolator.html b/docs/types/types.Interpolator.html new file mode 100644 index 00000000..8a10b292 --- /dev/null +++ b/docs/types/types.Interpolator.html @@ -0,0 +1,174 @@ +Interpolator | huetiful-js

Type alias Interpolator

Interpolator: ((arr) => ((t) => number))

Type declaration

    • (arr): ((t) => number)
    • Parameters

      • arr: number[]

      Returns ((t) => number)

        • (t): number
        • Parameters

          • t: number

          Returns number

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.InterpolatorOptions.html b/docs/types/types.InterpolatorOptions.html new file mode 100644 index 00000000..e965f483 --- /dev/null +++ b/docs/types/types.InterpolatorOptions.html @@ -0,0 +1,174 @@ +InterpolatorOptions | huetiful-js

Type alias InterpolatorOptions

InterpolatorOptions: Pick<Options, "easingFunc" | "hueInterpolator" | "chromaInterpolator" | "hueFixup" | "lightnessInterpolator">

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.Options.html b/docs/types/types.Options.html new file mode 100644 index 00000000..de023249 --- /dev/null +++ b/docs/types/types.Options.html @@ -0,0 +1,193 @@ +Options | huetiful-js

Type alias Options

Options: {
    chromaInterpolator?: Interpolator;
    earthtones?: "light-gray" | "silver" | "sand" | "tupe" | "mahogany" | "brick-red" | "clay" | "cocoa" | "dark-brown" | "dark";
    easingFunc?: ((t) => number);
    hueFixup?: ((arr) => number[]);
    hueInterpolator?: Interpolator;
    hueStep?: number;
    lightnessInterpolator?: Interpolator;
    maxLightness?: number;
    minLightness?: number;
    samples?: number;
    via?: Tone;
}

Type declaration

  • Optional chromaInterpolator?: Interpolator

    Param

    interpolation method to use on the chroma channel.

    +
  • Optional earthtones?: "light-gray" | "silver" | "sand" | "tupe" | "mahogany" | "brick-red" | "clay" | "cocoa" | "dark-brown" | "dark"
      +
    • +
    +

    Param

    The earthtone to interpolate with.

    +
  • Optional easingFunc?: ((t) => number)
      • (t): number
      • The easing function to use.

        +

        Parameters

        • t: number

          Any value between 0 and 1

          +

        Returns number

        A number.

        +
  • Optional hueFixup?: ((arr) => number[])
      • (arr): number[]
      • Parameters

        • arr: number[]

        Returns number[]

  • Optional hueInterpolator?: Interpolator

    Param

    interpolation method to use on the hue channel.

    +
  • Optional hueStep?: number

    Param

    amount of hue angles to increment each iteration with.

    +
  • Optional lightnessInterpolator?: Interpolator

    Param

    interpolation method to use on the lightness channel.

    +
  • Optional maxLightness?: number

    Param

    Maximum lightness value (range 0-100).

    +
  • Optional minLightness?: number
      +
    • +
    +

    Param

    Minimum lightness value (range 0-100).

    +
  • Optional samples?: number

    Param

    amount of samples to return in the result collection.

    +
  • Optional via?: Tone

    Param

    color to pass through during interpolation.

    +

Description

The override parameters for palette functions.

+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.Order.html b/docs/types/types.Order.html new file mode 100644 index 00000000..a4f48bc7 --- /dev/null +++ b/docs/types/types.Order.html @@ -0,0 +1,174 @@ +Order | huetiful-js

Type alias Order

Order: "asc" | "desc"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.PairedSchemeOptions.html b/docs/types/types.PairedSchemeOptions.html new file mode 100644 index 00000000..e58fb957 --- /dev/null +++ b/docs/types/types.PairedSchemeOptions.html @@ -0,0 +1,174 @@ +PairedSchemeOptions | huetiful-js

Type alias PairedSchemeOptions

PairedSchemeOptions: Omit<Options, "earthtones" | "maxLightness" | "minLightness">

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.QualitativeScheme.html b/docs/types/types.QualitativeScheme.html new file mode 100644 index 00000000..dac306b7 --- /dev/null +++ b/docs/types/types.QualitativeScheme.html @@ -0,0 +1,174 @@ +QualitativeScheme | huetiful-js

Type alias QualitativeScheme

QualitativeScheme: "Set2" | "Accent" | "Set1" | "Set3" | "Dark2" | "Paired" | "Pastel2" | "Pastel1"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.ScaleValues.html b/docs/types/types.ScaleValues.html new file mode 100644 index 00000000..538c30d7 --- /dev/null +++ b/docs/types/types.ScaleValues.html @@ -0,0 +1,174 @@ +ScaleValues | huetiful-js

Type alias ScaleValues

ScaleValues: "50" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.SequentialScheme.html b/docs/types/types.SequentialScheme.html new file mode 100644 index 00000000..7ce780c4 --- /dev/null +++ b/docs/types/types.SequentialScheme.html @@ -0,0 +1,174 @@ +SequentialScheme | huetiful-js

Type alias SequentialScheme

SequentialScheme: "OrRd" | "PuBu" | "BuPu" | "Oranges" | "BuGn" | "YlOrBr" | "YlGn" | "Reds" | "RdPu" | "Greens" | "YlGnBu" | "Purples" | "GnBu" | "Greys" | "YlOrRd" | "PuRd" | "Blues" | "PuBuGn" | "Viridis"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.TailwindColorFamilies.html b/docs/types/types.TailwindColorFamilies.html new file mode 100644 index 00000000..fa6794d2 --- /dev/null +++ b/docs/types/types.TailwindColorFamilies.html @@ -0,0 +1,174 @@ +TailwindColorFamilies | huetiful-js

Type alias TailwindColorFamilies

TailwindColorFamilies: "indigo" | "gray" | "zinc" | "neutral" | "stone" | "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "sky" | "blue" | "violet" | "purple" | "fuchsia" | "pink" | "rose"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.Tone.html b/docs/types/types.Tone.html new file mode 100644 index 00000000..7133cc2a --- /dev/null +++ b/docs/types/types.Tone.html @@ -0,0 +1,174 @@ +Tone | huetiful-js

Type alias Tone

Tone: "light" | "dark"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.UniformColorSpaces.html b/docs/types/types.UniformColorSpaces.html new file mode 100644 index 00000000..7ac45bb2 --- /dev/null +++ b/docs/types/types.UniformColorSpaces.html @@ -0,0 +1,174 @@ +UniformColorSpaces | huetiful-js

Type alias UniformColorSpaces

UniformColorSpaces: "lch" | "jch" | "dlch" | "lch" | "lch65" | "lchuv" | "oklch"

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/types.callback.html b/docs/types/types.callback.html new file mode 100644 index 00000000..286477f4 --- /dev/null +++ b/docs/types/types.callback.html @@ -0,0 +1,174 @@ +callback | huetiful-js

Type alias callback

callback: unknown

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/variables/helpers.interpolatorConfig.html b/docs/variables/helpers.interpolatorConfig.html new file mode 100644 index 00000000..ad67191b --- /dev/null +++ b/docs/variables/helpers.interpolatorConfig.html @@ -0,0 +1,177 @@ +interpolatorConfig | huetiful-js

Variable interpolatorConfigConst

interpolatorConfig: {
    chromaInterpolator: Interpolator;
    easingFunc: ((t) => number);
    hueFixup: ((arr) => number[]);
    hueInterpolator: Interpolator;
    lightnessInterpolator: Interpolator;
} = ...

Type declaration

  • chromaInterpolator: Interpolator
  • easingFunc: ((t) => number)
      • (t): number
      • The easing function to use.

        +

        Parameters

        • t: number

          Any value between 0 and 1

          +

        Returns number

        A number.

        +
  • hueFixup: ((arr) => number[])
      • (arr): number[]
      • Parameters

        • arr: number[]

        Returns number[]

  • hueInterpolator: Interpolator
  • lightnessInterpolator: Interpolator

Generated using TypeDoc

\ No newline at end of file diff --git a/guides/color_schemes.md b/guides/color_schemes.md deleted file mode 100644 index 76ab647d..00000000 --- a/guides/color_schemes.md +++ /dev/null @@ -1,15 +0,0 @@ -# Color Schemes - -This project contains the map of predefined color scales from [colorBrewer]() . These color scales are returned in theform of wrapper functions for convenience of calling a certain scale from each scheme type. - -The included scheme types are: - -- qualitative -- diverging -- sequential - -## Parameters - -Since there are just wrapper functions over a map of colors, they all take one parameter which is the name of the scheme type: - -### API diff --git a/guides/converters.md b/guides/converters.md deleted file mode 100644 index 10944dab..00000000 --- a/guides/converters.md +++ /dev/null @@ -1,20 +0,0 @@ -# Converters - -There a few converter functions in the library that can parse colors from different formats. - -The supported color formats are: - -- Arrays in the form of `[mode,number,number,number,number?]` . -The `mode` is the color space for example 'hsl' which we pass our channel values to. The fourth optional number value is the `alpha` or opacity channel which expects a value between [0,1], any value beyond this domain is normalized back to that range. - -- Any value of the type `Number` between 0 and 16,777,215. For example a binary number e.g `0x3af1d3` - -- Plain objects with channels as keys ,a required `mode` property and an optional alpha property. For example: `{ mode:'rgb', r:20, g:40, b:100, alpha:0.5 }` . - -- CSS named colors for example 'blue' or 'antiquewhite' . - -- And of course hexadecimal strings. - -### API - - diff --git a/guides/filterBy.md b/guides/filterBy.md deleted file mode 100644 index 2b3e8f91..00000000 --- a/guides/filterBy.md +++ /dev/null @@ -1,16 +0,0 @@ -# Filtering colors - -The `filterBy` module has functions for filtering colors from a collection. The colors that don't match the filtering conditions are removed from the result array. - -## Parameters - -The functions take the same base parameters: `(colors:Color[],start:number|string,end?:number):Colors[]` -The reason why the `start` parameter is a union type of `string|number` is because we can pass an expression as astring allowing us to skip the optional `end` parameter completely. - -The operator is expected to ocuppy the first index of the string"<5" which means return colors that are **less than** 5 (of whatever is being used for comparison e.g lightness.) . Or in the instance of testing for a value greater than or equal to, in that case the operator takes two indexes: 0 and 1. For example, ">=5" means return colors that are **greater than or equal to** 5. -> Remember: -> Don't include whitespaces in expression string. - -Coupled with a JavaScript language feature like template literals, we can even pass in the value and operators dynamically, allowing us to fine tune what we want in our palette according to changes in the template variable's value: - -### API \ No newline at end of file diff --git a/guides/getters_and_setters.md b/guides/getters_and_setters.md deleted file mode 100644 index e04a6f71..00000000 --- a/guides/getters_and_setters.md +++ /dev/null @@ -1,7 +0,0 @@ -# Getters and setters - -For operations where you may need to query the value before you can run other instructions, the library has some functions for retrieving such values such as an individual channel's value, getting its luminance et cetera. - -> You can pass any recognized color token because there all guarded by `toHex()` under the hood. - -### API \ No newline at end of file diff --git a/guides/palettes.md b/guides/palettes.md deleted file mode 100644 index 6013b1c4..00000000 --- a/guides/palettes.md +++ /dev/null @@ -1,7 +0,0 @@ -# Palettes - -A small collection of functions to help you kickstart your designs using mostly just a single base color and a few adjustable built-in parameters. All palette functions use `easingSmoothstep()` easing under the hood to ease certain values on channels being worked with. - -Because certain end results are easier to achieve in different color spaces, colors are converted to a mode that is suited for the desired and result. For example pastel colors have a high `value` and low `saturation` there are easier to compute in the HSV color space. But for the most part colors are converted to LCH and then returned as optional hex strings or plain color objects. - -### API \ No newline at end of file diff --git a/guides/sortBy.md b/guides/sortBy.md deleted file mode 100644 index a0c17777..00000000 --- a/guides/sortBy.md +++ /dev/null @@ -1,10 +0,0 @@ -# Sorting utilities - -The `sortBy` module is a collection of sorting functions that can sort collections of colors by using the values of their attributes for example lightness,hue angle and luminance. - -## Parameters - -The functions take 2 similar parameters in common; a collection of colors and an optional sorting order. The default is ascending. - - -### API \ No newline at end of file diff --git a/huetiful-logo.png b/huetiful-logo.png index e89e4861..8348f68d 100644 Binary files a/huetiful-logo.png and b/huetiful-logo.png differ diff --git a/huetiful.code-workspace b/huetiful.code-workspace new file mode 100644 index 00000000..0885b0e3 --- /dev/null +++ b/huetiful.code-workspace @@ -0,0 +1,10 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "liveServer.settings.multiRootWorkspaceName": "huetiful" + } +} diff --git a/jasmine.json b/jasmine.json new file mode 100644 index 00000000..50b60aef --- /dev/null +++ b/jasmine.json @@ -0,0 +1,9 @@ +{ + "spec_dir": "test", + "spec_files": ["**/*[sS]pec.?(m)js"], + "helpers": ["helpers/**/*.?(m)js"], + "env": { + "stopSpecOnExpectationFailure": false, + "random": true + } +} diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index 40174ac0..00000000 --- a/nodemon.json +++ /dev/null @@ -1,2 +0,0 @@ -{"exec":"node app.js --watch " -} \ No newline at end of file diff --git a/notes/about-colorspaces.md b/notes/about-colorspaces.md new file mode 100644 index 00000000..bb44f698 --- /dev/null +++ b/notes/about-colorspaces.md @@ -0,0 +1,40 @@ +# About colorspaces + +This library makes use of the predefined colorspaces provided by Culori. Information about the expected channel ranges, supported colorspaces and other related information on how the library handles them [can be found here](https://culorijs.org/color-spaces/) + +## Channel range normalization + +Because of how Culori defines its colorspaces it may feel unintuitive to, let's say for example pass RGB channel values in the range [0,1] since most developers are familiar of the [0,255] range, which isn't compatible with Culori and will return unexpected results: + +```js + +import { formatHex } from 'culori'; + +// The range [0,1] expected by Culori +let colorA = { r: 1, g: 0.5, b: 0.05, mode: 'rgb' }; +console.log(formatHex(colorA)); +// #ff800d + +// Also a valid range but not supported by Culori +let colorB = { r: 100, g: 58, b: 43.51, mode: 'rgb' }; +console.log(formatHex(colorB)); +// #ffffff + + +``` + +Note that `colorB` is returned as pure white. This same color token will return the correct color if we pass it to the `toHex` converter, which is generic: + +```js + +import { toHex } from 'huetiful-js'; + +let colorB = { r: 100, g: 58, b: 43.51, mode: 'rgb' }; + +console.log(toHex(colorB)); +// #643a2c + + +``` + +The `toHex` utility will treat the color to be in the range [0,255] if any of the channel values is greater than 1. This is in contrast to the way Culori handles such a scenario where it will just clamp the values to the [0,1] ranges. The formula is very simple: `channel / 255` which will give us our channel value in the supported range. This behaviour also applies to color tokens passed as arrays. diff --git a/notes/about-generators.md b/notes/about-generators.md new file mode 100644 index 00000000..e7fd1d73 --- /dev/null +++ b/notes/about-generators.md @@ -0,0 +1 @@ +# Working with the color generators diff --git a/notes/huetiful-logo.png b/notes/huetiful-logo.png new file mode 100644 index 00000000..8348f68d Binary files /dev/null and b/notes/huetiful-logo.png differ diff --git a/notes/index.md b/notes/index.md new file mode 100644 index 00000000..4733c47f --- /dev/null +++ b/notes/index.md @@ -0,0 +1,74 @@ +![Huetiful](/huetiful-logo.png) + +Open source TypeScript library for general purpose color manipulations and generating custom color scales based on Culori. + +## Getting started⛳ + +### Node + +The library🧾 is on npm as a package📦 for use in NodeJS: + +```bash +npm i huetiful-js +``` + +You can use a CDN in this example, jsdelivr to load the library remotely: + +```js +import {...} from 'https://cdn.jsdelivr.net/npm/huetiful-js/lib/huetiful.esm.min.mjs' + +``` + +### Browser + +Or load the library as a UMD glabal (`huetiful`) in your HTML file using a ` +``` + +## Modules + +Each module has a corresponding Typescript source file [in the repository](https://github.com/prjctimg/huetiful). + +- [colors](modules/colors.html) +- [converters](modules/converters.html) +- [filterBy](modules/filterBy.html) +- [generators](modules/generators.html) +- [helpers](modules/helpers.html) +- [sortBy](modules/sortBy.html) +- [types](modules/types.html) +- [utils](modules/utils.html) + +## What's next🤷🏽‍♂️ + +> The possibilities are limited by the imagination🤯 of the user._ +> ~ me :smile: + +[See the full docs here📜](https:prjctimg.github.io/huetiful) + +## Need help😣 ? + +See some unexpected results😖? [Check the issue tracker](https://github.com/prjctimg/huetiful/issues) to open an issue or search for the problem to see if your issue already exists or has been resolved. + +Would like to join the chat🗣️ and share ideas💡 and suggestions💭 ? [See the discussions and just say hi, or share a coding meme(whatever breaks the ice🏔️)](https://github.com/prjctimg/huetiful/discussions) + +## Contributing👐🏾 + +This project is fully open source so contributions are welcome! Help make this project better by suggesting improvements or features and patching bugs🐛. See🔍 the [CONTRIBUTING](./CONTRIBUTING.md) file for more information on how to get started. + +## Donating + +This project is completely free and will remain so forever. But if you wish to support the development of this project or just buy the developer a coffee, please feel free. + +## References🔗 + +[Coloring with code: A programmatic approach by George Francis](https://tympanus.net/codrops/2021/12/07/coloring-with-code-a-programmatic-approach-to-design/) + +> ###### License +> +> Copyright (c) 2023, +> Dean Tarisai and contributors +> huetiful-js is released under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) license. diff --git a/notes/quickstart.md b/notes/quickstart.md new file mode 100644 index 00000000..8e170d86 --- /dev/null +++ b/notes/quickstart.md @@ -0,0 +1,340 @@ +# Quickstart + +## A guide to working with color programmatically + +#### What's a color 🤔? + +A color can be defined using different data types(arrays, strings, numbers plain objects). This allows us to work with color in almost any format and flexibility in how we want to define our color. Below are some examples listing all the supported formats of passing in color values and their respective conversion functions: + +```js +import { num2rgb, toHex } from 'huetiful-js' + +let cssNamedColor = 'pink' +let colorNumber = 5000 +let colorObject = { l: 50, c: 20, h: 40, mode: 'lch' } +let colorObjectWithAlpha = { l: 50, c: 20, h: 40, alpha: 0.5, mode: 'lch' } +let arrColor = ['rgb', 120, 80, 50] +let arrColorWithAlpha = ['rgb', 120, 80, 50, 0.1] + + +// Converting CSS named colors to hex +console.log(toHex(cssNamedColor)) +// #ffc0cb + +// Converting a number to an RGB object +console.log(num2rgb(colorNumber, true)) +// #001388 + +// Converting a color object to a 6 character hex (without the alpha value) +console.log(toHex(colorObject)) +// #956d62 + +// Converting a color object with an alpha property to an 8 character hex code (including the alpha channel) +console.log(toHex(colorObjectWithAlpha)) +// #956d6280 + +// Converting an array of channel values to a 6 character hex code. +console.log(toHex(arrColor)) +// #785032 + +// Converting an array of channel values (including the alpha channel) to an 8 character hex +console.log(toHex(arrColorWithAlpha)) +//#7850321a + + +``` + +> ℹ️ [See here](https://culorijs.org/color-spaces +) and the expected channel ranges or [more on converter functions](https://prjctimg.github.io/huetiful/modules/converters.html) page 🔗. + +#### TailwindCSS colors🎨 + +As a starting point the library comes along with the default TailwindCSS palette included. This helps you get started easier when you're using [palette functions](https://prjctimg.github.io/huetiful/modules/generators.html) such as `hueShift()` and `earthtone()` + +The Tailwind colors can be accessed from two wrapper functions, `tailwindColors` and `colors` , that both take the same parameters but `colors` takes both parameters at once while `tailwindColors` is curried. Here's an example showing the differences between the two functions: + +```js + import { tailwindColors , colors } from "huetiful-js"; + +// We pass in red as the target hue. +// It returns a function that can be called with an optional value parameter +let red = tailwindColors("red"); +console.log(red()); +// [ + '#fef2f2', '#fee2e2', + '#fecaca', '#fca5a5', + '#f87171', '#ef4444', + '#dc2626', '#b91c1c', + '#991b1b', '#7f1d1d' +] + + +console.log(red(100)); +// '#fee2e2' + +console.log(red('900')); +// '#7f1d1d' + + ////// example for colors() ////// + +// colors() has a builtin parameter called 'all' that returns all colors at the specified value +let all300 = colors("all", 300); + +console.log(all300) +//[ + '#cbd5e1', '#d1d5db', '#d4d4d8', + '#d4d4d4', '#d6d3d1', '#fca5a5', + '#fdba74', '#fcd34d', '#fde047', + '#bef264', '#86efac', '#6ee7b7', + '#5eead4', '#7dd3fc', '#93c5fd', + '#c4b5fd', '#d8b4fe', '#f0abfc', + '#f9a8d4', '#fda4af' +] + +let red = colors("red"); +console.log(red); + +// [ + '#fef2f2', '#fee2e2', + '#fecaca', '#fca5a5', + '#f87171', '#ef4444', + '#dc2626', '#b91c1c', + '#991b1b', '#7f1d1d' +] + +let red100 = colors("red", 100); + +console.log(red100) +// #fee2e2 + +``` + +### Working with arrays of color🎨 + +We can sort and filter colors using their property or channel values values like saturation,lightness and even contrast! +Here are some example using the filtering and sorting functions on an array of colors: + +#### Sorting colors + +An example of sorting colors by the relative luminance as defined by the WCAG 2.0 formula + +```js +import { sortByLuminance } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + + + +let sorted = sortByLuminance(sample) +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + +// Note that you can specify the order as either ascending (`asc`) or descending (`desc`). The default is ascending. : + +let sortedDescending = sortByLuminance(sample, "desc"); +console.log(sortedDescending) +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + +``` + +#### Filtering colors + +An example of filtering colors by the value of the hue angle. The function uses the Jch colorspace because of its perceptual uniformity. [George Francis explains this phenomena in detail here.](https://tympanus.net/codrops/2021/12/07/coloring-with-code-a-programmatic-approach-to-design/) + +```js + let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByHue(sample, 20, 80) + +// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] + + // We can even use expressions as the condition e.g '>=50' which means return the colors with a hue angle greater than or equal to 50 + + // Here are some examples +console.log(filterByHue(sample, '>100') +) +// [ '#00ffdc', '#00ff78', '#00c000', '#007e00', '#164100' ] + +console.log(filterByHue(sample, '<=100') +) +// [ '#ffff00', '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] + +``` + +[See more about the parameter types and other filtering functions](https://prjctimg.github.io/huetiful/modules/sortBy.html) + +### Palette generators + +A few simple palette generator functions are included in the library. One of my favourites is `hueShift` (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) . + +```js +import { hueShift } from "huetiful-js"; + +let hueShiftedPalette = hueShift("#3e0000", {}, true); + +console.log(hueShiftedPalette); + +// [ + '#ffffe1', '#ffdca5', + '#ca9a70', '#935c40', + '#5c2418', '#3e0000', + '#310000', '#34000f', + '#38001e', '#3b002c', + '#3b0c3a' +] + + + +``` + +[See more palette generator functions](https://prjctimg.github.io/huetiful/modules/generators.html) + +### Predicates⚖️ + +Is this color cool🥶 or warm 🥵, is it achromatic (grayscale) or chromatic? Though its easy to tell colors apart visually when they're displayed on the screen📺 it can be a bit confusing to tell colors apart using code🔢. Below is an example showing how to determine if a color is gray or not: + +```js + +import { isAchromatic } from "huetiful-js"; +import { formatHex8, interpolate, samples } from "culori" + + +isAchromatic('pink') +// false + +let sample = [ + "#164100", + "#ffff00", + "#310000", + 'pink' +]; + +console.log(map(sample, isAchromatic)); + +// [false, false, false,false] + +isAchromatic('gray') +// true + + + +// Here are using some of Culori's functions to demonstrate this example +// we create an interpolation using black and white +let f = interpolate(["black", "white"]); + +//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function. +// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.) +let grays = map(samples(12), (c) => formatHex8(f(c))); +console.log(map(grays, isAchromatic)); + +// The last two colors are false because we can't categorize black and white as achromatic. + +// + [ false, true, true, + true, true, true, + true, true, true, + true, true, false +] + +``` + +Here's an example🎆 showing how we can check if a color is cool using one of the predicate functions: + +```js +import { isCool } from 'huetiful-js' + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000" +]; + + +console.log(isCool(sample[0])); +// false + +console.log(map(sample, isCool)); + +// [ true, false, true] + + +``` + +Another use👷 case would be passing the predicate to an array method like `filter` to filter a collection of colors removing colors that return false for the passed in predicate. In the following example we use is `isWarm` to only return warm colors: + +```js +import { isWarm } from 'huetiful-js' + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000" +]; + +console.log(sample.filter(isWarm)) +// [ '#00ff78' ] + + +``` + +Or maybe we want to know which color has the furthest hue distance in our sample collection against our target color 🤔: + +```js + +import { getFarthestHue } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(getFarthestHue('lime', sample, 'lch')) +// 112.60431681589854 + +``` diff --git a/package-lock.json b/package-lock.json index 67a261d4..3e891d1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "huetiful-js", - "version": "1.7.6", + "version": "1.79.94", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "huetiful-js", - "version": "1.7.6", - "license": "Apache 2.0", + "version": "1.79.94", + "license": "Apache-2.0", "dependencies": { "culori": "^3.0.1" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.10", "@types/culori": "^2.0.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", @@ -20,9 +21,15 @@ "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", + "feather-icons": "^4.29.1", + "github-emoji": "^1.2.0", "husky": "^8.0.3", "nodemon": "^3.0.1", - "typescript": "^4.9.5" + "tailwindcss": "^3.4.1", + "typedoc": "^0.25.3", + "typedoc-material-theme": "^1.0.2", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.0.2" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -37,6 +44,18 @@ "node": ">=0.10.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", @@ -436,33 +455,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", + "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -482,29 +478,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -524,6 +497,156 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@material/material-color-utilities": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz", + "integrity": "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -559,6 +682,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/utils": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", @@ -579,10 +712,38 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@types/culori": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/culori/-/culori-2.0.0.tgz", - "integrity": "sha512-bKpEra39sQS9UZ+1JoWhuGJEzwKS0dUkNCohVYmn6CAEBkqyIXimKiPDRZWtiOB7sKgkWMaTUpHFimygRoGIlg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/culori/-/culori-2.0.4.tgz", + "integrity": "sha512-GeLW8+KBRkwqIgeGrU8EnNbBE2D7waYbQHkx2xnI5exlzSGTMpjWtDaHzLWK1PTYmyJN9u6dPvMYumFevDe+VA==", "dev": true }, "node_modules/@types/json-schema": { @@ -592,22 +753,22 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", - "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", - "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", + "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/type-utils": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/type-utils": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -632,39 +793,16 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", - "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", + "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", "debug": "^4.3.4" }, "engines": { @@ -683,37 +821,14 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", - "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", + "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0" + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -724,13 +839,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", - "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", + "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/utils": "6.12.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -750,33 +865,10 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", - "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -787,13 +879,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", - "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", + "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -813,41 +905,18 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", - "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", "semver": "^7.5.4" }, "engines": { @@ -862,12 +931,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", - "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/types": "6.12.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -936,6 +1005,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -951,6 +1026,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -964,6 +1045,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -986,9 +1073,9 @@ "dev": true }, "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", "dev": true, "engines": { "node": ">=0.6" @@ -1061,6 +1148,15 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1077,27 +1173,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1125,6 +1200,24 @@ "fsevents": "~2.3.2" } }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "dev": true + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1157,12 +1250,32 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/core-js": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.35.0.tgz", + "integrity": "sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1177,21 +1290,41 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/culori": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/culori/-/culori-3.0.1.tgz", - "integrity": "sha512-XIGHoABtrJwxhzoTVA2BeyxFJ9jgiWkYHSBNp6NGAvtsl1QgQWN8a7nrp4LoxkRRWP0jxATeVTitD3kFroosZw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz", + "integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/deep-is": { @@ -1246,6 +1379,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1258,6 +1397,12 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1271,9 +1416,9 @@ } }, "node_modules/dts-bundle-generator": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-8.0.1.tgz", - "integrity": "sha512-9JVw78/OXdKfq+RUrmpLm6WAUJp+aOUGEHimVqIlOEH2VugRt1I8CVIoQZlirWZko+/SVZkNgpWCyZubUuzzPA==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-8.1.2.tgz", + "integrity": "sha512-/yvy9Xw0cfFodA8n6jEq8/COZ/WXgJtPabnLBAzIfP/TfxWbD/0a0dvfqNHneNqswQrH0kUcaAfGJC9UNvH97w==", "dev": true, "dependencies": { "typescript": ">=5.0.2", @@ -1286,18 +1431,11 @@ "node": ">=14.0.0" } }, - "node_modules/dts-bundle-generator/node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -1364,15 +1502,15 @@ } }, "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", + "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", + "@eslint/js": "8.54.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -1487,41 +1625,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -1632,6 +1735,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1653,6 +1768,16 @@ "reusify": "^1.0.4" } }, + "node_modules/feather-icons": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.29.1.tgz", + "integrity": "sha512-P1we61haGTds6lKWe6CCVPsNULb8tHr1y6S9gXEpU+lNR1Ja7GdV0A1l2hTNmzXv+0Stix/3YMWMAn7n1Qtd6A==", + "dev": true, + "dependencies": { + "classnames": "^2.2.5", + "core-js": "^3.1.3" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1694,9 +1819,9 @@ } }, "node_modules/flat-cache": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", - "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { "flatted": "^3.2.9", @@ -1704,7 +1829,7 @@ "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { @@ -1713,6 +1838,34 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1720,9 +1873,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -1733,6 +1886,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1754,6 +1916,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/github-emoji": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/github-emoji/-/github-emoji-1.2.0.tgz", + "integrity": "sha512-+oqVqVMjVR7AUvTsaYkXAIyqFga+YyBfBCQd3kmXPoKuNXexkolaMuFE15//7WNAHyFv3sAF3rjQqoVFGwEK8g==", + "dev": true + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1775,15 +1943,15 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/globals": { @@ -1827,13 +1995,46 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/human-signals": { @@ -1861,9 +2062,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "engines": { "node": ">= 4" @@ -1928,6 +2129,18 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", @@ -2054,6 +2267,33 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2084,6 +2324,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2106,6 +2352,21 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2121,6 +2382,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2139,6 +2412,24 @@ "node": ">=10" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -2191,16 +2482,69 @@ "node": "*" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "node_modules/nodemon": { @@ -2231,6 +2575,36 @@ "url": "https://opencollective.com/nodemon" } }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -2282,6 +2656,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2410,6 +2802,37 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2437,6 +2860,170 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2447,9 +3034,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "peer": true, "bin": { @@ -2509,6 +3096,15 @@ } ] }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2530,6 +3126,23 @@ "node": ">=0.10.0" } }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2727,6 +3340,18 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", + "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -2754,6 +3379,24 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2768,6 +3411,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2780,6 +3438,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -2804,16 +3475,96 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/synckit": { @@ -2832,12 +3583,70 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/titleize": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", @@ -2886,6 +3695,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -2916,17 +3731,113 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typedoc": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.3.tgz", + "integrity": "sha512-Ow8Bo7uY1Lwy7GTmphRIMEo6IOZ+yYUyrc8n5KXIZg1svpqhZSWgni2ZrDhe+wLosFS8yswowUzljTAV/3jmWw==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" + } + }, + "node_modules/typedoc-material-theme": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typedoc-material-theme/-/typedoc-material-theme-1.0.2.tgz", + "integrity": "sha512-/nH/twYeHrnz5sZaaXzYJ85EOgKqnbl1ivzBKmuEAga1dBsARttwQUTPKAT7XrCPD+rRcoqxuCOdXZ6EGiqRQA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paypal.me/dmnsgn" + }, + { + "type": "individual", + "url": "https://commerce.coinbase.com/checkout/56cbdf28-e323-48d8-9c98-7019e72c97f3" + } + ], + "dependencies": { + "@material/material-color-utilities": "^0.2.7" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.6.0" + }, + "peerDependencies": { + "typedoc": "^0.25.3" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" } }, "node_modules/undefsafe": { @@ -2953,6 +3864,24 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2968,6 +3897,12 @@ "node": ">= 8" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2985,6 +3920,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3006,6 +3959,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 783ae809..b928f0e4 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,18 @@ { "name": "huetiful-js", - "version": "1.7.6", + "version": "1.79.94", "type": "module", - "module": "./dist/huetiful.esm.mjs", - "browser": "./dist/huetiful.min.js", - "exports": { - "./fp": "./dist/fp/index.esm.mjs", - "./getters_and_setters/index.esm.mjs": "./dist/getters_and_setters/index.esm.mjs", - "./converters": "./dist/converters/index.esm.mjs", - "./palettes": "./dist/palettes/index.esm.mjs", - "./filterBy": "./dist/filterBy/index.esm.mjs", - "./sortBy": "./dist/sortBy/index.esm.mjs", - "./colors": "./dist/colors/index.esm.mjs", - ".": "./dist/huetiful.esm.mjs" - }, - "types": "./dist/index.d.ts", - "description": "JavaScript library for general purpose color manipulations based on Culori.", + "main": "./lib/huetiful.esm.mjs", + "module": "./lib/huetiful.esm.mjs", + "browser": "./lib/huetiful.esm.mjs", + "jsdelivr": "./lib/huetiful.umd.js", + "types": "./lib/huetiful.d.ts", + "description": "Open source TypeScript library for general purpose color manipulations and generating custom color scales.", "dependencies": { "culori": "^3.0.1" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.10", "@types/culori": "^2.0.0", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", @@ -28,52 +21,116 @@ "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", + "feather-icons": "^4.29.1", + "github-emoji": "^1.2.0", "husky": "^8.0.3", "nodemon": "^3.0.1", - "typescript": "^4.9.5" + "tailwindcss": "^3.4.1", + "typedoc": "^0.25.3", + "typedoc-material-theme": "^1.0.2", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.0.2" }, "scripts": { - "build": " node build.cjs", - "prepare": " npm run build && npm run types", - "types": "dts-bundle-generator -o ./dist/index.d.ts ./src/index.ts", - "docs": "cd docs && npm run build:docs && npm run deploy:docs", - "lint": "eslint . --ext .ts", - "format": "prettier --config .prettierrc.json \"src/**/*.ts\" --write" + "test": "jasmine", + "docs": "npx typedoc --themeColor '#065f46' & cp -r huetiful-logo.png docs & git add .", + "code:build": "node build.cjs", + "code:prepare": " npm run code:build & npm run code:types & npm run docs", + "code:types": "dts-bundle-generator -o ./lib/huetiful.d.ts ./src/index.ts", + "code:lint": "eslint --fix --ext .ts", + "code:format": "prettier \"./src/*.ts\" --write", + "start": "nodemon app.js --watch " }, "husky": { - "hooks": { - "pre-commit": " echo \"[Husky] pre-commit\" && npm run format && npm run lint", - "pre-push": "git pull" + "hooks": {} + }, + "prettier": { + "semi": true, + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "none", + "bracketSpacing": true + }, + "eslintConfig": { + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint", + "prettier" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "rules": { + "prefer-const": 0, + "no-console": 1, + "no-ternary": 2, + "no-var": 0, + "no-explicit-any": 0, + "prettier/prettier": 0, + "@typescript-eslint/ban-ts-comment": 0, + "no-useless-escape": 0 } }, + "typedocOptions": { + "entryPoints": [ + "./src/generators.ts", + "./src/utils.ts", + "./src/colors.ts", + "./src/converters.ts", + "./src/filterBy.ts", + "./src/sortBy.ts", + "./src/types.d.ts", + "./src/helpers.ts" + ], + "plugin": [ + "typedoc-material-theme" + ], + "themeColor": "#0ea5e9", + "entryPointStrategy": "resolve", + "out": "docs", + "exclude": [ + "./color-maps" + ], + "tsconfig": "./tsconfig.json", + "disableSources": true + }, "files": [ - "dist", - "src" + "lib", + "CHANGELOG.md", + "CODE_OF_CONDUCT.md", + "README.md", + "CONTRIBUTING.md", + "LICENSE.md" ], "repository": { "type": "git", - "url": "git+https://github.com/prjctimg/huetiful.git" + "url": "https://github.com/prjctimg/huetiful.git" }, "keywords": [ - "lch", - "palettes", - "color schemes", + "typescript", + "uniform colorspaces", + "color vision deficiency", "color", "culori", - "gradients", + "interpolation", "tailwind", - "cielab", - "rgb", - "hsl", - "hsv", - "colorbrewer", - "tailwindcss" + "palette generator", + "color brewer" ], "author": "Dean Tarisai", "email": "arcaneqoder@gmail.com", - "homepage": "https://huetiful-docs.vercel.app", - "license": "Apache 2.0", + "homepage": "https://prjctimg.github.io/huetiful", + "license": "Apache-2.0", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "bugs": { + "url": "https://github.com/prjctimg/huetiful/issues" } -} +} \ No newline at end of file diff --git a/spacebook/.eleventy.js b/spacebook/.eleventy.js new file mode 100644 index 00000000..fcc00bfd --- /dev/null +++ b/spacebook/.eleventy.js @@ -0,0 +1,270 @@ +const { DateTime } = require("luxon"); +const CleanCSS = require("clean-css"); +const UglifyJS = require("uglify-es"); +const htmlmin = require("html-minifier"); +const svgContents = require("eleventy-plugin-svg-contents"); +const mdIterator = require('markdown-it-for-inline') +const embedEverything = require('eleventy-plugin-embed-everything'); +const eleventyNavigationPlugin = require('@11ty/eleventy-navigation'); +const Image = require('@11ty/eleventy-img'); +module.exports = function (eleventyConfig) { + // eleventyConfig.addPlugin(pluginTOC); + eleventyConfig.addPlugin(svgContents); + eleventyConfig.addPlugin(embedEverything); + eleventyConfig.addShortcode('version', function () { + return String(Date.now()); + }); + + // Responsive image shortcode + eleventyConfig.addLiquidShortcode( + 'image', + async function (src, alt, sizes = '100vw') { + if (alt === undefined) { + // You bet we throw an error on missing alt (alt="" works okay) + throw new Error(`Missing \`alt\` on responsiveimage from: ${src}`); + } + src = './content/images/' + src; + let metadata = await Image(src, { + widths: [400, 600, 800, 1000, 1200, 1400, 1600, 1900], + formats: ['webp', 'jpeg', 'png'], + urlPath: '/content/images/', + outputDir: './_site/content/images/' + }); + + let lowsrc = metadata.jpeg[0]; + + let picture = ` + ${Object.values(metadata) + .map((imageFormat) => { + return ` `; + }) + .join('\n')} + ${alt} + `; + + return `${picture}`; + } + ); + + eleventyConfig.addLiquidShortcode('icon', function (title, url) { + return '' + title + ''; + }); + + // Button shortcode -- experimental + // eleventyConfig.addLiquidShortcode("button", function(title,url) { + // return ''+title+''; + // }); + + // Tailwind pass through and watch target + eleventyConfig.addWatchTarget('./_tmp/style.css'); + eleventyConfig.addPassthroughCopy({ './_tmp/style.css': './style.css' }); + + // Alpine.js pass through + eleventyConfig.addPassthroughCopy({ + './node_modules/alpinejs/dist/alpine.js': './js/alpine.js' + }); + + // Eleventy Navigation https://www.11ty.dev/docs/plugins/navigation/ + eleventyConfig.addPlugin(eleventyNavigationPlugin); + + // Configuration API: use eleventyConfig.addLayoutAlias(from, to) to add + // layout aliases! Say you have a bunch of existing content using + // layout: post. If you don’t want to rewrite all of those values, just map + // post to a new file like this: + // eleventyConfig.addLayoutAlias("post", "layouts/my_new_post_layout.njk"); + + // Merge data instead of overriding + // https://www.11ty.dev/docs/data-deep-merge/ + eleventyConfig.setDataDeepMerge(true); + + // Add support for maintenance-free post authors + // Adds an authors collection using the author key in our post frontmatter + // Thanks to @pdehaan: https://github.com/pdehaan + // eleventyConfig.addCollection("authors", collection => { + // const blogs = collection.getFilteredByGlob("posts/*.md"); + // return blogs.reduce((coll, post) => { + // const author = post.data.author; + // if (!author) { + // return coll; + // } + // if (!coll.hasOwnProperty(author)) { + // coll[author] = []; + // } + // coll[author].push(post.data); + // return coll; + // }, {}); + // }); + + // Creates custom collection "pages" + eleventyConfig.addCollection('pages', function (collection) { + return collection.getFilteredByGlob('pages/*.md'); + }); + + // Creates custom collection "posts" + // eleventyConfig.addCollection("posts", function(collection) { + // const coll = collection.getFilteredByGlob("posts/*.md"); + + // for(let i = 0; i < coll.length ; i++) { + // const prevPost = coll[i-1]; + // const nextPost = coll[i + 1]; + + // coll[i].data["prevPost"] = prevPost; + // coll[i].data["nextPost"] = nextPost; + // } + + // return coll; + // }); + + // Creates custom collection "results" for search + const searchFilter = require('./filters/searchFilter'); + eleventyConfig.addFilter('search', searchFilter); + eleventyConfig.addCollection('results', (collection) => { + return [...collection.getFilteredByGlob('**/*.md')]; + }); + + // Creates custom collection "menuItems" + eleventyConfig.addCollection('menuItems', (collection) => + collection + .getAll() + .filter(function (item) { + return 'eleventyNavigation' in item.data; + }) + .sort((a, b) => { + return ( + (a.data.eleventyNavigation.order || 0) - + (b.data.eleventyNavigation.order || 0) + ); + }) + ); + + // Date formatting (human readable) + eleventyConfig.addFilter('readableDate', (dateObj) => { + return DateTime.fromJSDate(dateObj).toFormat('LLL dd, yyyy'); + }); + + // Date formatting (machine readable) + eleventyConfig.addFilter('machineDate', (dateObj) => { + return DateTime.fromJSDate(dateObj).toFormat('yyyy-MM-dd'); + }); + + // Minify CSS + eleventyConfig.addFilter('cssmin', function (code) { + return new CleanCSS({}).minify(code).styles; + }); + + // Minify JS + eleventyConfig.addFilter('jsmin', function (code) { + let minified = UglifyJS.minify(code); + if (minified.error) { + console.log('UglifyJS error: ', minified.error); + return code; + } + return minified.code; + }); + + // Minify HTML output + eleventyConfig.addTransform('htmlmin', function (content, outputPath) { + if (outputPath.indexOf('.html') > -1) { + let minified = htmlmin.minify(content, { + useShortDoctype: true, + removeComments: true, + collapseWhitespace: true + }); + return minified; + } + return content; + }); + + // Don't process folders with static assets e.g. images + eleventyConfig.addPassthroughCopy('favicon.ico'); + eleventyConfig.addPassthroughCopy('images/'); + eleventyConfig.addPassthroughCopy('content/images/'); + //eleventyConfig.addPassthroughCopy('admin'); + eleventyConfig.addPassthroughCopy('_includes/assets/'); + eleventyConfig.addPassthroughCopy('_includes/experimental/'); + + // /* Markdown Plugins */ + let markdownIt = require('markdown-it'); + let markdownItAnchor = require('markdown-it-anchor'); + let markdownItEmoji = require('markdown-it-emoji'); + let markdownItFootnote = require('markdown-it-footnote'); + let markdownItContainer = require('markdown-it-container'); + let markdownLinkifyImages = require('markdown-it-linkify-images'); + let markdownItTasks = require('markdown-it-task-lists'); + let markdownItAttrs = require('markdown-it-attrs'); + let markdownItCenterText = require('markdown-it-center-text'); + let options = { + html: true, + breaks: true, + linkify: true, + typographer: true + }; + let opts = { + // permalink: true, + // permalinkClass: "direct-link", + // permalinkSymbol: "#" + }; + + eleventyConfig.setLibrary( + 'md', + markdownIt(options) + .use(mdIterator, 'url_new_win', 'link_open', function (tokens, idx) { + const [attrName, href] = tokens[idx].attrs.find( + (attr) => attr[0] === 'href' + ); + if ( + href && + !href.includes('franknoirot.co') && + !href.startsWith('/') && + !href.startsWith('#') + ) { + tokens[idx].attrPush(['target', '_blank']); + tokens[idx].attrPush(['rel', 'noopener noreferrer']); + } + }) + .use(markdownItAnchor, opts) + .use(markdownItEmoji) + .use(markdownItFootnote) + .use(markdownItContainer, 'callout') + .use(markdownItContainer, 'callout-blue') + .use(markdownItContainer, 'callout-pink') + .use(markdownItContainer, 'callout-green') + .use(markdownItContainer, 'warning') + .use(markdownItTasks) + .use(markdownItCenterText) + .use(markdownLinkifyImages, { + imgClass: 'p-4' + }) + .use(markdownItAttrs, { + includeLevel: [2, 3], + listType: 'ol' + }) + ); + + return { + templateFormats: ['md', 'njk', 'html', 'liquid'], + + // If your site lives in a different subdirectory, change this. + // Leading or trailing slashes are all normalized away, so don’t worry about it. + // If you don’t have a subdirectory, use "" or "/" (they do the same thing) + // This is only used for URLs (it does not affect your file structure) + pathPrefix: '/', + markdownTemplateEngine: 'njk', + htmlTemplateEngine: 'liquid', + dataTemplateEngine: 'njk', + dir: { + input: '.', + includes: '_includes', + data: '_data', + output: '../docs' + } + }; +}; \ No newline at end of file diff --git a/spacebook/.eleventyignore b/spacebook/.eleventyignore new file mode 100644 index 00000000..64dca44c --- /dev/null +++ b/spacebook/.eleventyignore @@ -0,0 +1,4 @@ +README.md +.github/ +functions/* +functions/ diff --git a/spacebook/.gitignore b/spacebook/.gitignore new file mode 100644 index 00000000..de031e75 --- /dev/null +++ b/spacebook/.gitignore @@ -0,0 +1,6 @@ +_site/ +node_modules/ +package-lock.json +.idea +.vscode +*~ diff --git a/spacebook/404.md b/spacebook/404.md new file mode 100644 index 00000000..805df041 --- /dev/null +++ b/spacebook/404.md @@ -0,0 +1,5 @@ +--- +title: 404 +permalink: /404.html +layout: layouts/404.njk +--- diff --git a/spacebook/LICENSE b/spacebook/LICENSE new file mode 100644 index 00000000..1b4beb17 --- /dev/null +++ b/spacebook/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Tim Broeker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/spacebook/_data/site.json b/spacebook/_data/site.json new file mode 100644 index 00000000..684f383c --- /dev/null +++ b/spacebook/_data/site.json @@ -0,0 +1,20 @@ +{ + "name": "huetiful-js", + "subtitle": "Generate color schemes and create flexible color systems with ease.", + "description": "Library for general purpose color manipulations and generating custom color scales.", + "footer": "</> with ❤ in Crowhill.", + "url": "https://prjctimg.github.io/huetiful", + "githubUrl": "https://github.com/prjctimg/huetiful", + "githubBranch": "main", + "navigationStyle": "vertical", + "emoji": "", + "enableSearch": true, + "enableDarkMode": false, + "enableEditButton": true, + "enableDatestamp": true, + "enableGithubLink": true, + "enableNetlifyCMS": false, + "enableComments": false, + "enableEncryption": false, + "enablePageNavigation": true +} diff --git a/spacebook/_includes/assets/css/404.css b/spacebook/_includes/assets/css/404.css new file mode 100644 index 00000000..a5bcaa75 --- /dev/null +++ b/spacebook/_includes/assets/css/404.css @@ -0,0 +1,384 @@ +html, +body { + height: 100%; + width: 100%; + margin: 0px; + background: linear-gradient(90deg, #2f3640 23%, #181b20 100%); +} + +.moon { + background: linear-gradient(90deg, #d0d0d0 48%, #919191 100%); + position: absolute; + top: -100px; + left: -300px; + width: 900px; + height: 900px; + content: ''; + border-radius: 100%; + box-shadow: 0px 0px 30px -4px rgba(0, 0, 0, 0.5); +} + +.moon__crater { + position: absolute; + content: ''; + border-radius: 100%; + background: linear-gradient(90deg, #7a7a7a 38%, #c3c3c3 100%); + opacity: 0.6; +} + +.moon__crater1 { + top: 250px; + left: 500px; + width: 60px; + height: 180px; +} + +.moon__crater2 { + top: 650px; + left: 340px; + width: 40px; + height: 80px; + transform: rotate(55deg); +} + +.moon__crater3 { + top: -20px; + left: 40px; + width: 65px; + height: 120px; + transform: rotate(250deg); +} + +.star { + background: grey; + position: absolute; + width: 5px; + height: 5px; + content: ''; + border-radius: 100%; + transform: rotate(250deg); + opacity: 0.4; + animation-name: shimmer; + animation-duration: 1.5s; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +@keyframes shimmer { + from { + opacity: 0; + } + to { + opacity: 0.7; + } +} + +.star1 { + top: 40%; + left: 50%; + animation-delay: 1s; +} + +.star2 { + top: 60%; + left: 90%; + animation-delay: 3s; +} + +.star3 { + top: 10%; + left: 70%; + animation-delay: 2s; +} + +.star4 { + top: 90%; + left: 40%; +} + +.star5 { + top: 20%; + left: 30%; + animation-delay: 0.5s; +} + +.error { + position: absolute; + left: 100px; + top: 400px; + transform: translateY(-60%); + font-family: 'Righteous', cursive, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + color: #363e49; +} + +.error__title { + font-size: 10em; +} + +.error__subtitle { + font-size: 2em; +} + +.error__description { + opacity: 0.5; +} + +.error__button { + min-width: 7em; + margin-top: 3em; + margin-right: 0.5em; + padding: 0.5em 2em; + outline: none; + border: 2px solid #2f3640; + background-color: transparent; + border-radius: 8em; + color: #576375; + cursor: pointer; + transition-duration: 0.2s; + font-size: 0.75em; + font-family: 'Righteous', cursive,ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";; +} + +.error__button:hover { + color: #21252c; +} + +.error__button--active { + background-color: #e67e22; + border: 2px solid #e67e22; + color: white; +} + +.error__button--active:hover { + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.5); + color: white; +} + +.astronaut { + position: absolute; + width: 185px; + height: 300px; + left: 70%; + top: 50%; + transform: translate(-50%, -50%) rotate(20deg) scale(1.2); +} + +.astronaut__head { + background-color: white; + position: absolute; + top: 60px; + left: 60px; + width: 60px; + height: 60px; + content: ''; + border-radius: 2em; +} + +.astronaut__head-visor-flare1 { + background-color: #7f8fa6; + position: absolute; + top: 28px; + left: 40px; + width: 10px; + height: 10px; + content: ''; + border-radius: 2em; + opacity: 0.5; +} + +.astronaut__head-visor-flare2 { + background-color: #718093; + position: absolute; + top: 40px; + left: 38px; + width: 5px; + height: 5px; + content: ''; + border-radius: 2em; + opacity: 0.3; +} + +.astronaut__backpack { + background-color: #bfbfbf; + position: absolute; + top: 90px; + left: 47px; + width: 86px; + height: 90px; + content: ''; + border-radius: 8px; +} + +.astronaut__body { + background-color: #e6e6e6; + position: absolute; + top: 115px; + left: 55px; + width: 70px; + height: 80px; + content: ''; + border-radius: 8px; +} + +.astronaut__body__chest { + background-color: #d9d9d9; + position: absolute; + top: 140px; + left: 68px; + width: 45px; + height: 25px; + content: ''; + border-radius: 6px; +} + +.astronaut__arm-left1 { + background-color: #e6e6e6; + position: absolute; + top: 127px; + left: 9px; + width: 65px; + height: 20px; + content: ''; + border-radius: 8px; + transform: rotate(-30deg); +} + +.astronaut__arm-left2 { + background-color: #e6e6e6; + position: absolute; + top: 102px; + left: 7px; + width: 20px; + height: 45px; + content: ''; + border-radius: 8px; + transform: rotate(-12deg); + border-top-left-radius: 8em; + border-top-right-radius: 8em; +} + +.astronaut__arm-right1 { + background-color: #e6e6e6; + position: absolute; + top: 113px; + left: 100px; + width: 65px; + height: 20px; + content: ''; + border-radius: 8px; + transform: rotate(-10deg); +} + +.astronaut__arm-right2 { + background-color: #e6e6e6; + position: absolute; + top: 78px; + left: 141px; + width: 20px; + height: 45px; + content: ''; + border-radius: 8px; + transform: rotate(-10deg); + border-top-left-radius: 8em; + border-top-right-radius: 8em; +} + +.astronaut__arm-thumb-left { + background-color: #e6e6e6; + position: absolute; + top: 110px; + left: 21px; + width: 10px; + height: 6px; + content: ''; + border-radius: 8em; + transform: rotate(-35deg); +} + +.astronaut__arm-thumb-right { + background-color: #e6e6e6; + position: absolute; + top: 90px; + left: 133px; + width: 10px; + height: 6px; + content: ''; + border-radius: 8em; + transform: rotate(20deg); +} + +.astronaut__wrist-left { + background-color: #e67e22; + position: absolute; + top: 122px; + left: 6.5px; + width: 21px; + height: 4px; + content: ''; + border-radius: 8em; + transform: rotate(-15deg); +} + +.astronaut__wrist-right { + background-color: #e67e22; + position: absolute; + top: 98px; + left: 141px; + width: 21px; + height: 4px; + content: ''; + border-radius: 8em; + transform: rotate(-10deg); +} + +.astronaut__leg-left { + background-color: #e6e6e6; + position: absolute; + top: 188px; + left: 50px; + width: 23px; + height: 75px; + content: ''; + transform: rotate(10deg); +} + +.astronaut__leg-right { + background-color: #e6e6e6; + position: absolute; + top: 188px; + left: 108px; + width: 23px; + height: 75px; + content: ''; + transform: rotate(-10deg); +} + +.astronaut__foot-left { + background-color: white; + position: absolute; + top: 240px; + left: 43px; + width: 28px; + height: 20px; + content: ''; + transform: rotate(10deg); + border-radius: 3px; + border-top-left-radius: 8em; + border-top-right-radius: 8em; + border-bottom: 4px solid #e67e22; +} + +.astronaut__foot-right { + background-color: white; + position: absolute; + top: 240px; + left: 111px; + width: 28px; + height: 20px; + content: ''; + transform: rotate(-10deg); + border-radius: 3px; + border-top-left-radius: 8em; + border-top-right-radius: 8em; + border-bottom: 4px solid #e67e22; +} \ No newline at end of file diff --git a/spacebook/_includes/assets/css/inline.css b/spacebook/_includes/assets/css/inline.css new file mode 100644 index 00000000..e69de29b diff --git a/spacebook/_includes/assets/images/github.svg b/spacebook/_includes/assets/images/github.svg new file mode 100644 index 00000000..38997125 --- /dev/null +++ b/spacebook/_includes/assets/images/github.svg @@ -0,0 +1 @@ +GitHub icon \ No newline at end of file diff --git a/spacebook/_includes/assets/js/404.js b/spacebook/_includes/assets/js/404.js new file mode 100644 index 00000000..9db7367d --- /dev/null +++ b/spacebook/_includes/assets/js/404.js @@ -0,0 +1,84 @@ +(function (window, document) { + "use strict"; + window.addEventListener('load', function () { + const cordCanvas = document.getElementById('cord'); + const ctx = cordCanvas.getContext('2d'); + + let y1 = 160; + let y2 = 100; + let y3 = 100; + + let y1Forward = true; + let y2Forward = false; + let y3Forward = true; + drawVisor() + //animate() + }); + })(window, document); + +function drawVisor() { + const canvas = document.getElementById('visor'); + const ctx = canvas.getContext('2d'); + + ctx.beginPath(); + ctx.moveTo(5, 45); + ctx.bezierCurveTo(15, 64, 45, 64, 55, 45); + + ctx.lineTo(55, 20); + ctx.bezierCurveTo(55, 15, 50, 10, 45, 10); + + ctx.lineTo(15, 10); + + ctx.bezierCurveTo(15, 10, 5, 10, 5, 20); + ctx.lineTo(5, 45); + + ctx.fillStyle = '#2f3640'; + ctx.strokeStyle = '#f5f6fa'; + ctx.fill(); + ctx.stroke(); + } + + + + function animate() { + requestAnimationFrame(animate); + ctx.clearRect(0, 0, innerWidth, innerHeight); + + ctx.beginPath(); + ctx.moveTo(130, 170); + ctx.bezierCurveTo(250, y1, 345, y2, 400, y3); + + ctx.strokeStyle = 'white'; + ctx.lineWidth = 8; + ctx.stroke(); + + + if (y1 === 100) { + y1Forward = true; + } + + if (y1 === 300) { + y1Forward = false; + } + + if (y2 === 100) { + y2Forward = true; + } + + if (y2 === 310) { + y2Forward = false; + } + + if (y3 === 100) { + y3Forward = true; + } + + if (y3 === 317) { + y3Forward = false; + } + + y1Forward ? y1 += 1 : y1 -= 1; + y2Forward ? y2 += 1 : y2 -= 1; + y3Forward ? y3 += 1 : y3 -= 1; + } + \ No newline at end of file diff --git a/spacebook/_includes/assets/js/darkmode.js b/spacebook/_includes/assets/js/darkmode.js new file mode 100644 index 00000000..5dcef6d9 --- /dev/null +++ b/spacebook/_includes/assets/js/darkmode.js @@ -0,0 +1,19 @@ +function applyThemeColor () { + if (localStorage.getItem('darkmode') === 'true' || (!(localStorage.getItem('darkmode')) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { + localStorage.setItem('darkmode', 'true') + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } +} + +function activateDarkMode() { + const newDarkModeValue = localStorage.getItem('darkmode') === 'true' + ? 'false' + : 'true' + + localStorage.setItem('darkmode', newDarkModeValue) + applyThemeColor() +} + +applyThemeColor() diff --git a/spacebook/_includes/assets/js/inline.js b/spacebook/_includes/assets/js/inline.js new file mode 100644 index 00000000..533e2586 --- /dev/null +++ b/spacebook/_includes/assets/js/inline.js @@ -0,0 +1,49 @@ +if (window.netlifyIdentity) { + window.netlifyIdentity.on("init", user => { + if (!user) { + window.netlifyIdentity.on("login", () => { + document.location.href = "/admin/"; + }); + } + }); +} + +document.addEventListener("DOMContentLoaded", function() { + + const el = document.getElementById("main"); + el.addEventListener("click", closeNavigation, false) + +}); + +function logout() { + localStorage.removeItem("passphrase") + window.location.href = "/"; +} + +function showNavigation() { + const navigation = document.getElementById("navigation"); + navigation.classList.remove("hidden", "sticky","pt-32"); + navigation.classList.add("absolute","right-0", "top-0", "-mt-0", "z-50", "pt-0", "bg-white","border-l", "border-gray-200"); +} + +function closeNavigation() { + const navigation = document.getElementById("navigation"); + navigation.classList.add("hidden"); + navigation.classList.remove("absolute","right-0","z-50", "bg-gray-100", "border-r", "border-gray-800" ); +} + +function toggleLayout(state) { + if (localStorage.getItem('layout') === "horizontal") { + localStorage.setItem('layout', 'vertical') + } else if (localStorage.getItem('layout') === "vertical") { + localStorage.setItem('layout', "horizontal") + } else if (!localStorage.getItem('layout')) { + if (state === "horizontal") { + localStorage.setItem('layout', 'vertical') + } else { + localStorage.setItem('layout', 'horizontal') + } + } + + console.log(localStorage.getItem('layout')) +} \ No newline at end of file diff --git a/spacebook/_includes/assets/js/search.js b/spacebook/_includes/assets/js/search.js new file mode 100644 index 00000000..b3faca2b --- /dev/null +++ b/spacebook/_includes/assets/js/search.js @@ -0,0 +1,62 @@ +(function (window, document) { + "use strict"; + const search = (e) => { + const results = window.searchIndex.search(e.target.value, { + bool: "OR", + expand: true, + }); + const searchBox = document.getElementById("searchField"); + const resEl = document.getElementById("searchResults"); + const noResultsEl = document.getElementById("noResultsFound"); + const navigation = document.getElementById("navigation"); + + searchBox.addEventListener('focus', (event) => { + event.target.classList.remove("hidden"); + resEl.classList.remove("hidden"); + }); + + document.addEventListener('click', function(event) { + var isClickInside = searchBox.contains(event.target); + if (!isClickInside) { + console.log('hidden') + resEl.classList.add("hidden"),noResultsEl.classList.add("hidden") + noResultsEl.classList.add("hidden") + } + }); + + resEl.innerHTML = ""; + if (e.target.value != "") { + if (results != "") { + noResultsEl.classList.add("hidden") + resEl.classList.add("p-4") + results.map((r) => { + const { id, title, description } = r.doc; + const el = document.createElement("li", { tabindex: '-1' }); + resEl.appendChild(el); + + const h3 = document.createElement("h3"); + el.appendChild(h3); + + const a = document.createElement("a"); + a.setAttribute("href", id); + a.textContent = title; + h3.appendChild(a); + + const p = document.createElement("p"); + p.textContent = description; + el.appendChild(p); + }); + } else { + noResultsEl.classList.remove("hidden") + } + } else { + noResultsEl.classList.add("hidden") + } + }; + fetch("/search-index.json").then((response) => + response.json().then((rawIndex) => { + window.searchIndex = elasticlunr.Index.load(rawIndex); + document.getElementById("searchField").addEventListener("input", search); + }) + ); + })(window, document); \ No newline at end of file diff --git a/spacebook/_includes/assets/utils/image.txt b/spacebook/_includes/assets/utils/image.txt new file mode 100644 index 00000000..1d8d3747 --- /dev/null +++ b/spacebook/_includes/assets/utils/image.txt @@ -0,0 +1 @@ +{% image "sagan.jpg" "Carl Sagan, 1987" %} \ No newline at end of file diff --git a/spacebook/_includes/assets/utils/imagesize.txt b/spacebook/_includes/assets/utils/imagesize.txt new file mode 100644 index 00000000..7e3c22b1 --- /dev/null +++ b/spacebook/_includes/assets/utils/imagesize.txt @@ -0,0 +1 @@ +{% image "sagan.jpg" "Carl Sagan, 1987" "200px" %} \ No newline at end of file diff --git a/spacebook/_includes/components/contact.njk b/spacebook/_includes/components/contact.njk new file mode 100644 index 00000000..778abbf3 --- /dev/null +++ b/spacebook/_includes/components/contact.njk @@ -0,0 +1,24 @@ +
+
+
+

Send a message

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
diff --git a/spacebook/_includes/components/footer.njk b/spacebook/_includes/components/footer.njk new file mode 100644 index 00000000..8b26cfec --- /dev/null +++ b/spacebook/_includes/components/footer.njk @@ -0,0 +1,35 @@ +
+
+ +
+ {% if site.enableContact == 1 %} + + + + {% endif %} +
+ +
+ + {% if site.footer %} + {{ site.footer | safe }} + {% endif %} + +
+ {% if site.enableNetlifyCMS == 1 %} + + {% endif %} + {% if site.enableGithubLink == 1 %} +
+ +
+ {% endif %} +
+
diff --git a/spacebook/_includes/components/head.njk b/spacebook/_includes/components/head.njk new file mode 100644 index 00000000..ba452020 --- /dev/null +++ b/spacebook/_includes/components/head.njk @@ -0,0 +1,33 @@ + + + + {{ renderData.title or title or site.name }} + + + + + {% set js %} + {% include "assets/js/inline.js" %} + {% include "assets/js/search.js" %} + {% endset %} + {% set darkmode %} + {% include "assets/js/darkmode.js" %} + {% endset %} + + + + + {% if site.enableNetlifyCMS == 1 %} + + + {% endif %} + + {% if site.enableSearch == 1 %} + + {% endif %} + + {% if site.navigationStyle == 'horizontal' %} + + {% endif %} + + diff --git a/spacebook/_includes/components/header.njk b/spacebook/_includes/components/header.njk new file mode 100644 index 00000000..1d078279 --- /dev/null +++ b/spacebook/_includes/components/header.njk @@ -0,0 +1,41 @@ +
+ + {% if site.navigationStyle == 'horizontal' %} +
+ {% include "components/nav.njk" %} +
+ {% endif %} +
+ + + \ No newline at end of file diff --git a/spacebook/_includes/components/nav.njk b/spacebook/_includes/components/nav.njk new file mode 100644 index 00000000..7b97aa7b --- /dev/null +++ b/spacebook/_includes/components/nav.njk @@ -0,0 +1,99 @@ +{% set navPages = collections.all | eleventyNavigation %} + +{% macro renderNavListItem(entry,rel) -%} + + {% if entry.url == page.url or entry.title == eleventyNavigation.parent or entry.title == eleventyComputed.key%} + {% if rel == "child" %} + {% set highlight = 'border-gray-600 font-semibold text-gray-500 focus:border-gray-700' %} + {% else %} + {% set highlight = 'border-gray-600 font-semibold focus:border-gray-700' %} + {% endif %} + {% set current = 'aria-current="page"' %} + {% else %} + {% set highlight = 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-500 ' %} + {% set current = '' %} + {% endif %} + +{%- if not entry.children.length -%} + {% if rel == "child" %} + {{ entry.title }} + {% else %} + {{ entry.title }} + {% endif %} + + {%- else -%} + + {%- if entry.children.length -%} + +
+ +
+
+ {%- for child in entry.children %}{{ renderNavListItem(child) }}{% endfor -%} +
+
+
+ {%- endif -%} + {%- endif -%} +{%- endmacro %} + +
+
+ + + + +
+
+ + diff --git a/spacebook/_includes/layouts/404.njk b/spacebook/_includes/layouts/404.njk new file mode 100644 index 00000000..ab775fda --- /dev/null +++ b/spacebook/_includes/layouts/404.njk @@ -0,0 +1,60 @@ + + + + + {{ title }} + {% set css %} + {% include "_includes/assets/css/404.css" %} + {% endset %} + + {% set js %} + {% include "_includes/assets/js/404.js" %} + {% endset %} + +
+
+
+
+ +
+
+
+
+
+ +
+
404
+
Ground Control to Major Tom
+
Your circuit's dead, there's something wrong
+ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+ +
+
+
+
+ + diff --git a/spacebook/_includes/layouts/base.njk b/spacebook/_includes/layouts/base.njk new file mode 100644 index 00000000..ae7467bc --- /dev/null +++ b/spacebook/_includes/layouts/base.njk @@ -0,0 +1,29 @@ + + + {% include "components/head.njk" %} + +
+ {% include "components/header.njk" %} + {% if site.navigationStyle == "vertical" %} + + {% endif %} + {% if site.navigationStyle == 'horizontal' %} +
+ {% else %} +
+ {% endif %} + {{ layoutContent | safe }} + {% include "components/footer.njk" %} +
+
+ + diff --git a/spacebook/_includes/layouts/contact.njk b/spacebook/_includes/layouts/contact.njk new file mode 100644 index 00000000..a544fb3f --- /dev/null +++ b/spacebook/_includes/layouts/contact.njk @@ -0,0 +1,7 @@ +--- +layout: layouts/base.njk +section: contact +--- +
+{% include "components/contact.njk" %} +
\ No newline at end of file diff --git a/spacebook/_includes/layouts/page.njk b/spacebook/_includes/layouts/page.njk new file mode 100644 index 00000000..24e1cc89 --- /dev/null +++ b/spacebook/_includes/layouts/page.njk @@ -0,0 +1,74 @@ +--- +layout: layouts/base.njk +section: page +--- + +{% if site.navigationStyle == "vertical" %} + {% if site.enableEditButton == true or site.enableDatestamp == true %} +
+ {% if site.enableDatestamp == true %} +
Updated
+ {% endif %} + {% if site.enableEditButton == true %} + + {% endif %} +
+ {% endif %} +{% endif %} + +
+
+
+ {% if site.enableTOC and toc %} +
+ {% else %} +
+ {% endif %} +
+ + {% if site.navigationStyle == "horizontal" %} + {% if site.enableEditButton == true or site.enableDatestamp == true %} +
+ {% if site.enableDatestamp == true %} +
Updated
+ {% endif %} + {% if site.enableEditButton == true %} + + {% endif %} +
+ {% endif %} + {% endif %} +
+ {{ layoutContent | safe }} + {% if (site.enableComments) and (comments !== 0) %} + + {% endif %} + + {% if site.enablePageNavigation == true %} + + {% endif %} +
+
+
+ + {% if site.enableTOC and toc %} + + {% endif %} +
+
+
+ + diff --git a/spacebook/_includes/layouts/robots.njk b/spacebook/_includes/layouts/robots.njk new file mode 100644 index 00000000..1f53798b --- /dev/null +++ b/spacebook/_includes/layouts/robots.njk @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/spacebook/_tmp/style.css b/spacebook/_tmp/style.css new file mode 100644 index 00000000..7911f43a --- /dev/null +++ b/spacebook/_tmp/style.css @@ -0,0 +1,385 @@ +/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com */ + +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ + +/* +Document +======== +*/ + +/** +Use a better box model (opinionated). +*/ + +*, +::before, +::after { + box-sizing: border-box; +} + +/** +Use a more readable tab size (opinionated). +*/ + +/** +1. Correct the line height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +*/ + +/* +Sections +======== +*/ + +/** +Remove the margin in all browsers. +*/ + +/** +Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +*/ + +/* +Grouping content +================ +*/ + +/** +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +*/ + +/* +Text-level semantics +==================== +*/ + +/** +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +/** +Add the correct font weight in Edge and Safari. +*/ + +/** +1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +2. Correct the odd 'em' font sizing in all browsers. +*/ + +/** +Add the correct font size in all browsers. +*/ + +/** +Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. +*/ + +/* +Tabular data +============ +*/ + +/** +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +*/ + +/* +Forms +===== +*/ + +/** +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +*/ + +/** +Remove the inheritance of text transform in Edge and Firefox. +1. Remove the inheritance of text transform in Firefox. +*/ + +/** +Correct the inability to style clickable types in iOS and Safari. +*/ + +/** +Remove the inner border and padding in Firefox. +*/ + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** +Restore the focus styles unset by the previous rule. +*/ + +/** +Remove the additional ':invalid' styles in Firefox. +See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 +*/ + +/** +Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. +*/ + +/** +Add the correct vertical alignment in Chrome and Firefox. +*/ + +/** +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +/** +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to 'inherit' in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* +Interactive +=========== +*/ + +/* +Add the correct display in Chrome and Safari. +*/ + +/** + * Manually forked from SUIT CSS Base: https://github.com/suitcss/base + * A thin layer on top of normalize.css that provides a starting point more + * suitable for web applications. + */ + +/** + * Removes the default spacing and border for appropriate elements. + */ + +/** + * Tailwind custom reset styles + */ + +/** + * 1. Use the user's configured `sans` font-family (with Tailwind's default + * sans-serif font stack as a fallback) as a sane default. + * 2. Use Tailwind's default "normal" line-height so the user isn't forced + * to override it to ensure consistency even when using the default theme. + */ + +/** + * Inherit font-family and line-height from `html` so users can set them as + * a class directly on the `html` element. + */ + +/** + * 1. Prevent padding and border from affecting element width. + * + * We used to set this in the html element and inherit from + * the parent element for everything else. This caused issues + * in shadow-dom-enhanced elements like
where the content + * is wrapped by a div with box-sizing set to `content-box`. + * + * https://github.com/mozdevs/cssremedy/issues/4 + * + * + * 2. Allow adding a border to an element by just adding a border-width. + * + * By default, the way the browser specifies that an element should have no + * border is by setting it's border-style to `none` in the user-agent + * stylesheet. + * + * In order to easily add borders to elements by just setting the `border-width` + * property, we change the default border-style for all elements to `solid`, and + * use border-width to hide them instead. This way our `border` utilities only + * need to set the `border-width` property instead of the entire `border` + * shorthand, making our border utilities much more straightforward to compose. + * + * https://github.com/tailwindcss/tailwindcss/pull/116 + */ + +*, +::before, +::after { + box-sizing: border-box; /* 1 */ + border-width: 0; /* 2 */ + border-style: solid; /* 2 */ + border-color: currentColor; /* 2 */ +} + +/* + * Ensure horizontal rules are visible by default + */ + +/** + * Undo the `border-style: none` reset that Normalize applies to images so that + * our `border-{width}` utilities have the expected effect. + * + * The Normalize reset is unnecessary for us since we default the border-width + * to 0 on all elements. + * + * https://github.com/tailwindcss/tailwindcss/issues/362 + */ + +/** + * Override legacy focus reset from Normalize with modern Firefox focus styles. + * + * This is actually an improvement over the new defaults in Firefox in our testing, + * as it triggers the better focus styles even for links, which still use a dotted + * outline in Firefox by default. + */ + +/** + * Reset links to optimize for opt-in styling instead of + * opt-out. + */ + +/** + * Reset form element properties that are easy to forget to + * style explicitly so you don't inadvertently introduce + * styles that deviate from your design system. These styles + * supplement a partial reset that is already applied by + * normalize.css. + */ + +/** + * Use the configured 'mono' font family for elements that + * are expected to be rendered with a monospace font, falling + * back to the system monospace stack if there is no configured + * 'mono' font family. + */ + +/** + * 1. Make replaced elements `display: block` by default as that's + * the behavior you want almost all of the time. Inspired by + * CSS Remedy, with `svg` added as well. + * + * https://github.com/mozdevs/cssremedy/issues/14 + * + * 2. Add `vertical-align: middle` to align replaced elements more + * sensibly by default when overriding `display` by adding a + * utility like `inline`. + * + * This can trigger a poorly considered linting error in some + * tools but is included by design. + * + * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210 + */ + +/** + * Constrain images and videos to the parent width and preserve + * their intrinsic aspect ratio. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +/** + * Ensure the default browser behavior of the `hidden` attribute. + */ + +*, ::before, ::after { + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; +} + +/* Set up some default image behavior for nicer images */ + +/* Overrides for Tailwind Typography prose class */ + +/* Define blockquotes and some standard callout blocks */ + +/* Overrides for nav/Table of Contents block */ + +/* Utilities and misc */ + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +@keyframes ping { + 75%, 100% { + transform: scale(2); + opacity: 0; + } +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +@keyframes bounce { + 0%, 100% { + transform: translateY(-25%); + animation-timing-function: cubic-bezier(0.8,0,1,1); + } + + 50% { + transform: none; + animation-timing-function: cubic-bezier(0,0,0.2,1); + } +} + +*, ::before, ::after { + --tw-shadow: 0 0 #0000; +} + +*, ::before, ::after { + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; +} + +@media (min-width: 640px) { +} + +@media (min-width: 768px) { +} + +@media (min-width: 1024px) { +} + +@media (min-width: 1280px) { +} + +@media (min-width: 1536px) { +} \ No newline at end of file diff --git a/spacebook/content/images/huetiful-logo.png b/spacebook/content/images/huetiful-logo.png new file mode 100644 index 00000000..8348f68d Binary files /dev/null and b/spacebook/content/images/huetiful-logo.png differ diff --git a/spacebook/content/pages/colors.md b/spacebook/content/pages/colors.md new file mode 100644 index 00000000..a08b819e --- /dev/null +++ b/spacebook/content/pages/colors.md @@ -0,0 +1,278 @@ +--- +title: Colors +eleventyNavigation: + key: Colors + order: 1 + title: Colors +--- + +# Module:📦 colors + +## Table of contents📜 + +### Classes🗃 + +- [Color](../classes/colors.Color.md) +- [ColorArray](../classes/colors.ColorArray.md) + +### Functions🧰 + +- [color](colors.md#color) +- [colors](colors.md#colors) +- [diverging](colors.md#diverging) +- [load](colors.md#load) +- [qualitative](colors.md#qualitative) +- [sequential](colors.md#sequential) +- [tailwindColors](colors.md#tailwindColors) + +## Functions + +### color + +▸ **color**(`color`): [`Color`](../classes/colors.Color.md) + +Wrapper function over the Color class that returns a new Color method chain. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color token to bind. | + +#### Returns🔙 + +[`Color`](../classes/colors.Color.md) + +A new Color class with all the utilities that take a single color as the first parameter bound to its prototype. + +___ + +### colors + +▸ **colors**(`shade`, `value?`): [`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[] + +A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `shade` | [`TailwindColorFamilies`](types.md#TailwindColorFamilies) \| ``"all"`` | Any shade in the default TailwindCSS palette e.g amber,blue. | +| `value?` | [`ScaleValues`](types.md#ScaleValues) | Any value from 100 to 900 in increments of 100 e.g "200". | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[] + +color Returns a hex code string or array of hex codes depending on how the function is called. + +**`Example`** 📋 + +```ts +import { colors } from "huetiful-js"; + +let all300 = colors("all", 300); + +console.log(all300) +//[ + '#cbd5e1', '#d1d5db', '#d4d4d8', + '#d4d4d4', '#d6d3d1', '#fca5a5', + '#fdba74', '#fcd34d', '#fde047', + '#bef264', '#86efac', '#6ee7b7', + '#5eead4', '#7dd3fc', '#93c5fd', + '#c4b5fd', '#d8b4fe', '#f0abfc', + '#f9a8d4', '#fda4af' +] + +let red = colors("red"); +console.log(red); + +// [ + '#fef2f2', '#fee2e2', + '#fecaca', '#fca5a5', + '#f87171', '#ef4444', + '#dc2626', '#b91c1c', + '#991b1b', '#7f1d1d' +] + +let red100 = colors("red", 100); + +console.log(red100) +// #fee2e2 +``` + +___ + +### diverging + +▸ **diverging**(`scheme`): [`ColorToken`](types.md#ColorToken)[] + +A wrapper function for ColorBrewer's map of diverging color schemes. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `scheme` | [`DivergingScheme`](types.md#DivergingScheme) | The name of the scheme. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of colors in hex represantation. + +**`Example`** 📋 + +```ts +import { diverging } from 'huetiful-js' + +console.log(diverging("Spectral")) +//[ + '#7fc97f', '#beaed4', + '#fdc086', '#ffff99', + '#386cb0', '#f0027f', + '#bf5b17', '#666666' +] +``` + +___ + +### load + +▸ **load**(`colors`): [`ColorArray`](../classes/colors.ColorArray.md) + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `colors` | [`ColorToken`](types.md#ColorToken)[] | An array of colors to chain the array methods on. Every element in the array will be parsed as a color token. | + +#### Returns🔙 + +[`ColorArray`](../classes/colors.ColorArray.md) + +___ + +### qualitative + +▸ **qualitative**(`scheme`): [`ColorToken`](types.md#ColorToken)[] + +A wrapper function for ColorBrewer's map of qualitative color schemes. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `scheme` | [`QualitativeScheme`](types.md#QualitativeScheme) | The name of the scheme | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of colors in hex represantation. + +**`Example`** 📋 + +```ts +import { qualitative } from 'huetiful-js' + +console.log(qualitative("Accent")) +// [ + '#7fc97f', '#beaed4', + '#fdc086', '#ffff99', + '#386cb0', '#f0027f', + '#bf5b17', '#666666' +] +``` + +___ + +### sequential + +▸ **sequential**(`scheme`): [`ColorToken`](types.md#ColorToken)[] + +A wrapper function for ColorBrewer's map of sequential color schemes. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `scheme` | [`SequentialScheme`](types.md#SequentialScheme) | The name of the scheme | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of colors in hex represantation. + +**`Example`** 📋 + +```ts +import { sequential } from 'huetiful-js + +console.log(sequential("OrRd")) + +// [ + '#fff7ec', '#fee8c8', + '#fdd49e', '#fdbb84', + '#fc8d59', '#ef6548', + '#d7301f', '#b30000', + '#7f0000' +] +``` + +___ + +### tailwindColors + +▸ **tailwindColors**(`shade`): (`val?`: [`ScaleValues`](types.md#ScaleValues)) => `string` \| `string`[] + +Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale, + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `shade` | `number` \| typeof `iterator` \| ``"length"`` \| ``"toString"`` \| ``"concat"`` \| ``"slice"`` \| ``"indexOf"`` \| ``"lastIndexOf"`` \| ``"charAt"`` \| ``"charCodeAt"`` \| ``"localeCompare"`` \| ``"match"`` \| ``"replace"`` \| ``"search"`` \| ``"split"`` \| ``"substring"`` \| ``"toLowerCase"`` \| ``"toLocaleLowerCase"`` \| ``"toUpperCase"`` \| ``"toLocaleUpperCase"`` \| ``"trim"`` \| ``"substr"`` \| ``"codePointAt"`` \| ``"includes"`` \| ``"endsWith"`` \| ``"normalize"`` \| ``"repeat"`` \| ``"startsWith"`` \| ``"anchor"`` \| ``"big"`` \| ``"blink"`` \| ``"bold"`` \| ``"fixed"`` \| ``"fontcolor"`` \| ``"fontsize"`` \| ``"italics"`` \| ``"link"`` \| ``"small"`` \| ``"strike"`` \| ``"sub"`` \| ``"sup"`` \| ``"valueOf"`` | + +#### Returns🔙 + +`fn` + +color A hex string value or array of hex strings. + +▸ (`val?`): `string` \| `string`[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `val?` | [`ScaleValues`](types.md#ScaleValues) | + +##### Returns🔙 + +`string` \| `string`[] + +**`Example`** 📋 + +```ts +import { tailwindColors } from "huetiful-js"; + +// We pass in red as the target hue. +// It returns a function that can be called with an optional value parameter +let red = tailwindColors("red"); +console.log(red()); +// [ + '#fef2f2', '#fee2e2', + '#fecaca', '#fca5a5', + '#f87171', '#ef4444', + '#dc2626', '#b91c1c', + '#991b1b', '#7f1d1d' +] + +console.log(red(100)); +// '#fee2e2' + +console.log(red('900')); +// '#7f1d1d' +``` diff --git a/spacebook/content/pages/converters.md b/spacebook/content/pages/converters.md new file mode 100644 index 00000000..f3be38cd --- /dev/null +++ b/spacebook/content/pages/converters.md @@ -0,0 +1,196 @@ +--- +title: Converter functions +date: Last Modified +eleventyNavigation: + order: 3 + title: Converter functions +--- + +# Module:📦 converters + +## Table of contents📜 + +### Functions🧰 + +- [num2rgb](converters.md#num2rgb) +- [rgb2num](converters.md#rgb2num) +- [temp2Color](converters.md#temp2Color) +- [toColorTuple](converters.md#toColorTuple) +- [toHex](converters.md#toHex) +- [ucsConverter](converters.md#ucsConverter) + +## Functions + +### num2rgb + +▸ **num2rgb**(`num`, `hex?`): [`ColorToken`](types.md#ColorToken) + +Returns the RGB color equivalent of any number between 0 and 16,777,215. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `num` | `number` | `undefined` | The number to convert to RGB | +| `hex` | `boolean` | `false` | - | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +color An RGB color object or hex string. + +**`Example`** 📋 + +```ts +import { num2rgb } from 'huetiful-js' + +console.log(num2rgb(900, true)) +// #000384 +``` + +___ + +### rgb2num + +▸ **rgb2num**(`color`): `number` + +Returns the numerical equivalent of a color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to convert to its numerical equivalent. | + +#### Returns🔙 + +`number` + +value The numerical value of the color from 0 to 16,777,215. + +**`Example`** 📋 + +```ts +import { rgb2num } from 'huetiful-js' + +console.log(rgb2num("b2c3f1")) +// 11715569 +``` + +___ + +### temp2Color + +▸ **temp2Color**(`kelvin`, `hex?`): [`ColorToken`](types.md#ColorToken) + +Converts the temperature value (in Kelvins) to an RGB color. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `kelvin` | `number` | `undefined` | The number of Kelvins. From 0 to 30,000 . | +| `hex` | `boolean` | `false` | Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +color The color as a hexadecimal or RGB color object. + +**`Example`** 📋 + +```ts +import { temp2Color } from 'huetiful-js' + +console.log(temp2Color(2542)) +// #ffa44a +``` + +___ + +### toColorTuple + +▸ **toColorTuple**(`color`, `mode`): `any`[] + +Returns an array of channel values in the mode color space. It does not mutate the values of the passed in color token. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | `string` \| `object` | Expects the color to be in hexadecimal represantation or as a plain color object. | +| `mode` | [`Colorspaces`](types.md#Colorspaces) | The mode color space to return channel values for | + +#### Returns🔙 + +`any`[] + +An array of channel values with the colorspace as first element and the alpha channel if its explicitly defined in the passed in color. + +**`Example`** 📋 + +```ts +let rgbColor = { + r: 0.4, + g: 0.3, + b: 0.7, + mode: "rgb", +}; +console.log(toColorTuple(rgbColor,'rgb')); + +// [ 'rgb', 0.4, 0.3, 0.7 ] +``` + +___ + +### toHex + +▸ **toHex**(`color`): `string` + +Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to convert to hexadecimal. Works on color objects and CSS named colors. | + +#### Returns🔙 + +`string` + +A hexadecimal representation of the passed in color. + +**`Example`** 📋 + +```ts +import { toHex } from "huetiful-js"; + +console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" })) +// #7b794180 + +console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" })) +// #7b7941 +``` + +___ + +### ucsConverter + +▸ **ucsConverter**(`colorspace`): `ConvertFn`\<`any`\> + +Converter function with mode definitions for uniform color spaces. The function is curried to return a converter in the passed colospace. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `colorspace` | [`UniformColorSpaces`](types.md#UniformColorSpaces) | The mode converter to return. | + +#### Returns🔙 + +`ConvertFn`\<`any`\> + +The converter function in the mode colorspace. diff --git a/spacebook/content/pages/filterBy.md b/spacebook/content/pages/filterBy.md new file mode 100644 index 00000000..a3f745ab --- /dev/null +++ b/spacebook/content/pages/filterBy.md @@ -0,0 +1,290 @@ +--- +title: Filtering functions +eleventyNavigation: + order: 2 + title: Filtering functions +--- + +# Module:📦 filterBy + +## Table of contents📜 + +### Functions🧰 + +- [filterByContrast](filterBy.md#filterByContrast) +- [filterByDistance](filterBy.md#filterByDistance) +- [filterByHue](filterBy.md#filterByHue) +- [filterByLightness](filterBy.md#filterByLightness) +- [filterByLuminance](filterBy.md#filterByLuminance) +- [filterBySaturation](filterBy.md#filterBySaturation) + +## Functions + +### filterByContrast + +▸ **filterByContrast**(`collection`, `against`, `startContrast?`, `endContrast?`): [`ColorToken`](types.md#ColorToken)[] + +Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `against` | [`ColorToken`](types.md#ColorToken) | `undefined` | - | +| `startContrast` | `number` | `1` | The minimum end of the contrast range. | +| `endContrast` | `number` | `21` | The maximum end of the contrast range. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +Array of filtered colors. + +**`Example`** 📋 + +```ts +import { filterByContrast } from 'huetiful-js' + +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(filterByContrast(sample, 'green', '>=3')) +// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] +``` + +___ + +### filterByDistance + +▸ **filterByDistance**(`collection`, `against`, `startDistance?`, `endDistance?`): [`ColorToken`](types.md#ColorToken)[] + +Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. Uses the differenceHyab metric for calculating the distances. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `against` | [`ColorToken`](types.md#ColorToken) | `undefined` | - | +| `startDistance` | `number` | `0.05` | The minimum end of the distance range. | +| `endDistance?` | `number` | `undefined` | The maximum end of the distance range. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +Array of filtered colors. + +**`Example`** 📋 + +```ts +import { filterByDistance } from 'huetiful-js' + +let sample = [ + "#ffff00", + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#720000", + "#600000", +] + +console.log(filterByDistance(sample, "yellow", 0.1)) +// [ '#ffff00' ] +``` + +___ + +### filterByHue + +▸ **filterByHue**(`collection`, `startHue?`, `endHue?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[] + +Returns colors in the specified hue ranges between 0 to 360. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `startHue` | `number` | `0` | The minimum end of the hue range. | +| `endHue` | `number` | `360` | The maximum end of the hue range. | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | - | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +Array of the filtered colors. + +**`Example`** 📋 + +```ts +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByHue(sample, 20, 80) + +// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] +``` + +___ + +### filterByLightness + +▸ **filterByLightness**(`collection`, `startLightness?`, `endLightness?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[] + +Returns an array of colors in the specified lightness range. The range is between 0 and 100. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `startLightness` | `number` | `5` | The minimum end of the lightness range. | +| `endLightness` | `number` | `100` | The maximum end of the lightness range. | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The mode colorspace to retrieve the lightness value from. The default is lch65 | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +Array of filtered colors. + +**`Example`** 📋 + +```ts +import { filterByLightness } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByLightness(sample, 20, 80) + +// [ '#00c000', '#007e00', '#164100', '#720000' ] +``` + +___ + +### filterByLuminance + +▸ **filterByLuminance**(`collection`, `startLuminance?`, `endLuminance?`): [`ColorToken`](types.md#ColorToken)[] + +Returns an array of colors in the specified luminance range. The range is normalised to [0,1]. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `startLuminance` | `number` | `0.05` | The minimum end of the luminance range. | +| `endLuminance` | `number` | `1` | The maximum end of the luminance range. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +Array of filtered colors. + +**`Example`** 📋 + +```ts +import { filterByLuminance } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByLuminance(sample, 0.4, 0.9) + +// [ '#00ffdc', '#00ff78' ] +``` + +___ + +### filterBySaturation + +▸ **filterBySaturation**(`collection`, `startSaturation?`, `endSaturation?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[] + +Returns an array of colors in the specified saturation range. The range is normalised to [0,1]. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `startSaturation` | `number` | `0.05` | The minimum end of the saturation range. | +| `endSaturation` | `number` | `1` | The maximum end of the saturation range. | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +Array of filtered colors. + +**`Example`** 📋 + +```ts +import { filterByContrast } from 'huetiful-js' + +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(filterByContrast(sample, 'green', '>=3')) +// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] +``` diff --git a/spacebook/content/pages/generators.md b/spacebook/content/pages/generators.md new file mode 100644 index 00000000..41fee021 --- /dev/null +++ b/spacebook/content/pages/generators.md @@ -0,0 +1,292 @@ +--- +title: Generator functions +eleventyNavigation: + order: 5 + title: Generator functions +--- + +# Module:📦 generators + +## Table of contents📜 + +### References + +- [ucsConverter](generators.md#ucsConverter) + +### Functions🧰 + +- [discoverPalettes](generators.md#discoverPalettes) +- [earthtone](generators.md#earthtone) +- [hueShift](generators.md#hueShift) +- [interpolateSpline](generators.md#interpolateSpline) +- [interpolator](generators.md#interpolator) +- [pairedScheme](generators.md#pairedScheme) +- [pastel](generators.md#pastel) +- [scheme](generators.md#scheme) + +## References + +### ucsConverter + +Re-exports [ucsConverter](converters.md#ucsConverter) + +## Functions + +### discoverPalettes + +▸ **discoverPalettes**(`colors`, `schemeType?`): [`ColorToken`](types.md#ColorToken)[] \| `object` + +Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `colors` | [`ColorToken`](types.md#ColorToken)[] | The array of colors to create palettes from. Preferably use 5 or more colors for better results. | +| `schemeType?` | ``"analogous"`` \| ``"triadic"`` \| ``"tetradic"`` \| ``"complementary"`` | (Optional) The palette type you want to return. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] \| `object` + +An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results. + +**`Example`** 📋 + +```ts +import { discoverPalettes } from 'huetiful-js' + +let sample = [ + "#ffff00", + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#720000", + "#600000", + "#4e0000", + "#3e0000", + "#310000", +] + +console.log(discoverPalettes(sample, "tetradic")) +// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ] +``` + +___ + +### earthtone + +▸ **earthtone**(`color`, `colorspace?`, `options?`): [`ColorToken`](types.md#ColorToken)[] + +Creates a scale of a spline based interpolation between an earthtone and a color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to interpolate an earth tone with. * | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | - | +| `options?` | [`EarthtoneOptions`](types.md#EarthtoneOptions) | Optional overrides for customising interpolation and easing functions. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +The array of colors resulting from the earthtone interpolation as hex codes. + +**`Example`** 📋 + +```ts +import { earthtone } from 'huetiful-js' + +console.log(earthtone("pink",{earthtones:'clay',iterations:5 })) +// [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ] +``` + +___ + +### hueShift + +▸ **hueShift**(`color`, `colorspace?`, `options?`): [`ColorToken`](types.md#ColorToken)[] + +Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to use as the base of the hueshift. Colors are internally converted to LCH. | +| `colorspace?` | [`UniformColorSpaces`](types.md#UniformColorSpaces) | - | +| `options?` | [`HueShiftOptions`](types.md#HueShiftOptions) | The optional overrides object to customize per channel options like interpolation methods and channel fixups. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of colors in hex. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1 + +**`Example`** 📋 + +```ts +import { hueShift } from "huetiful-js"; + +let hueShiftedPalette = hueShift("#3e0000"); + +console.log(hueShiftedPalette); + +// [ + '#ffffe1', '#ffdca5', + '#ca9a70', '#935c40', + '#5c2418', '#3e0000', + '#310000', '#34000f', + '#38001e', '#3b002c', + '#3b0c3a' +] +``` + +___ + +### interpolateSpline + +▸ **interpolateSpline**(`colors`, `colorspace?`, `samples?`, `kind?`, `closed?`, `options?`): [`ColorToken`](types.md#ColorToken)[] + +Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `colors` | [`ColorToken`](types.md#ColorToken)[] | `undefined` | The array of colors to interpolate. If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result. | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch. | +| `samples?` | `number` | `undefined` | - | +| `kind?` | ``"natural"`` \| ``"monotone"`` \| ``"basis"`` | `undefined` | The type of the spline interpolation method. Default is basis. | +| `closed` | `boolean` | `false` | Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false | +| `options?` | [`InterpolatorOptions`](types.md#InterpolatorOptions) | `undefined` | Optional channel specific overrides. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +A hexadecimal representation of the resultant color. + +___ + +### interpolator + +▸ **interpolator**(`colors`, `colorspace?`, `options?`): `Interpolator`\<[`HueColorSpaces`](types.md#HueColorSpaces)\> + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `colors` | [`ColorToken`](types.md#ColorToken)[] | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | +| `options?` | `object` | + +#### Returns🔙 + +`Interpolator`\<[`HueColorSpaces`](types.md#HueColorSpaces)\> + +___ + +### pairedScheme + +▸ **pairedScheme**(`color`, `options?`): [`ColorToken`](types.md#ColorToken)[] \| [`ColorToken`](types.md#ColorToken) + +pairedScheme + Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to return a paired color scheme from. | +| `options?` | [`PairedSchemeOptions`](types.md#PairedSchemeOptions) | The optional overrides object to customize per channel options like interpolation methods and channel fixups. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] \| [`ColorToken`](types.md#ColorToken) + +An array containing the paired scheme. + +**`Example`** 📋 + +```ts +import { pairedScheme } from 'huetiful-js' + +console.log(pairedScheme("green",{hueStep:6,iterations:4,tone:'dark'})) +// [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ] +``` + +___ + +### pastel + +▸ **pastel**(`color`): [`ColorToken`](types.md#ColorToken) + +Returns a random pastel variant of the passed in color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to return a pastel variant of. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +A random pastel color. + +**`Example`** 📋 + +```ts +import { pastel } from 'huetiful-js' + +console.log(pastel("green")) +// #036103ff +``` + +___ + +### scheme + +▸ **scheme**(`schemeType`): (`color`: [`ColorToken`](types.md#ColorToken), `easingFunc?`: (`t`: `number`) => `number`) => [`ColorToken`](types.md#ColorToken)[] + +Generates a randomised classic color scheme from a single base color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `schemeType` | `string` | Any classic color scheme either "analogous"\|"triadic"\|"tetradic"\|"complementary"\|"splitComplementary". | + +#### Returns🔙 + +`fn` + +An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme. + +▸ (`color`, `easingFunc?`): [`ColorToken`](types.md#ColorToken)[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | +| `easingFunc?` | (`t`: `number`) => `number` | + +##### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +**`Example`** 📋 + +```ts +import { base } from 'huetiful-js' + +console.log(base("triadic")("#a1bd2f", true)) +// [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ] +``` diff --git a/spacebook/content/pages/helpers.md b/spacebook/content/pages/helpers.md new file mode 100644 index 00000000..833c591f --- /dev/null +++ b/spacebook/content/pages/helpers.md @@ -0,0 +1,776 @@ +--- +title: Helper functions +eleventyNavigation: + order: 8 + title: Helper functions +--- + +# Module:📦 helpers + +## Table of contents📜 + +### Variables + +- [interpolatorConfig](helpers.md#interpolatorConfig) + +### Functions🧰 + +- [adjustHue](helpers.md#adjustHue) +- [channelDifference](helpers.md#channelDifference) +- [checkArg](helpers.md#checkArg) +- [colorObj](helpers.md#colorObj) +- [colorObjArr](helpers.md#colorObjArr) +- [customConcat](helpers.md#customConcat) +- [customFindKey](helpers.md#customFindKey) +- [customSort](helpers.md#customSort) +- [eq](helpers.md#eq) +- [expressionParser](helpers.md#expressionParser) +- [filteredArr](helpers.md#filteredArr) +- [floorCeil](helpers.md#floorCeil) +- [getModeChannel](helpers.md#getModeChannel) +- [gt](helpers.md#gt) +- [gte](helpers.md#gte) +- [inRange](helpers.md#inRange) +- [isInteger](helpers.md#isInteger) +- [lt](helpers.md#lt) +- [lte](helpers.md#lte) +- [matchChromaChannel](helpers.md#matchChromaChannel) +- [matchComparator](helpers.md#matchComparator) +- [matchDigits](helpers.md#matchDigits) +- [matchLightnessChannel](helpers.md#matchLightnessChannel) +- [max](helpers.md#max) +- [min](helpers.md#min) +- [normalize](helpers.md#normalize) +- [random](helpers.md#random) +- [sortedArr](helpers.md#sortedArr) + +## Variables + +### interpolatorConfig + +• `Const` **interpolatorConfig**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `chromaInterpolator` | [`Interpolator`](types.md#Interpolator) | +| `easingFunc` | (`t`: `number`) => `number` | +| `hueFixup` | (`arr`: `number`[]) => `number`[] | +| `hueInterpolator` | [`Interpolator`](types.md#Interpolator) | +| `lightnessInterpolator` | [`Interpolator`](types.md#Interpolator) | + +## Functions + +### adjustHue + +▸ **adjustHue**(`value`): `number` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `value` | `number` | The hue angle to normalize. | + +#### Returns🔙 + +`number` + +The normalized hue angle or passed in value if it was within [0,360] + +**`Example`** 📋 + +```ts +console.log(adjustHue(4)); +// 4 + +console.log(adjustHue(444)); +// 84 +``` + +___ + +### channelDifference + +▸ **channelDifference**(`color`, `modeChannel`): (`subtrahend`: [`ColorToken`](types.md#ColorToken)) => `number` + +Returns the channel value difference between the passed in colors. They are both converted to the colorspace in the modeChannel parameter before values are computed. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to subtract values from/ | +| `modeChannel` | `string` | The colorspace and channel string to perform the operation in. | + +#### Returns🔙 + +`fn` + +The difference between the color channel(s) + +▸ (`subtrahend`): `number` + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `subtrahend` | [`ColorToken`](types.md#ColorToken) | + +##### Returns🔙 + +`number` + +**`Example`** 📋 + +```ts + +``` + +___ + +### checkArg + +▸ **checkArg**(`arg`, `def`): `unknown` + +Returns the first truthy value. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `arg` | `unknown` | The value to check | +| `def` | `unknown` | The value to cast if arg is falsy | + +#### Returns🔙 + +`unknown` + +The first truthy value + +___ + +### colorObj + +▸ **colorObj**(`factor`, `callback`): (`color`: [`ColorToken`](types.md#ColorToken)) => \{ `color`: [`ColorToken`](types.md#ColorToken) = color } + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `factor` | [`Factor`](types.md#Factor) | +| `callback` | `unknown` | + +#### Returns🔙 + +`fn` + +▸ (`color`): `Object` + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | + +##### Returns🔙 + +`Object` + +| Name | Type | +| :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | + +___ + +### colorObjArr + +▸ **colorObjArr**(`factor`, `callback`): (`collection`: `object` \| [`ColorToken`](types.md#ColorToken)[]) => \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: [`Factor`](types.md#Factor) }[] + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `factor` | [`Factor`](types.md#Factor) | +| `callback` | `any` | + +#### Returns🔙 + +`fn` + +▸ (`collection`): \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: [`Factor`](types.md#Factor) }[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | + +##### Returns🔙 + +\{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: [`Factor`](types.md#Factor) }[] + +___ + +### customConcat + +▸ **customConcat**(`hue`): `number`[] + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `hue` | `object` | + +#### Returns🔙 + +`number`[] + +___ + +### customFindKey + +▸ **customFindKey**(`collection`, `factor`): `string` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` | The collection to inspect. | +| `factor` | `number` | The value to compare against | + +#### Returns🔙 + +`string` + +Returns the found element or its key, else `undefined`. + +___ + +### customSort + +▸ **customSort**(`order`, `factor?`): (`a`: `any`, `b`: `any`) => `number` + +Helper function for native sorting method for arrays. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `order` | [`Order`](types.md#Order) | Either ascending or descending. | +| `factor?` | `string` | The property to query. | + +#### Returns🔙 + +`fn` + +A sorted array. + +▸ (`a`, `b`): `number` + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `a` | `any` | +| `b` | `any` | + +##### Returns🔙 + +`number` + +___ + +### eq + +▸ **eq**(`x`, `y`): `boolean` + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `x` | `number` | +| `y` | `number` | + +#### Returns🔙 + +`boolean` + +___ + +### expressionParser + +▸ **expressionParser**(`color`, `modeChannel`, `expression`): `number` + +Takes an arithmetic operator followed by a value and passes the result of the expression to the specified channel. Currently supports addition,subtraction,division and multiplication symbols only. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color. | +| `modeChannel` | `string` | The colorspace channel to set. | +| `expression` | `string` | The expression assignment as a string. | + +#### Returns🔙 + +`number` + +**`Example`** 📋 + +```ts +console.log(lch('blue')); +// { mode: 'lch',l: 29.568297153444703,c: 131.2014771995311,h: 301.36428148973533} + +console.log(expressionParser('blue', 'lch.l', '*0.3')); +// { mode: 'lch',l: 8.87048914603341,c: 131.2014771995311,h: 301.36428148973533 } +``` + +___ + +### filteredArr + +▸ **filteredArr**(`factor`, `cb?`): (`collection`: `object` \| [`ColorToken`](types.md#ColorToken)[], `start`: `string` \| `number`, `end?`: `number`) => [`ColorToken`](types.md#ColorToken)[] + +Filters an array according to the value of a color's queried factor + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `factor` | [`Factor`](types.md#Factor) | The property to query and use as filtering criteria | +| `cb?` | `unknown` | The function to use for comparison | + +#### Returns🔙 + +`fn` + +The filtered array + +▸ (`collection`, `start`, `end?`): [`ColorToken`](types.md#ColorToken)[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | +| `start` | `string` \| `number` | +| `end?` | `number` | + +##### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +___ + +### floorCeil + +▸ **floorCeil**(`num`): `number` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `num` | `number` | The number to round up or down. | + +#### Returns🔙 + +`number` + +An integer + + + +Rounds up or down a number based on the float value. + +**`Example`** 📋 + +```ts +console.log(floorCeil(1.45)); +// 1 +console.log(floorCeil(1.501)); +// 2 +``` + +___ + +### getModeChannel + +▸ **getModeChannel**(`colorspace`, `index?`): `string` + +Gets the clipped string of a passed in colorspace by removing non-channel characters. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `colorspace` | `string` | The colorspace to get the channel keys. | +| `index?` | `number` | Optional index to return a single specified channel. | + +#### Returns🔙 + +`string` + +A string. + +**`Example`** 📋 + +```ts +console.log(getModeChannel("oklch")); +// lch + +console.log(getModeChannel("okhsl", 2)); +// l +``` + +___ + +### gt + +▸ **gt**(`x`, `y`): `boolean` + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `x` | `number` | +| `y` | `number` | + +#### Returns🔙 + +`boolean` + +___ + +### gte + +▸ **gte**(`x`, `y`): `boolean` + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `x` | `number` | +| `y` | `number` | + +#### Returns🔙 + +`boolean` + +___ + +### inRange + +▸ **inRange**(`number`, `start`, `end?`): `boolean` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `number` | `number` | The number to check. | +| `start` | `number` | The minimum or starting value. | +| `end?` | `number` | The maximum or starting value. | + +#### Returns🔙 + +`boolean` + +True if the number is in range else false. + + + +Checks if a value is within the start and end range. + +___ + +### isInteger + +▸ **isInteger**(`num`): `boolean` + +Checks if a number is an integer or float. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `num` | `string` \| `number` | The number to query | + +#### Returns🔙 + +`boolean` + +True if the number is an integer else false if it is a float. + +___ + +### lt + +▸ **lt**(`x`, `y`): `boolean` + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `x` | `number` | +| `y` | `number` | + +#### Returns🔙 + +`boolean` + +___ + +### lte + +▸ **lte**(`x`, `y`): `boolean` + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `x` | `number` | +| `y` | `number` | + +#### Returns🔙 + +`boolean` + +___ + +### matchChromaChannel + +▸ **matchChromaChannel**(`colorspace`): `string` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `colorspace` | `string` | The color space to match saturation/chroma channel. | + +#### Returns🔙 + +`string` + +The mode channel string passed to getChannel() + + + +Matches the chroma/saturation channel of any compliant color space + +**`Example`** 📋 + +```ts +import { matchChromaChannel } from 'huetiful-js' +console.log(matchChromaChannel("jch")); +// jch.c + +console.log(matchChromaChannel("okhsl")); +// okhsl.s +``` + +___ + +### matchComparator + +▸ **matchComparator**(`s`): `string` + +Matches the comparison symbols used in the expression string. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `s` | `string` | The string to match. | + +#### Returns🔙 + +`string` + +The matched comparator, if any, as a string. + +___ + +### matchDigits + +▸ **matchDigits**(`s`): `string` + +Gets the digits in the expression string + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `s` | `string` | Thestring to match | + +#### Returns🔙 + +`string` + +The matched digits, if any, as a string. + +___ + +### matchLightnessChannel + +▸ **matchLightnessChannel**(`colorspace`): `string` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `colorspace` | `string` | The color space to match lightness channel. | + +#### Returns🔙 + +`string` + +The mode channel string passed to getChannel + + + +Matches the lightness channel of any compliant color space + +**`Example`** 📋 + +```ts +console.log(matchLightnessChannel("jch")); +// jch.j + +console.log(matchLightnessChannel("okhsl")); +// okhsl.l +``` + +___ + +### max + +▸ **max**(`array`): `number` + +Gets the largest value in an array + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `array` | `number`[] | The array to retrieve maximum value | + +#### Returns🔙 + +`number` + +The largest number in the array + +**`Example`** 📋 + +```ts +console.log(max([0, 3, 4])); +// 4 +``` + +___ + +### min + +▸ **min**(`array`): `number` + +Gets the smallest value in an array + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `array` | `number`[] | The array to retrieve minimum value | + +#### Returns🔙 + +`number` + +The smallest number in the array + +**`Example`** 📋 + +```ts +console.log(min([0, 3, 4])); +// 0 +``` + +___ + +### normalize + +▸ **normalize**(`value`, `modeChannel`): `number` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `value` | `number` | The value to chec if its in the accepted range for the passed in mode channel | +| `modeChannel` | `string` | A string defining the mode and channel ranges to use for comparison | + +#### Returns🔙 + +`number` + +The normalized channel value or the passed in value if it was within range + + + +Normalizes passed in channel value to a range accepted by color spaces as defined in Culori. + +___ + +### random + +▸ **random**(`min`, `max`): `number` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `min` | `number` | The lower bound. | +| `max` | `number` | The upper bound. | + +#### Returns🔙 + +`number` + +A number. + + + +Returns a random number between minimum and maximum bounds. + +___ + +### sortedArr + +▸ **sortedArr**(`factor`, `callback`, `order`, `colorObj?`): (`collection`: `object` \| [`ColorToken`](types.md#ColorToken)[]) => `any`[] + +Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `factor` | [`Factor`](types.md#Factor) | `undefined` | The property to query | +| `callback` | `unknown` | `undefined` | The function to use for comparison. | +| `order` | [`Order`](types.md#Order) | `undefined` | - | +| `colorObj` | `boolean` | `false` | - | + +#### Returns🔙 + +`fn` + +An array of colors or color objects. + +▸ (`collection`): `any`[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | + +##### Returns🔙 + +`any`[] diff --git a/spacebook/content/pages/index.md b/spacebook/content/pages/index.md new file mode 100644 index 00000000..ace60b2f --- /dev/null +++ b/spacebook/content/pages/index.md @@ -0,0 +1,93 @@ +--- +title: Home +permalink: / +layout: layouts/page.njk +eleventyNavigation: + key: Home + order: 0 +--- + +![Huetiful](/content/images/huetiful-logo.png) + +Open source TypeScript library for general purpose color manipulations and generating custom color scales based on Culori. + +## Getting started⛳ + +### Node + +The library🧾 is on npm as a package📦 for use in NodeJS: + +```bash +npm i huetiful-js +``` + +You can use a CDN in this example, jsdelivr to load the library remotely: + +```js +import {...} from 'https://cdn.jsdelivr.net/npm/huetiful-js/lib/huetiful.esm.min.mjs' + +``` + +### Browser + +Or load the library as a UMD glabal (`huetiful`) in your HTML file using a ` +``` + +## Modules + +Each module has a corresponding Typescript source file [in the repository](https://github.com/prjctimg/huetiful). + +- [colors](/colors.html) +- [converters](/converters.html) +- [filterBy](/filterBy.html) +- [generators](/generators.html) +- [helpers](/helpers.html) +- [sortBy](/sortBy.html) +- [types](/types.html) +- [utils](/utils.html) + +## Notes and references + +- [Quickstart](/quickstart.html) +- [About generator functions](/about-generators.html) +- [About filtering functions](/about-filtering-functions) +- [The attributes of a color: An introduction](about-color.html) +- [About colorspaces](/about-colorspaces.html) +- [About common types used](/about-types.html) +- [About utilities](/about-utils.html) + +## What's next🤷🏽‍♂️ + +> The possibilities are limited by the imagination🤯 of the user._ +> ~ me :smile: + +[See the full docs here📜](https:prjctimg.github.io/huetiful) + +## Need help😣 ? + +See some unexpected results😖? [Check the issue tracker](https://github.com/prjctimg/huetiful/issues) to open an issue or search for the problem to see if your issue already exists or has been resolved. + +Would like to join the chat🗣️ and share ideas💡 and suggestions💭 ? [See the discussions and just say hi, or share a coding meme(whatever breaks the ice🏔️)](https://github.com/prjctimg/huetiful/discussions) + +## Contributing👐🏾 + +This project is fully open source so contributions are welcome! Help make this project better by suggesting improvements or features and patching bugs🐛. See🔍 the [CONTRIBUTING](./CONTRIBUTING.md) file for more information on how to get started. + +## Donating + +This project is completely free and will remain so forever. But if you wish to support the development of this project or just buy the developer a coffee, please feel free. + +## References🔗 + +[Coloring with code: A programmatic approach by George Francis](https://tympanus.net/codrops/2021/12/07/coloring-with-code-a-programmatic-approach-to-design/) + +> ###### License +> +> Copyright (c) 2023, +> Dean Tarisai and contributors +> huetiful-js is released under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) license. diff --git a/spacebook/content/pages/pages.json b/spacebook/content/pages/pages.json new file mode 100644 index 00000000..c93d256b --- /dev/null +++ b/spacebook/content/pages/pages.json @@ -0,0 +1,5 @@ +{ + "layout": "/layouts/page.njk", + "permalink": "/{{title | slug}}", + "date": "Last Modified" +} diff --git a/spacebook/content/pages/sortBy.md b/spacebook/content/pages/sortBy.md new file mode 100644 index 00000000..2316e0d8 --- /dev/null +++ b/spacebook/content/pages/sortBy.md @@ -0,0 +1,353 @@ +--- +title: Sorting functions +eleventyNavigation: + order: 6 + title: Sorting functions +--- + +# Module:📦 sortBy + +## Table of contents📜 + +### Functions🧰 + +- [sortByContrast](sortBy.md#sortByContrast) +- [sortByDistance](sortBy.md#sortByDistance) +- [sortByHue](sortBy.md#sortByHue) +- [sortByLightness](sortBy.md#sortByLightness) +- [sortByLuminance](sortBy.md#sortByLuminance) +- [sortBySaturation](sortBy.md#sortBySaturation) + +## Functions + +### sortByContrast + +▸ **sortByContrast**(`collection`, `against`, `order?`): [`ColorToken`](types.md#ColorToken)[] + +Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param) + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `against` | [`ColorToken`](types.md#ColorToken) | - | +| `order?` | [`Order`](types.md#Order) | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of the sorted color values. + +**`Example`** 📋 + +```ts +import { sortByContrast } from 'huetiful-js' + +let sample = ['purple', 'green', 'red', 'brown'] +console.log(sortByContrast(sample, 'yellow')) +// [ 'red', 'green', 'brown', 'purple' ] + +console.log(sortByContrast(sample, 'yellow', 'desc')) +// [ 'purple', 'brown', 'green', 'red' ] +``` + +___ + +### sortByDistance + +▸ **sortByDistance**(`collection`, `against`, `order?`, `options?`): [`ColorToken`](types.md#ColorToken)[] + +Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `against` | [`ColorToken`](types.md#ColorToken) | The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array. | +| `order?` | ``"asc"`` \| ``"desc"`` | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') | +| `options?` | [`ColorDistanceOptions`](types.md#ColorDistanceOptions) | - | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of the sorted color values. + +**`Example`** 📋 + +```ts +import { sortByDistance } from 'huetiful-js' + +let sample = ['purple', 'green', 'red', 'brown'] +console.log( + sortByDistance(sample, 'yellow', 'asc', { + mode: 'lch', + }) +) + +// [ 'brown', 'red', 'green', 'purple' ] + +let sample = ['purple', 'green', 'red', 'brown'] +console.log( + sortByDistance(sample, 'yellow', 'asc', { + mode: 'lch', + }) +) + +// [ 'green', 'brown', 'red', 'purple' ] +``` + +___ + +### sortByHue + +▸ **sortByHue**(`collection`, `order?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[] + +Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `order?` | [`Order`](types.md#Order) | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of the sorted color values. + +**`Example`** 📋 + +```ts +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + +let sorted = sortByHue(sample); +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#ffff00', + '#164100', '#00c000', + '#007e00', '#00ff78', + '#00ffdc' +] + +let sortedDescending = sortByHue(sample,'desc'); +console.log(sortedDescending) +// [ + '#00ffdc', '#00ff78', + '#007e00', '#00c000', + '#164100', '#ffff00', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] +``` + +___ + +### sortByLightness + +▸ **sortByLightness**(`collection`, `order?`, `colorspace?`): [`ColorToken`](types.md#ColorToken)[] + +Sorts colors according to their lightness. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `order?` | [`Order`](types.md#Order) | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | The mode colorspace to use for filtering color lightness. Defaut is lch65 | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of the sorted color values. + +**`Example`** 📋 + +```ts +import { sortByLightness } from "huetiful-js"; + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +] + +sortByLightness(sample) + +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + +sortByLightness(sample,'desc') + +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] +``` + +___ + +### sortByLuminance + +▸ **sortByLuminance**(`collection`, `order`): [`ColorToken`](types.md#ColorToken)[] + +Sorts colors according to the relative brightness as defined by WCAG definition. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `order` | ``"asc"`` \| ``"desc"`` | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of the sorted color values. + +**`Example`** 📋 + +```ts +import { sortByLuminance } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + +let sorted = sortByLuminance(sample) +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + +let sortedDescending = sortByLuminance(sample, "desc"); +console.log(sortedDescending) +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] +``` + +___ + +### sortBySaturation + +▸ **sortBySaturation**(`collection`, `order`, `mode?`): [`ColorToken`](types.md#ColorToken)[] + +Sorts colors according to their saturation. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `order` | ``"asc"`` \| ``"desc"`` | The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') | +| `mode?` | [`HueColorSpaces`](types.md#HueColorSpaces) | The mode color space to compute the saturation value in. The default is jch . | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +An array of the sorted color values. + +**`Example`** 📋 + +```ts +import { sortBySaturation } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + +let sorted = sortBySaturation(sample); +console.log(sorted); + +// [ + '#310000', '#3e0000', + '#164100', '#4e0000', + '#600000', '#720000', + '#00ffdc', '#007e00', + '#00ff78', '#00c000', + '#ffff00' +] + +let sortedDescending = sortBySaturation(sample,'desc'); +console.log(sortedDescending) +// [ + '#ffff00', '#00c000', + '#00ff78', '#007e00', + '#00ffdc', '#720000', + '#600000', '#4e0000', + '#164100', '#3e0000', + '#310000' +] +``` diff --git a/spacebook/content/pages/types.md b/spacebook/content/pages/types.md new file mode 100644 index 00000000..a8804463 --- /dev/null +++ b/spacebook/content/pages/types.md @@ -0,0 +1,324 @@ +--- +title: Types +eleventyNavigation: + order: 7 + title: Types +--- + +# Module:📦 types + +## Table of contents📜 + +### Type Aliases + +- [AdaptivePaletteOptions](types.md#AdaptivePaletteOptions) +- [ColorDistanceOptions](types.md#ColorDistanceOptions) +- [ColorObject](types.md#ColorObject) +- [ColorOptions](types.md#ColorOptions) +- [ColorToken](types.md#ColorToken) +- [ColorTuple](types.md#ColorTuple) +- [Colorspaces](types.md#Colorspaces) +- [DeficiencyType](types.md#DeficiencyType) +- [DivergingScheme](types.md#DivergingScheme) +- [EarthtoneOptions](types.md#EarthtoneOptions) +- [Factor](types.md#Factor) +- [FactorMapper](types.md#FactorMapper) +- [HueColorSpaces](types.md#HueColorSpaces) +- [HueFamily](types.md#HueFamily) +- [HueShiftOptions](types.md#HueShiftOptions) +- [Interpolator](types.md#Interpolator) +- [InterpolatorOptions](types.md#InterpolatorOptions) +- [Options](types.md#Options) +- [Order](types.md#Order) +- [PairedSchemeOptions](types.md#PairedSchemeOptions) +- [QualitativeScheme](types.md#QualitativeScheme) +- [ScaleValues](types.md#ScaleValues) +- [SequentialScheme](types.md#SequentialScheme) +- [TailwindColorFamilies](types.md#TailwindColorFamilies) +- [Tone](types.md#Tone) +- [UniformColorSpaces](types.md#UniformColorSpaces) +- [callback](types.md#callback) + +## Type Aliases + +### AdaptivePaletteOptions + +Ƭ **AdaptivePaletteOptions**: `Object` + +**`Description`** ℹ + +This object returns the lightMode and darkMode optimized version of a color with support to add color vision deficiency simulation to the final color result. + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `backgroundColor?` | \{ `dark?`: [`ColorToken`](types.md#ColorToken) ; `light?`: [`ColorToken`](types.md#ColorToken) } | +| `backgroundColor.dark?` | [`ColorToken`](types.md#ColorToken) | +| `backgroundColor.light?` | [`ColorToken`](types.md#ColorToken) | +| `colorBlind?` | `boolean` | +| `viewingConditions?` | `ViewingConditions` | + +___ + +### ColorDistanceOptions + +Ƭ **ColorDistanceOptions**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `mode?` | [`Colorspaces`](types.md#Colorspaces) | +| `weights?` | [`number`, `number`, `number`, `number`] | + +___ + +### ColorObject + +Ƭ **ColorObject**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `alpha?` | `number` | +| `mode` | [`Colorspaces`](types.md#Colorspaces) | + +___ + +### ColorOptions + +Ƭ **ColorOptions**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `alpha?` | `number` | +| `colorSpace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | +| `contrast?` | `number` | +| `darkMode?` | [`ColorToken`](types.md#ColorToken) | +| `lightMode?` | [`ColorToken`](types.md#ColorToken) | +| `lightness?` | `number` | +| `luminance?` | `number` | +| `saturation?` | `number` | +| `temperature?` | `number` | + +___ + +### ColorToken + +Ƭ **ColorToken**: `number` \| `string` \| `object` \| [`ColorTuple`](types.md#ColorTuple) + +**`Description`** ℹ + +Any recognizable color token. + +___ + +### ColorTuple + +Ƭ **ColorTuple**: [`string`, `number`, `number`, `number`, number?] + +___ + +### Colorspaces + +Ƭ **Colorspaces**: ``"a98"`` \| ``"cubehelix"`` \| ``"dlab"`` \| ``"jab"`` \| ``"lab"`` \| ``"lab65"`` \| ``"lrgb"`` \| ``"luv"`` \| ``"oklab"`` \| ``"rgb"`` \| [`HueColorSpaces`](types.md#HueColorSpaces) + +___ + +### DeficiencyType + +Ƭ **DeficiencyType**: ``"red"`` \| ``"blue"`` \| ``"green"`` \| ``"monochromacy"`` + +___ + +### DivergingScheme + +Ƭ **DivergingScheme**: ``"Spectral"`` \| ``"RdYlGn"`` \| ``"RdBu"`` \| ``"PiYG"`` \| ``"PRGn"`` \| ``"RdYlBu"`` \| ``"BrBG"`` \| ``"RdGy"`` \| ``"PuOr"`` + +___ + +### EarthtoneOptions + +Ƭ **EarthtoneOptions**: `Omit`\<[`Options`](types.md#Options), ``"hueStep"`` \| ``"via"`` \| ``"maxLightness"`` \| ``"minLightness"``\> + +___ + +### Factor + +Ƭ **Factor**: ``"luminance"`` \| ``"temp"`` \| ``"saturation"`` \| ``"contrast"`` \| ``"distance"`` \| ``"lightness"`` \| ``"hue"`` + +___ + +### FactorMapper + +Ƭ **FactorMapper**: (`factor`: [`Factor`](types.md#Factor), `callback`: [`callback`](types.md#callback), `order?`: [`Order`](types.md#Order), `colorObj?`: `boolean`) => (`colors`: [`ColorToken`](types.md#ColorToken)[]) => [`ColorToken`](types.md#ColorToken)[] + +#### Type declaration + +▸ (`factor`, `callback`, `order?`, `colorObj?`): (`colors`: [`ColorToken`](types.md#ColorToken)[]) => [`ColorToken`](types.md#ColorToken)[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `factor` | [`Factor`](types.md#Factor) | +| `callback` | [`callback`](types.md#callback) | +| `order?` | [`Order`](types.md#Order) | +| `colorObj?` | `boolean` | + +##### Returns🔙 + +`fn` + +▸ (`colors`): [`ColorToken`](types.md#ColorToken)[] + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `colors` | [`ColorToken`](types.md#ColorToken)[] | + +##### Returns🔙 + +[`ColorToken`](types.md#ColorToken)[] + +___ + +### HueColorSpaces + +Ƭ **HueColorSpaces**: [`UniformColorSpaces`](types.md#UniformColorSpaces) \| ``"hsl"`` \| ``"hsv"`` \| ``"hsi"`` \| ``"hwb"`` \| ``"okhsl"`` \| ``"okhsv"`` + +___ + +### HueFamily + +Ƭ **HueFamily**: ``"red-purple"`` \| ``"red"`` \| ``"yellow-red"`` \| ``"yellow"`` \| ``"green-yellow"`` \| ``"green"`` \| ``"blue-green"`` \| ``"blue"`` \| ``"purple-blue"`` \| ``"purple"`` + +___ + +### HueShiftOptions + +Ƭ **HueShiftOptions**: `Omit`\<[`Options`](types.md#Options), ``"via"`` \| ``"earthtones"`` \| ``""``\> & [`InterpolatorOptions`](types.md#InterpolatorOptions) + +___ + +### Interpolator + +Ƭ **Interpolator**: (`arr`: `number`[]) => (`t`: `number`) => `number` + +#### Type declaration + +▸ (`arr`): (`t`: `number`) => `number` + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `arr` | `number`[] | + +##### Returns🔙 + +`fn` + +▸ (`t`): `number` + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `t` | `number` | + +##### Returns🔙 + +`number` + +___ + +### InterpolatorOptions + +Ƭ **InterpolatorOptions**: `Pick`\<[`Options`](types.md#Options), ``"easingFunc"`` \| ``"hueInterpolator"`` \| ``"chromaInterpolator"`` \| ``"hueFixup"`` \| ``"lightnessInterpolator"``\> + +___ + +### Options + +Ƭ **Options**: `Object` + +**`Description`** ℹ + +The override parameters for palette functions. + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `chromaInterpolator?` | [`Interpolator`](types.md#Interpolator) | **`Param`** interpolation method to use on the chroma channel. | +| `earthtones?` | ``"light-gray"`` \| ``"silver"`` \| ``"sand"`` \| ``"tupe"`` \| ``"mahogany"`` \| ``"brick-red"`` \| ``"clay"`` \| ``"cocoa"`` \| ``"dark-brown"`` \| ``"dark"`` | * **`Param`** The earthtone to interpolate with. | +| `easingFunc?` | (`t`: `number`) => `number` | The easing function to use. | +| `hueFixup?` | (`arr`: `number`[]) => `number`[] | | +| `hueInterpolator?` | [`Interpolator`](types.md#Interpolator) | **`Param`** interpolation method to use on the hue channel. | +| `hueStep?` | `number` | **`Param`** amount of hue angles to increment each iteration with. | +| `lightnessInterpolator?` | [`Interpolator`](types.md#Interpolator) | **`Param`** interpolation method to use on the lightness channel. | +| `maxLightness?` | `number` | **`Param`** Maximum lightness value (range 0-100). | +| `minLightness?` | `number` | * **`Param`** Minimum lightness value (range 0-100). | +| `samples?` | `number` | **`Param`** amount of samples to return in the result collection. | +| `via?` | [`Tone`](types.md#Tone) | **`Param`** color to pass through during interpolation. | + +___ + +### Order + +Ƭ **Order**: ``"asc"`` \| ``"desc"`` + +___ + +### PairedSchemeOptions + +Ƭ **PairedSchemeOptions**: `Omit`\<[`Options`](types.md#Options), ``"earthtones"`` \| ``"maxLightness"`` \| ``"minLightness"``\> + +___ + +### QualitativeScheme + +Ƭ **QualitativeScheme**: ``"Set2"`` \| ``"Accent"`` \| ``"Set1"`` \| ``"Set3"`` \| ``"Dark2"`` \| ``"Paired"`` \| ``"Pastel2"`` \| ``"Pastel1"`` + +___ + +### ScaleValues + +Ƭ **ScaleValues**: ``"50"`` \| ``"100"`` \| ``"200"`` \| ``"300"`` \| ``"400"`` \| ``"500"`` \| ``"600"`` \| ``"700"`` \| ``"800"`` \| ``"900"`` + +___ + +### SequentialScheme + +Ƭ **SequentialScheme**: ``"OrRd"`` \| ``"PuBu"`` \| ``"BuPu"`` \| ``"Oranges"`` \| ``"BuGn"`` \| ``"YlOrBr"`` \| ``"YlGn"`` \| ``"Reds"`` \| ``"RdPu"`` \| ``"Greens"`` \| ``"YlGnBu"`` \| ``"Purples"`` \| ``"GnBu"`` \| ``"Greys"`` \| ``"YlOrRd"`` \| ``"PuRd"`` \| ``"Blues"`` \| ``"PuBuGn"`` \| ``"Viridis"`` + +___ + +### TailwindColorFamilies + +Ƭ **TailwindColorFamilies**: ``"indigo"`` \| ``"gray"`` \| ``"zinc"`` \| ``"neutral"`` \| ``"stone"`` \| ``"red"`` \| ``"orange"`` \| ``"amber"`` \| ``"yellow"`` \| ``"lime"`` \| ``"green"`` \| ``"emerald"`` \| ``"teal"`` \| ``"sky"`` \| ``"blue"`` \| ``"violet"`` \| ``"purple"`` \| ``"fuchsia"`` \| ``"pink"`` \| ``"rose"`` + +___ + +### Tone + +Ƭ **Tone**: ``"light"`` \| ``"dark"`` + +___ + +### UniformColorSpaces + +Ƭ **UniformColorSpaces**: ``"lch"`` \| ``"jch"`` \| ``"dlch"`` \| ``"lch"`` \| ``"lch65"`` \| ``"lchuv"`` \| ``"oklch"`` + +___ + +### callback + +Ƭ **callback**: `unknown` diff --git a/spacebook/content/pages/utils.md b/spacebook/content/pages/utils.md new file mode 100644 index 00000000..0b4bacd3 --- /dev/null +++ b/spacebook/content/pages/utils.md @@ -0,0 +1,886 @@ +--- +title: Utilities +eleventyNavigation: + order: 4 + title: Utility functions +--- + + +# Module:📦 utils + +## Table of contents📜 + +### Functions🧰 + +- [alpha](utils.md#alpha) +- [brighten](utils.md#brighten) +- [colorDeficiency](utils.md#colorDeficiency) +- [darken](utils.md#darken) +- [getChannel](utils.md#getChannel) +- [getComplimentaryHue](utils.md#getComplimentaryHue) +- [getContrast](utils.md#getContrast) +- [getFarthestChroma](utils.md#getFarthestChroma) +- [getFarthestContrast](utils.md#getFarthestContrast) +- [getFarthestHue](utils.md#getFarthestHue) +- [getFarthestLightness](utils.md#getFarthestLightness) +- [getHueFamily](utils.md#getHueFamily) +- [getLuminance](utils.md#getLuminance) +- [getNearestChroma](utils.md#getNearestChroma) +- [getNearestColor](utils.md#getNearestColor) +- [getNearestContrast](utils.md#getNearestContrast) +- [getNearestHue](utils.md#getNearestHue) +- [getNearestLightness](utils.md#getNearestLightness) +- [isAchromatic](utils.md#isAchromatic) +- [isCool](utils.md#isCool) +- [isWarm](utils.md#isWarm) +- [overtone](utils.md#overtone) +- [setChannel](utils.md#setChannel) +- [setLuminance](utils.md#setLuminance) + +## Functions + +### alpha + +▸ **alpha**(`color`, `value?`): `number` + +Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color with the targeted opacity/alpha channel. | +| `value?` | `string` \| `number` | The value to apply to the opacity channel. The value is between [0,1] | + +#### Returns🔙 + +`number` + +color The resulting color. Returns an 8 character hex code. + +**`Example`** 📋 + +```ts +// Getting the alpha +console.log(alpha('#a1bd2f0d')) +// 0.050980392156862744 + +// Setting the alpha + +let myColor = alpha('b2c3f1', 0.5) + +console.log(myColor) + +// #b2c3f180 +``` + +___ + +### brighten + +▸ **brighten**(`color`, `value`, `colorspace`): [`ColorToken`](types.md#ColorToken) + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to brighten. | +| `value` | `string` \| `number` | The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") | +| `colorspace` | `any` | - | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +___ + +### colorDeficiency + +▸ **colorDeficiency**(`deficiencyType?`): (`color`: [`ColorToken`](types.md#ColorToken), `severity`: `number`) => `string` + +Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `deficiencyType?` | [`DeficiencyType`](types.md#DeficiencyType) | The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value. | + +#### Returns🔙 + +`fn` + +The color as its simulated variant as a hexadecimal string. + +▸ (`color`, `severity?`): `string` + +##### Parameters🧮 + +| Name | Type | Default value | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | `undefined` | +| `severity` | `number` | `1` | + +##### Returns🔙 + +`string` + +**`See`** + +For a deep dive on color vision deficiency go to + +**`Example`** 📋 + +```ts +import { colorDeficiency, toHex } from 'huetiful-js' + +// Here we are simulating color blindness of tritanomaly or we can't see 'blue'. +// We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1 +let tritanomaly = colorDeficiency('blue') +console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1)) +// #dd663680 + +// Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1 +let protanopia = colorDeficiency('red') +console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' })) +// #9f9f9f +``` + +___ + +### darken + +▸ **darken**(`color`, `value`): [`ColorToken`](types.md#ColorToken) + +Darkens the color by reducing the lightness channel. . + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to darken. | +| `value` | `string` \| `number` | The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +color The darkened color. + +**`Example`** 📋 + +```ts + +``` + +___ + +### getChannel + +▸ **getChannel**(`mc`): (`color`: [`ColorToken`](types.md#ColorToken)) => `number` + +Gets the value specifified channel on the color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `mc` | `string` | The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color. | + +#### Returns🔙 + +`fn` + +value The value of the queried channel. + +▸ (`color`): `number` + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | + +##### Returns🔙 + +`number` + +**`Example`** 📋 + +```ts +import { getChannel } from 'huetiful-js' + +console.log(getChannel('rgb.g')('#a1bd2f')) +// 0.7411764705882353 +``` + +___ + +### getComplimentaryHue + +▸ **getComplimentaryHue**(`color`, `colorspace?`, `colorObj?`): \{ `color`: [`ColorToken`](types.md#ColorToken) ; `hue`: `string` } \| [`ColorToken`](types.md#ColorToken) + +Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | `undefined` | The color to retrieve its complimentary hue. | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | - | +| `colorObj` | `boolean` | `false` | Optional boolean whether to return an object with the result color hue family or just the result color. Default is false. | + +#### Returns🔙 + +\{ `color`: [`ColorToken`](types.md#ColorToken) ; `hue`: `string` } \| [`ColorToken`](types.md#ColorToken) + +An object with the hue family and complimentary color as keys. + +**`Example`** 📋 + +```ts +import { getComplimentaryHue } from "huetiful-js"; + +console.log(getComplimentaryHue("pink", true)) +//// { hue: 'blue-green', color: '#97dfd7ff' } + +console.log(getComplimentaryHue("purple")) +// #005700ff +``` + +___ + +### getContrast + +▸ **getContrast**(`color`, `against`): `number` + +Gets the contrast between the passed in colors. + +#### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | +| `against` | [`ColorToken`](types.md#ColorToken) | + +#### Returns🔙 + +`number` + +The relative luminance of the lightest color. + +**`Example`** 📋 + +```ts +import { getContrast } from 'huetiful-js' + +console.log(getContrast("black", "white")); +// 21 +``` + +___ + +### getFarthestChroma + +▸ **getFarthestChroma**(`collection`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +Gets the largest saturation value from the passed in colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +The largest saturation value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getFarthestChroma } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getFarthestChroma(sample, 'lch')) +// 67.22120855010492 +``` + +___ + +### getFarthestContrast + +▸ **getFarthestContrast**(`collection`, `against`, `colorObj?`): `number` \| \{ `factor`: `number` ; `name`: [`ColorToken`](types.md#ColorToken) } + +Gets the largest contrast value from the passed in colors compared against a sample color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `against` | [`ColorToken`](types.md#ColorToken) | - | +| `colorObj?` | `boolean` | Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `factor`: `number` ; `name`: [`ColorToken`](types.md#ColorToken) } + +The largest contrast value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getFarthestContrast } from 'huetiful-js' + +console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green")); +// 3.08355493246362 + +console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true)); +// { contrast: 3.08355493246362, name: '#f3bac1' } +``` + +___ + +### getFarthestHue + +▸ **getFarthestHue**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +Gets the largest hue value from the passed in colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The mode color space to perform the computation in. | +| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +The largest hue value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getFarthestHue } from 'huetiful-js' +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getFarthestHue(sample, 'lch')) +// 273.54920266436477 +``` + +___ + +### getFarthestLightness + +▸ **getFarthestLightness**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +Gets the largest lightness value from the passed in colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | THe mode colorspace to retrieve the lightness value from. | +| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +The largest lightness value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getFarthestLightness } from 'huetiful-js' + +let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] + +console.log(getFarthestLightness(sample, true)) + +// { lightness: 80.94668903360088, name: '#f3bac1' } +``` + +___ + +### getHueFamily + +▸ **getHueFamily**(`color`, `mode?`): [`HueFamily`](types.md#HueFamily) + +Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray". + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to query its shade or hue family. | +| `mode?` | [`HueColorSpaces`](types.md#HueColorSpaces) | - | + +#### Returns🔙 + +[`HueFamily`](types.md#HueFamily) + +The name of the hue family for example red or green. + +**`Example`** 📋 + +```ts +import { getHue } from 'huetiful-js' + +console.log(getHue("#310000")) +// red +``` + +___ + +### getLuminance + +▸ **getLuminance**(`color`): `number` + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to query. | + +#### Returns🔙 + +`number` + +value The color's luminance value. + +**`Alias`** + +Gets the luminance value of that color as defined by WCAG. + +**`Example`** 📋 + +```ts +import { getLuminance } from 'huetiful-js' + +console.log(getLuminance('#a1bd2f')) +// 0.4417749513730954 +``` + +___ + +### getNearestChroma + +▸ **getNearestChroma**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +Gets the smallest chroma/saturation value from the passed in colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | The mode color space to perform the computation in. | +| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +The smallest chroma/saturation value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getNearestChroma } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getNearestChroma(sample, 'lch')) +// 22.45669293295522 +``` + +___ + +### getNearestColor + +▸ **getNearestColor**(`collection`, `color`, `num?`): [`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[] + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | [`ColorToken`](types.md#ColorToken)[] \| ``"tailwind"`` | `undefined` | The collection of colors to search for nearest colors | +| `color` | [`ColorToken`](types.md#ColorToken) | `undefined` | The color to use for distance comparison | +| `num` | `number` | `1` | The number of colors to return, if the value is above the colors in the available sample, the entire collection is returned with colors ordered in ascending order using the differenceHyab metric. | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) \| [`ColorToken`](types.md#ColorToken)[] + +An array of colors. + +**`Example`** 📋 + +```ts +let cols = colors('all', '500') + +console.log(getNearestColor(cols, 'blue', 3)); +// [ '#a855f7', '#8b5cf6', '#d946ef' ] +``` + +___ + +### getNearestContrast + +▸ **getNearestContrast**(`collection`, `against`, `colorObj?`): `any` + +Gets the smallest contrast value from the passed in colors compared against a sample color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | - | +| `against` | [`ColorToken`](types.md#ColorToken) | - | +| `colorObj?` | `boolean` | Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`any` + +The smallest contrast value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getNearestContrast } from 'huetiful-js' + +console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green")); +// 2.4061390502133424 + +console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true)); +// { contrast: 2.4061390502133424, name: '#a1bd2f' } +``` + +___ + +### getNearestHue + +▸ **getNearestHue**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +Gets the smallest hue value from the passed in colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `colorspace?` | `string` | `undefined` | The mode color space to perform the computation in. | +| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +The smallest hue value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getNearestHue } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getNearestHue(sample, 'lch')) +// 12.462831644544274 +``` + +___ + +### getNearestLightness + +▸ **getNearestLightness**(`collection`, `colorspace?`, `colorObj?`): `number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +Gets the smallest lightness value from the passed in colors. + +#### Parameters🧮 + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `collection` | `object` \| [`ColorToken`](types.md#ColorToken)[] | `undefined` | - | +| `colorspace?` | [`HueColorSpaces`](types.md#HueColorSpaces) | `undefined` | - | +| `colorObj` | `boolean` | `false` | Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. | + +#### Returns🔙 + +`number` \| \{ `color`: [`ColorToken`](types.md#ColorToken) ; `factor`: `number` } + +The smallest lightness value in the colors passed in or a custom object. + +**`Example`** 📋 + +```ts +import { getNearestLightness } from 'huetiful-js' + +let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] + +console.log(getNearestLightness(sample, true)) + +// { lightness: 72.61647882089876, name: '#a1bd2f' } +``` + +___ + +### isAchromatic + +▸ **isAchromatic**(`color`, `mode?`): `boolean` + +Checks if a color is achromatic(without hue or simply grayscale). + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to test if it is achromatic or not. | +| `mode?` | [`HueColorSpaces`](types.md#HueColorSpaces) | - | + +#### Returns🔙 + +`boolean` + +boolean Returns true if the color is achromatic else false + +**`Example`** 📋 + +```ts +import { isAchromatic } from "huetiful-js"; +import { formatHex8, interpolate, samples } from "culori" + +isAchromatic('pink') +// false + +let sample = [ + "#164100", + "#ffff00", + "#310000", + 'pink' +]; + +console.log(map(sample, isAchromatic)); + +// [false, false, false,false] + +isAchromatic('gray') +// Returns true + +console.log(map(sample, isAchromatic)); + +// we create an interpolation using black and white +let f = interpolate(["black", "white"]); + +//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function. +// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.) +let grays = map(samples(12), (c) => formatHex8(f(c))); +console.log(map(grays, isAchromatic)); + +// +[ false, true, true, + true, true, true, + true, true, true, + true, true, false +] +``` + +___ + +### isCool + +▸ **isCool**(`color`): `boolean` + +Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to check the temperature. | + +#### Returns🔙 + +`boolean` + +True or false. + +**`Example`** 📋 + +```ts +import { isCool } from 'huetiful-js' + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000" +]; + +console.log(isCool(sample[2])); +// false + +console.log(map(sample, isCool)); + +// [ true, false, true] +``` + +___ + +### isWarm + +▸ **isWarm**(`color`): `boolean` + +Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to check the temperature. | + +#### Returns🔙 + +`boolean` + +True or false. + +**`Example`** 📋 + +```ts +import { isWarm } from 'huetiful-js' + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000" +]; + +console.log(isWarm(sample[2])); +//true + +console.log(map(sample, isWarm)); + +// [ false, true, false] +``` + +___ + +### overtone + +▸ **overtone**(`color`): `string` \| `boolean` + +Returns the hue which is biasing the passed in color + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to query its overtone. | + +#### Returns🔙 + +`string` \| `boolean` + +The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false. + +**`Example`** 📋 + +```ts +import { overtone } from "huetiful-js"; + +console.log(overtone("fefefe")) +// 'gray' + +console.log(overtone("cyan")) +// 'green' + +console.log(overtone("blue")) +// false +``` + +___ + +### setChannel + +▸ **setChannel**(`mc`): (`color`: [`ColorToken`](types.md#ColorToken), `value`: `string` \| `number`) => [`ColorToken`](types.md#ColorToken) + +Sets the value for the specified channel in a color. + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `mc` | `string` | The mode and channel to work with. For example 'rgb.b'. | + +#### Returns🔙 + +`fn` + +color The mutated color. + +▸ (`color`, `value`): [`ColorToken`](types.md#ColorToken) + +##### Parameters🧮 + +| Name | Type | +| :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | +| `value` | `string` \| `number` | + +##### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +**`Example`** 📋 + +```ts +import { setChannel } from 'huetiful-js' + +let myColor = setChannel('lch.h')('green',10) + +console.log(getChannel('lch.h')(myColor)) +// 10 +``` + +___ + +### setLuminance + +▸ **setLuminance**(`color`, `lum`): [`ColorToken`](types.md#ColorToken) + +Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance). + +#### Parameters🧮 + +| Name | Type | Description | +| :------ | :------ | :------ | +| `color` | [`ColorToken`](types.md#ColorToken) | The color to set luminance | +| `lum` | `number` | The amount of luminance to set. The value range is normalised between [0,1] | + +#### Returns🔙 + +[`ColorToken`](types.md#ColorToken) + +The mutated color with the modified properties. + +**`Example`** 📋 + +```ts +import { setLuminance, getLuminance } from 'huetiful-js' + +let myColor = setLuminance('#a1bd2f', 0.5) + +console.log(getLuminance(myColor)) +// 0.4999999136285792 +``` diff --git a/spacebook/filters/searchFilter.js b/spacebook/filters/searchFilter.js new file mode 100644 index 00000000..566de841 --- /dev/null +++ b/spacebook/filters/searchFilter.js @@ -0,0 +1,56 @@ +const elasticlunr = require("elasticlunr"); +const emojiRegex = require('emoji-regex/RGI_Emoji.js') + +module.exports = function (collection) { + // what fields we'd like our index to consist of + var index = elasticlunr(function () { + this.addField("title"); + this.addField("content"); + this.setRef("id"); + }); + + // loop through each page and add it to the index + collection.forEach((page) => { + index.addDoc({ + id: page.url, + title: page.template.frontMatter.data.title, + content: squash(page.templateContent), + }); + }); + + function squash(text) { + const regex = emojiRegex(); + var content = new String(text); + + // all lower case, please + var content = content.toLowerCase(); + + // remove all html elements and new lines + var re = /(.*?<.*?>)/gi; + var plain = unescape(content.replace(re, '')); + + // remove duplicated words + var words = plain.split(' '); + var deduped = [...(new Set(words))]; + var dedupedStr = deduped.join(' ') + + // remove short and less meaningful words + var result = dedupedStr.replace(/\b(\.|\,|\<;|the|a|an|and|am|you|I|to|if|of|off|me|my|on|in|it|is|at|as|we|do|be|has|but|was|so|no|not|or|up|for)\b/gi, ''); + //remove newlines, and punctuation + result = result.replace(/\.|\,|\?|

|-|—|\n/g, ''); + //remove repeated spaces + result = result.replace(/[ ]{2,}/g, ' '); + // remove most emoji + result = result.replace(/([#0-9]\u20E3)|[\xA9\xAE\u203C\u2047-\u2049\u2122\u2139\u3030\u303D\u3297\u3299][\uFE00-\uFEFF]?|[\u2190-\u21FF][\uFE00-\uFEFF]?|[\u2300-\u23FF][\uFE00-\uFEFF]?|[\u2460-\u24FF][\uFE00-\uFEFF]?|[\u25A0-\u25FF][\uFE00-\uFEFF]?|[\u2600-\u27BF][\uFE00-\uFEFF]?|[\u2900-\u297F][\uFE00-\uFEFF]?|[\u2B00-\u2BF0][\uFE00-\uFEFF]?|(?:\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDEFF])[\uFE00-\uFEFF]?/g, ''); + + let match; + while (match = regex.exec(result)) { + const emoji = match[0]; + result = result.replace(emoji,' ') + } + + return result; + } + + return index.toJSON(); +}; \ No newline at end of file diff --git a/spacebook/package.json b/spacebook/package.json new file mode 100644 index 00000000..68f6c45a --- /dev/null +++ b/spacebook/package.json @@ -0,0 +1,67 @@ +{ + "name": "spacebook", + "version": "1.0.0", + "description": "A simple site generator based on Eleventy, Tailwind 2.0, and Alpine.js", + "scripts": { + "start": "eleventy --serve & postcss styles/tailwind.css --o _tmp/style.css --watch", + "build": "eleventy & NODE_ENV=production postcss styles/tailwind.css --o _site/style.css && ./node_modules/.bin/cleancss -o _site/style.css _site/style.css ", + "watch": "npx eleventy --watch", + "debug": "DEBUG=* npx eleventy" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/broeker/spacebook.git" + }, + "author": "Tim Broeker (https://www.electriccitizen.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/broeker/spacebook/issues" + }, + "homepage": "https://github.com/broeker/spacebook", + "devDependencies": { + "@11ty/eleventy": "^2.0.1", + "alpinejs": "^2.7.3", + "eleventy-plugin-lazyimages": "^2.1.0", + "eslint": "^7.9.0", + "lazysizes": "^5.2.2", + "luxon": "^1.25.0", + "markdown-it": "^14.0.0", + "markdown-it-anchor": "^5.3.0", + "markdown-it-image-lazysizes": "^1.0.0", + "postcss-cli": "^8.3.0", + "prettier": "^2.1.2", + "tailwindcss": "^2.0.2" + }, + "dependencies": { + "@11ty/eleventy-img": "^3.1.8", + "@11ty/eleventy-navigation": "^0.1.6", + "@tailwindcss/forms": "^0.2.1", + "@tailwindcss/typography": "^0.3.1", + "autoprefixer": "^10.1.0", + "clean-css": "^4.2.1", + "clean-css-cli": "^4.3.0", + "elasticlunr": "^0.9.5", + "eleventy-plugin-embed-everything": "^1.9.4", + "eleventy-plugin-nesting-toc": "^1.2.0", + "eleventy-plugin-svg-contents": "^0.7.0", + "eleventy-plugin-toc": "^1.1.0", + "emoji-regex": "^9.2.0", + "html-minifier": "^4.0.0", + "markdown-it-attrs": "^4.1.6", + "markdown-it-center-text": "^1.0.4", + "markdown-it-container": "^3.0.0", + "markdown-it-emoji": "^2.0.0", + "markdown-it-footnote": "^3.0.2", + "markdown-it-for-inline": "^0.1.1", + "markdown-it-linkify-images": "^2.0.0", + "markdown-it-table-of-contents": "^0.5.0", + "markdown-it-task-lists": "^2.1.1", + "postcss": "^8.2.2", + "qs": "^6.9.4", + "remove": "^0.1.5", + "staticrypt": "^1.3.2", + "uglify-es": "^3.3.9", + "url-pattern": "^1.0.3" + }, + "main": ".eleventy.js" +} diff --git a/spacebook/postcss.config.js b/spacebook/postcss.config.js new file mode 100644 index 00000000..7e8c15e1 --- /dev/null +++ b/spacebook/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: [ + require(`tailwindcss`)(`./styles/tailwind.config.js`), + require(`autoprefixer`), + ], +}; diff --git a/spacebook/robots.md b/spacebook/robots.md new file mode 100644 index 00000000..7d69aa86 --- /dev/null +++ b/spacebook/robots.md @@ -0,0 +1,5 @@ +--- +title: Robots +permalink: /robots.txt +layout: layouts/robots.njk +--- diff --git a/spacebook/search-index.json.njk b/spacebook/search-index.json.njk new file mode 100644 index 00000000..689e69a7 --- /dev/null +++ b/spacebook/search-index.json.njk @@ -0,0 +1,6 @@ +--- +permalink: /search-index.json +--- +{{ collections.results | search | dump | safe }} + + diff --git a/spacebook/styles/tailwind.config.js b/spacebook/styles/tailwind.config.js new file mode 100644 index 00000000..a8da3d6c --- /dev/null +++ b/spacebook/styles/tailwind.config.js @@ -0,0 +1,62 @@ +const autoprefixer = require('autoprefixer'); + +module.exports = { + important: true, + future: { + removeDeprecatedGapUtilities: true, + purgeLayersByDefault: true, + }, + purge: { + enabled: true, + content: ["_site/**/*.html"], + options: { + safelist: [], + }, + }, + darkMode: 'class', + theme: { + container: { + center: true, + }, + extend: { + typography: { + DEFAULT: { + css: { + maxWidth: '100%', + a: { + color: '#1D4ED8', + '&:hover': { + color: '#1E3A8A', + }, + }, + '.prose a.edit, .tag a': { + color: '#333', + 'text-decoration': 'none', + }, + 'ul.footer-nav': { + '::before': { + display: 'none', + 'text-decoration': 'none', + } + }, + 'ul.contains-task-list': { + '::before': { + display: 'none', + } + }, + 'ul.spacelog': { + '::before': { + display: 'none', + } + }, + }, + }, + } + }, + }, + variants: {}, + plugins: [ + require('@tailwindcss/typography'), + require('@tailwindcss/forms'), + ], +} \ No newline at end of file diff --git a/spacebook/styles/tailwind.css b/spacebook/styles/tailwind.css new file mode 100644 index 00000000..43a33711 --- /dev/null +++ b/spacebook/styles/tailwind.css @@ -0,0 +1,130 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + + /* Set up some default image behavior for nicer images */ + img { + @apply w-auto shadow-md border-2 border-transparent !important + } + img:hover { + @apply border-2 border-gray-100 + } + /* Overrides for Tailwind Typography prose class */ + .prose a { + @apply dark:text-gray-400 + } + .prose a:hover { + @apply dark:text-gray-500 + } + .prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 .prose hr, .prose strong { + @apply dark:text-gray-400 + } + .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 { + scroll-margin-top: 3.5em; + } + .prose pre code { + @apply overflow-x-auto !important + } + .prose .footer-nav a { + @apply no-underline !important + } + .prose ul.contains-task-list, + .prose ul.spacelog { + @apply list-none -ml-6 !important; + } + .prose ul.contains-task-list .task-list-item, + .prose ul.spacelog { + ::before { + @apply hidden !important; + } + } + + /* Define blockquotes and some standard callout blocks */ + blockquote { + @apply rounded-lg p-4 bg-gray-100 dark:bg-gray-500 border-gray-200 border-l-8 dark:border-gray-700; + } + .callout { + @apply px-8 py-4 mb-4 rounded-lg bg-yellow-50; + } + .callout, .callout strong, .callout em { + @apply dark:bg-gray-400 dark:text-gray-900; + } + .callout-blue { + @apply px-8 py-4 mb-4 rounded-lg bg-blue-50; + } + .callout-blue, .callout-blue strong, .callout-blue em { + @apply dark:text-gray-200 dark:bg-blue-900; + } + .callout-pink { + @apply px-8 py-4 mb-4 rounded-lg bg-pink-50; + } + .callout-pink, .callout-pink strong, .callout-pink em { + @apply dark:text-gray-200 dark:bg-pink-900; + } + .callout-green { + @apply px-8 py-4 mb-4 rounded-lg bg-green-50; + } + .callout-green, .callout-green strong, .callout-green em { + @apply dark:text-gray-200 dark:bg-green-900; + } + .warning { + @apply px-8 py-4 mb-4 rounded-lg bg-red-800 text-gray-50; + } + .warning, .warning strong, .warning em { + @apply text-gray-50 dark:bg-red-900 dark:text-gray-200; + } + + /* Overrides for nav/Table of Contents block */ + nav ul { + @apply ml-0 text-gray-500; + } + nav ul ul { + @apply ml-6 text-gray-500; + } + nav ul li a { + @apply mb-1 pt-2 pr-4 pb-1 pl-2 w-full block text-gray-500 dark:text-gray-500; + } + nav ul li a:hover { + @apply text-gray-900 dark:text-gray-400; + } + nav ul li a.active { + @apply font-semibold; + } + nav.toc ol li { + @apply pt-2 !important + } + nav.toc ol li li { + @apply pt-2 ml-4 + } + nav.toc ol li a { + @apply text-gray-500 + } + nav.toc ol li a:hover { + @apply text-gray-900 + } + .prose .footer-nav a:hover { + @apply dark:text-gray-400 !important + } + +/* Utilities and misc */ + .adjust p img, .adjust img, .adjust p iframe, .adjust iframe, .twitter-tweet { + @apply shadow-md ml-auto mr-auto p-2 !important + } + .adjust p img:hover, .adjust img:hover { + @apply shadow-xl + } + .adjust img.button { + @apply w-auto shadow-none !important + } + .text-align-center { + @apply flex justify-center; + } + .icon-spacer { + width: "24px"; + } + input { + @apply dark:text-gray-400 + } +} \ No newline at end of file diff --git a/spacebook/uploads/uws2.png b/spacebook/uploads/uws2.png new file mode 100644 index 00000000..4e54c5fa Binary files /dev/null and b/spacebook/uploads/uws2.png differ diff --git a/src/.eslintignore b/src/.eslintignore new file mode 100644 index 00000000..f708b012 --- /dev/null +++ b/src/.eslintignore @@ -0,0 +1,6 @@ +node_modules +dist +*.js +*.cjs +leonardo-main + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 00000000..13e839a7 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +/lib +node_modules \ No newline at end of file diff --git a/src/.prettierignore b/src/.prettierignore new file mode 100644 index 00000000..94ef8c0f --- /dev/null +++ b/src/.prettierignore @@ -0,0 +1,6 @@ +**/node_modules +**/color-maps +**/lib +.scripts +webapp +**/leonardo-main \ No newline at end of file diff --git a/src/adaptive.ts b/src/adaptive.ts new file mode 100644 index 00000000..be86fcf8 --- /dev/null +++ b/src/adaptive.ts @@ -0,0 +1,76 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +// @ts-nocheck +import type { Color, AdaptivePaletteOptions } from '../types'; +// This module will make use of contrast ratio to create adaptive palettes + +// Things I need to understand first: +// 1. What is adaptive color exactly +// -> Adaptive color is meant to adjust to luminance changes in the surrounding colors (or simply background) + +// 2. What makes color adaptive +// -> Being able to look perceptually similar between different themes or backgrounds. For example a certain color looks over saturated in a dark theme. We can change the color's luminance to compensate for this shortfall whilt maintaining the hue and saturation constraints in our design system. + +// 3. What is contrast and how does it affect adaptive color +// -> Contrast is the perceived difference in color i.e the ability of one color to stand out from another. This allows colors to be readible on different backgrounds. + +// 4. What are the specifics of contrast ratio in relation to design elements +// -> Contrast ratios allow us to define the contrast relationship between the colors we're working with. For example certain text must meet a minimum contrast ratio in order for it to be easily viewable to a wider userbase. + +// 5. What are the nuances of dark/light mode. How is the color corrected when we switch themes +// -> Certain colors will diverge from our color system if we tune them up. For example yellow will turn dark yellow if we adjust it to meet WCAG requirements in light mode. Therefore such colors cannot be used for text. + +// 6. How can this be implemented in code + +// Providing a min luminance contrast ratio between text and background. +// + +// adaptive returns an object of the adjusted colors +// {lightMode/darkMode:Color[]} + +// Possible params +// 1. backgroundColor -> The backgroundColor to compare against +// 2. colors -> The colors to tune +// 3. viewingConditions + +// First I need to declare some constants to serve as starting points +////// Approach was adapted from Adobe's Leonardo tool + +/////// Rough concept of adaptive color + +// Dark theme factors to consider +// 1. if color is darker than background. It means that color is not legible and will be tuned up +// 2. if color is lighter than background. it means its legible + +// Light theme factors to consider +// 1. if a color is lighter than background. This means color is not compliant and will be fixed up. +// 2. if a color is darker than background. It means its legible + +// Constants +// - baseluminance +// - colorluminance + +// Find contrast colors +// find contrast color pairs that are harmonius + +// The relative luminance returned should be compliant to the defined ratio + +// The correlates are channels in short "QJMCshH" + +// First convert cam to xyz then rgb + +const adaptivePalette = (colors: Color[], options?: AdaptivePaletteOptions) => { + // return customMystery; +}; + +// let { light, dark } = undefined || {}; +// let = options +// light = checkArg(light, tailwindColors('gray')('100')); + +// // First get the contrast between the passed in color and the backgrounds +// dark = checkArg(dark, tailwindColors('stone')('800')); +// const lightContrast = getContrast(colors, light); +// const darkContrast = getContrast(colors, dark); +// const colorLuminance = getLuminance(color); +// const lightLuminance = getLuminance(light); +// const darkLuminance = getLuminance(dark); +// colors = colors.map((color) => toJch(color)); diff --git a/src/color-maps/samples/modeRanges.ts b/src/color-maps/samples/modeRanges.ts new file mode 100644 index 00000000..71d38837 --- /dev/null +++ b/src/color-maps/samples/modeRanges.ts @@ -0,0 +1,23 @@ +export default { + cubehelix: { + s: [0, 4.614], + l: [0, 1], + }, + lab: { + l: [0, 100], + }, + dlch: { l: [0, 100], c: [0, 51.484] }, + jab: { + j: [0, 0.222], + }, + jch: { + j: [0, 0.221], + c: [0, 0.19], + }, + lch: { l: [0, 100], c: [0, 150] }, + lch65: { l: [0, 100], c: [0, 133.807] }, + lchuv: { l: [0, 100], c: [0, 176.956] }, + luv: { l: [0, 100] }, + oklab: { l: [0, 1] }, + oklch: { l: [0, 1], c: [0, 0.4] }, +}; diff --git a/src/colors.ts b/src/colors.ts new file mode 100644 index 00000000..eee4ff0c --- /dev/null +++ b/src/colors.ts @@ -0,0 +1,1659 @@ +/* + * @license + * colors.ts - Colors and schemes for huetiful-js. + * Contains colors from TailwindCSS released under the MIT permissive licence. + * Contains parts of chroma.js released under the Apache-2.0 license. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import tailwindHues from './color-maps/swatches/tailwind.ts'; +import type { + SequentialScheme, + DivergingScheme, + QualitativeScheme, + TailwindColorFamilies, + ScaleValues, + ColorDistanceOptions, + ColorToken, + HueColorSpaces, + InterpolatorOptions, + ColorOptions, + EarthtoneOptions, + HueFamily, + HueShiftOptions, + PairedSchemeOptions +} from './types'; + +import * as filterBy from './filterBy'; +import * as sortBy from './sortBy'; +import { + discoverPalettes as nativeDiscoverPalettes, + getFarthestHue as nativeMaxHue, + getNearestHue as nativeMinHue, + getNearestLightness as nativeMaxLightness, + getFarthestLightness as nativeMinLightness, + alpha as nativeAlpha, + brighten as nativeBrighten, + darken as nativeDarken, + isAchromatic as nativeIsAchromatic, + isCool as nativeIsCool, + isWarm as nativeIsWarm, + getFarthestChroma as nativeGetFarthestChroma, + getFarthestHue as nativeGetFarthestHue, + getFarthestLightness as nativeGetFarthestLightness, + getNearestHue as nativeGetNearestHue, + getNearestChroma as nativeGetNearestChroma, + getNearestLightness as nativeGetNearestLightness, + overtone as nativeOvertone, + toHex as nativeToHex, + getChannel as nativeGetChannel, + getContrast, + getLuminance, + setChannel as nativeSetChannel, + setLuminance, + checkArg, + matchChromaChannel, + scheme as nativeScheme, + pastel as nativePastel, + hueShift as nativeHueShift, + getHueFamily as nativeGetHue, + pairedScheme as nativePairedScheme, + earthtone as nativeEarthtone, + getComplimentaryHue as nativeGetComplimentaryHue, + colorDeficiency as nativeColorDeficiency, + interpolator, + interpolateSpline as nativeInterpolatorSpline +} from './index'; + +import { interpolatorConfig } from './helpers'; + +class ColorArray { + constructor(colors: ColorToken[]) { + this['colors'] = colors; + return this; + } + + /** + * + * Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides.If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result. + * @param colorspace The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch. + * @param kind The type of the spline interpolation method. Default is basis. + * @param closed Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false + * @param options Optional channel specific overrides. + * @returns A hexadecimal representation of the resultant color. + */ + interpolateSpline( + colorspace?: HueColorSpaces, + samples?: number, + kind?: 'natural' | 'monotone' | 'basis', + closed?: boolean, + options?: InterpolatorOptions + ): ColorToken[] { + this['colors'] = nativeInterpolatorSpline( + this['colors'], + colorspace, + samples, + kind, + closed, + options + ); + // @ts-ignore + return this; + } + + /** + * + * Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function. + * @param schemeType (Optional) The palette type you want to return. + * @returns An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results. + * @example + * + * import { discoverPalettes } from 'huetiful-js' + +let sample = [ + "#ffff00", + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#720000", + "#600000", + "#4e0000", + "#3e0000", + "#310000", +] + +console.log(load(sample).discoverPalettes(sample, "tetradic").output()) +// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ] + */ + discoverPalettes( + schemeType?: 'analogous' | 'triadic' | 'tetradic' | 'complementary' + ): ColorToken[] | object { + this['colors'] = nativeDiscoverPalettes(this['colors'], schemeType); + return this; + } + + /** + * + * Gets the largest hue value from the passed in colors. + * @param colorSpace The mode color space to perform the computation in. + * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. + * @returns The largest hue value in the colors passed in or a custom object. + * @example + * + * import { getFarthestHue } from 'huetiful-js' +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(load(output).getFarthestHue('lch')) +// 273.54920266436477 + */ + getFarthestHue( + colorSpace?: HueColorSpaces, + colorObj = false + ): number | { factor: number; color: ColorToken } { + return nativeMaxHue(this['colors'], colorSpace, colorObj); + } + + /** + * + * Gets the smallest hue value from the passed in colors. + * @param colors The array of colors to query the color with the smallest hue value. + * @param colorSpace The mode color space to perform the computation in. + * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. + * @returns The smallest hue value in the colors passed in or a custom object. + * @example + * + * import { getNearestHue } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(load(sample).getNearestHue('lch')) +// 12.462831644544274 + */ + getNearestHue( + colorSpace?: HueColorSpaces, + colorObj = false + ): number | { factor: number; color: ColorToken } { + return nativeMinHue(this['colors'], colorSpace, colorObj); + } + + /** + * + * Gets the smallest lightness value from the passed in colors. + * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. + * @returns The smallest lightness value in the colors passed in or a custom object. + * @example + * + * import { minLightness } from 'huetiful-js' + +let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] + +console.log(load(sample).getNearestLightness('lch', true)) + +// { lightness: 72.61647882089876, name: '#a1bd2f' } + + */ + getNearestLightness( + colorspace?: HueColorSpaces, + colorObj = false + ): number | { factor: number; color: ColorToken } { + return nativeMinLightness(this['colors'], colorspace, colorObj); + } + + /** + * + * Gets the largest lightness value from the passed in colors. + * @param colors The array of colors to query the color with the largest lightness value. + * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. + * @returns The largest lightness value in the colors passed in or a custom object. + * @example + * + * import { maxLightness } from 'huetiful-js' + +let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] + +console.log(load(sample).getFarthestLightness('lch', true)) + +// { lightness: 80.94668903360088, name: '#f3bac1' } + + */ + getFarthestLightness( + colorspace?: HueColorSpaces, + colorObj = false + ): number | { factor: number; color: ColorToken } { + return nativeMaxLightness(this['colors'], colorspace, colorObj); + } + + /** + * + * Returns an array of colors in the specified saturation range. The range is normalised to [0,1]. + * @param startSaturation The minimum end of the saturation range. + * @param endSaturation The maximum end of the saturation range. + * @param mode The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do. + * @returns Array of filtered colors. + * @example + * import { filterByContrast } from 'huetiful-js' + +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(filterByContrast(sample, 'green', '>=3')) +// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] + */ + + filterBySaturation( + startSaturation = 0.05, + endSaturation = 1, + mode?: HueColorSpaces + ): ColorArray { + // @ts-ignore + + this['colors'] = filterBy.filterBySaturation( + this['colors'], + startSaturation, + endSaturation, + mode + ); + return this; + } + + /** + * + * Returns an array of colors in the specified lightness range. The range is between 0 and 100. + * @param startLightness The minimum end of the lightness range. + * @param endLightness The maximum end of the lightness range. + * @returns Array of filtered colors. + * @example + * + * import { filterByLightness } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByLightness(sample, 20, 80) + +// [ '#00c000', '#007e00', '#164100', '#720000' ] + */ + filterByLightness(startLightness = 5, endLightness = 100): ColorArray { + this['colors'] = filterBy.filterByLightness( + this['colors'], + startLightness, + endLightness + ); + return this; + } + /** + * + * Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. + * @param startDistance The minimum end of the distance range. + * @param endDistance The maximum end of the distance range. + * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0]. + * @param mode The color space to calculate the distance in . + * @returns Array of filtered colors. + * @example + * import { filterByDistance } from 'huetiful-js' + +let sample = [ + "#ffff00", + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#720000", + "#600000", +] + +console.log(filterByDistance(sample, "yellow", 0.1)) +// [ '#ffff00' ] + */ + + filterByDistance( + against: ColorToken, + startDistance = 0.05, + endDistance?: number + ): ColorArray { + this['colors'] = filterBy.filterByDistance( + this['colors'], + against, + startDistance, + endDistance + ); + return this; + } + + /** + * + * + * Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges. + * @param startContrast The minimum end of the contrast range. + * @param endContrast The maximum end of the contrast range. + * @returns Array of filtered colors. + * + * @example + * + * import { filterByContrast } from 'huetiful-js' + +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(filterByContrast(sample, 'green', '>=3')) +// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] + */ + + filterByContrast( + against: ColorToken, + startContrast = 0.05, + endContrast?: number + ): ColorArray { + this['colors'] = filterBy.filterByContrast( + this['colors'], + against, + startContrast, + endContrast + ); + + return this; + } + /** + * + * Returns colors in the specified hue ranges between 0 to 360. + * @param startHue The minimum end of the hue range. + * @param endHue The maximum end of the hue range. + * @returns Array of the filtered colors. + * @example + * let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByHue(sample, 20, 80) + +// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] + */ + + filterByHue(startHue = 0, endHue = 360): ColorArray { + this['colors'] = filterBy.filterByHue(this['colors'], startHue, endHue); + return this; + } + /** + * + * Returns an array of colors in the specified luminance range. The range is normalised to [0,1]. + * @param startLuminance The minimum end of the luminance range. + * @param endLuminance The maximum end of the luminance range. + * @returns Array of filtered colors. + * @example + * + * import { filterByLuminance } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByLuminance(sample, 0.4, 0.9) + +// [ '#00ffdc', '#00ff78' ] + */ + + filterByLuminance(startLuminance = 0.05, endLuminance = 1): ColorArray { + this['colors'] = filterBy.filterByLuminance( + this['colors'], + startLuminance, + endLuminance + ); + return this; + } + + /** + * + * Sorts colors according to their lightness. + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @returns An array of the sorted color values. + * @example + * import { sortByLightness } from "huetiful-js"; + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +] + +sortByLightness(sample) + +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + + +sortByLightness(sample,'desc') + +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + */ + + sortByLightness(order?: 'asc' | 'desc'): ColorArray { + // @ts-ignore + + this['colors'] = sortBy.sortByLightness(this['colors'], order); + return this; + } + /** + * + * Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist + * @param against The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array. + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0]. + * @param mode The color space to calculate the distance in . The default is the cylindrical variant of the CIELUV colorspace ('lchuv') + * @returns An array of the sorted color values. + * @example + * import { sortByDistance } from 'huetiful-js' + +let sample = ['purple', 'green', 'red', 'brown'] +console.log( + sortByDistance(sample, 'yellow', 'asc', { + mode: 'lch', + }) +) + +// [ 'brown', 'red', 'green', 'purple' ] + +let sample = ['purple', 'green', 'red', 'brown'] +console.log( + sortByDistance(sample, 'yellow', 'asc', { + mode: 'lch', + }) +) + +// [ 'green', 'brown', 'red', 'purple' ] + */ + + sortByDistance( + against: ColorToken, + order?: 'asc' | 'desc', + options?: ColorDistanceOptions + ): ColorArray { + this['colors'] = sortBy.sortByDistance( + this['colors'], + against, + order, + options + ); + + return this; + } + + /** + * + * Sorts colors according to the relative brightness as defined by WCAG definition. + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @returns An array of the sorted color values. + * @example + * import { sortByLuminance } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + + + +let sorted = sortByLuminance(sample) +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + +let sortedDescending = sortByLuminance(sample, "desc"); +console.log(sortedDescending) +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + + */ + + sortByLuminance(order?: 'asc' | 'desc'): ColorArray { + this['colors'] = sortBy.sortByLuminance(this['colors'], order); + return this; + } + /** + * + * Sorts colors according to their saturation. + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @param mode The mode color space to compute the saturation value in. The default is jch . + * @returns An array of the sorted color values. + * @example + * import { sortBySaturation } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + +let sorted = sortBySaturation(sample); +console.log(sorted); + +// [ + '#310000', '#3e0000', + '#164100', '#4e0000', + '#600000', '#720000', + '#00ffdc', '#007e00', + '#00ff78', '#00c000', + '#ffff00' +] + +let sortedDescending = sortBySaturation(sample,'desc'); +console.log(sortedDescending) +// [ + '#ffff00', '#00c000', + '#00ff78', '#007e00', + '#00ffdc', '#720000', + '#600000', '#4e0000', + '#164100', '#3e0000', + '#310000' +] + + */ + + sortBySaturation(order: 'asc' | 'desc', mode?: HueColorSpaces): ColorArray { + this['colors'] = sortBy.sortBySaturation(this['colors'], order, mode); + return this; + } + + /** + * + * Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param) + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @returns An array of the sorted color values. + * @example + * + * import { sortByContrast } from 'huetiful-js' + +let sample = ['purple', 'green', 'red', 'brown'] +console.log(sortByContrast(sample, 'yellow')) +// [ 'red', 'green', 'brown', 'purple' ] + +console.log(sortByContrast(sample, 'yellow', 'desc')) +// [ 'purple', 'brown', 'green', 'red' ] + + */ + + sortByContrast(against: ColorToken, order?: 'asc' | 'desc'): ColorArray { + this['colors'] = sortBy.sortByContrast(this['colors'], against, order); + return this; + } + /** + * + * Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') +* @param colorspace The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results. +* @returns An array of the sorted color values. + * @example + * let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + + +let sorted = sortByHue(sample); +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#ffff00', + '#164100', '#00c000', + '#007e00', '#00ff78', + '#00ffdc' +] + +let sortedDescending = sortByHue(sample,'desc'); +console.log(sortedDescending) +// [ + '#00ffdc', '#00ff78', + '#007e00', '#00c000', + '#164100', '#ffff00', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + */ + + // Todo: Add the mode param so that users can select mode to work with. The default is lch + sortByHue(order: 'asc' | 'desc', colorspace: HueColorSpaces): ColorArray { + this['colors'] = sortBy.sortByHue(this['colors'], order, colorspace); + return this; + } + + /** + * @method + * @returns Returns the result value from the chain. + */ + output(): ColorToken { + return this['colors']; + } +} + +/** + * @class + * A class that takes an array of colors and exposes all the utilities that handle collections of colors as methods. The methods can be chained as long as `this` being returned can be iterated on. Works like Array object. + * @param colors An array of colors to chain the array methods on. Every element in the array will be parsed as a color token. + */ +function load(colors: ColorToken[]): ColorArray { + return new ColorArray(colors); +} + +//Check if the scheme object has the passed in scheme +function schemeMapper(scheme: string, schemesObject: object): ColorToken[] { + const cb = (str: string) => str.toLowerCase(); + const { keys } = Object; + // Map all schemes keys to lower case + const schemeOptions = keys(schemesObject).map(cb); + + scheme = cb(scheme); + // Check if passed in scheme is available + if (schemeOptions.indexOf(scheme) > -1) { + return schemesObject[scheme]; + } else { + // Else throw error:Invalid scheme + throw Error(`${scheme} is an invalid scheme option.`); + } +} + +/** + * + * A wrapper function for ColorBrewer's map of sequential color schemes. + * @param scheme The name of the scheme + * @returns An array of colors in hex represantation. + * @example + * import { sequential } from 'huetiful-js + + +console.log(sequential("OrRd")) + +// [ + '#fff7ec', '#fee8c8', + '#fdd49e', '#fdbb84', + '#fc8d59', '#ef6548', + '#d7301f', '#b30000', + '#7f0000' +] + + + + */ +function sequential(scheme: SequentialScheme): ColorToken[] { + const schemes = { + OrRd: [ + '#fff7ec', + '#fee8c8', + '#fdd49e', + '#fdbb84', + '#fc8d59', + '#ef6548', + '#d7301f', + '#b30000', + '#7f0000' + ], + PuBu: [ + '#fff7fb', + '#ece7f2', + '#d0d1e6', + '#a6bddb', + '#74a9cf', + '#3690c0', + '#0570b0', + '#045a8d', + '#023858' + ], + BuPu: [ + '#f7fcfd', + '#e0ecf4', + '#bfd3e6', + '#9ebcda', + '#8c96c6', + '#8c6bb1', + '#88419d', + '#810f7c', + '#4d004b' + ], + Oranges: [ + '#fff5eb', + '#fee6ce', + '#fdd0a2', + '#fdae6b', + '#fd8d3c', + '#f16913', + '#d94801', + '#a63603', + '#7f2704' + ], + BuGn: [ + '#f7fcfd', + '#e5f5f9', + '#ccece6', + '#99d8c9', + '#66c2a4', + '#41ae76', + '#238b45', + '#006d2c', + '#00441b' + ], + YlOrBr: [ + '#ffffe5', + '#fff7bc', + '#fee391', + '#fec44f', + '#fe9929', + '#ec7014', + '#cc4c02', + '#993404', + '#662506' + ], + YlGn: [ + '#ffffe5', + '#f7fcb9', + '#d9f0a3', + '#addd8e', + '#78c679', + '#41ab5d', + '#238443', + '#006837', + '#004529' + ], + Reds: [ + '#fff5f0', + '#fee0d2', + '#fcbba1', + '#fc9272', + '#fb6a4a', + '#ef3b2c', + '#cb181d', + '#a50f15', + '#67000d' + ], + RdPu: [ + '#fff7f3', + '#fde0dd', + '#fcc5c0', + '#fa9fb5', + '#f768a1', + '#dd3497', + '#ae017e', + '#7a0177', + '#49006a' + ], + Greens: [ + '#f7fcf5', + '#e5f5e0', + '#c7e9c0', + '#a1d99b', + '#74c476', + '#41ab5d', + '#238b45', + '#006d2c', + '#00441b' + ], + YlGnBu: [ + '#ffffd9', + '#edf8b1', + '#c7e9b4', + '#7fcdbb', + '#41b6c4', + '#1d91c0', + '#225ea8', + '#253494', + '#081d58' + ], + Purples: [ + '#fcfbfd', + '#efedf5', + '#dadaeb', + '#bcbddc', + '#9e9ac8', + '#807dba', + '#6a51a3', + '#54278f', + '#3f007d' + ], + GnBu: [ + '#f7fcf0', + '#e0f3db', + '#ccebc5', + '#a8ddb5', + '#7bccc4', + '#4eb3d3', + '#2b8cbe', + '#0868ac', + '#084081' + ], + Greys: [ + '#ffffff', + '#f0f0f0', + '#d9d9d9', + '#bdbdbd', + '#969696', + '#737373', + '#525252', + '#252525', + '#000000' + ], + YlOrRd: [ + '#ffffcc', + '#ffeda0', + '#fed976', + '#feb24c', + '#fd8d3c', + '#fc4e2a', + '#e31a1c', + '#bd0026', + '#800026' + ], + PuRd: [ + '#f7f4f9', + '#e7e1ef', + '#d4b9da', + '#c994c7', + '#df65b0', + '#e7298a', + '#ce1256', + '#980043', + '#67001f' + ], + Blues: [ + '#f7fbff', + '#deebf7', + '#c6dbef', + '#9ecae1', + '#6baed6', + '#4292c6', + '#2171b5', + '#08519c', + '#08306b' + ], + PuBuGn: [ + '#fff7fb', + '#ece2f0', + '#d0d1e6', + '#a6bddb', + '#67a9cf', + '#3690c0', + '#02818a', + '#016c59', + '#014636' + ], + Viridis: [ + '#440154', + '#482777', + '#3f4a8a', + '#31678e', + '#26838f', + '#1f9d8a', + '#6cce5a', + '#b6de2b', + '#fee825' + ] + }; + + return schemeMapper(scheme, schemes); +} + +/** + * + * A wrapper function for ColorBrewer's map of diverging color schemes. + * @param scheme The name of the scheme. + * @returns An array of colors in hex represantation. + * @example + * + * import { diverging } from 'huetiful-js' + + + +console.log(diverging("Spectral")) +//[ + '#7fc97f', '#beaed4', + '#fdc086', '#ffff99', + '#386cb0', '#f0027f', + '#bf5b17', '#666666' +] + */ + +function diverging(scheme: DivergingScheme): ColorToken[] { + const schemes = { + Spectral: [ + '#9e0142', + '#d53e4f', + '#f46d43', + '#fdae61', + '#fee08b', + '#ffffbf', + '#e6f598', + '#abdda4', + '#66c2a5', + '#3288bd', + '#5e4fa2' + ], + RdYlGn: [ + '#a50026', + '#d73027', + '#f46d43', + '#fdae61', + '#fee08b', + '#ffffbf', + '#d9ef8b', + '#a6d96a', + '#66bd63', + '#1a9850', + '#006837' + ], + RdBu: [ + '#67001f', + '#b2182b', + '#d6604d', + '#f4a582', + '#fddbc7', + '#f7f7f7', + '#d1e5f0', + '#92c5de', + '#4393c3', + '#2166ac', + '#053061' + ], + PiYG: [ + '#8e0152', + '#c51b7d', + '#de77ae', + '#f1b6da', + '#fde0ef', + '#f7f7f7', + '#e6f5d0', + '#b8e186', + '#7fbc41', + '#4d9221', + '#276419' + ], + PRGn: [ + '#40004b', + '#762a83', + '#9970ab', + '#c2a5cf', + '#e7d4e8', + '#f7f7f7', + '#d9f0d3', + '#a6dba0', + '#5aae61', + '#1b7837', + '#00441b' + ], + RdYlBu: [ + '#a50026', + '#d73027', + '#f46d43', + '#fdae61', + '#fee090', + '#ffffbf', + '#e0f3f8', + '#abd9e9', + '#74add1', + '#4575b4', + '#313695' + ], + BrBG: [ + '#543005', + '#8c510a', + '#bf812d', + '#dfc27d', + '#f6e8c3', + '#f5f5f5', + '#c7eae5', + '#80cdc1', + '#35978f', + '#01665e', + '#003c30' + ], + RdGy: [ + '#67001f', + '#b2182b', + '#d6604d', + '#f4a582', + '#fddbc7', + '#ffffff', + '#e0e0e0', + '#bababa', + '#878787', + '#4d4d4d', + '#1a1a1a' + ], + PuOr: [ + '#7f3b08', + '#b35806', + '#e08214', + '#fdb863', + '#fee0b6', + '#f7f7f7', + '#d8daeb', + '#b2abd2', + '#8073ac', + '#542788', + '#2d004b' + ] + }; + + return schemeMapper(scheme, schemes); +} + +/** + * + * A wrapper function for ColorBrewer's map of qualitative color schemes. + * @param scheme The name of the scheme + * @returns An array of colors in hex represantation. + * @example + * + * import { qualitative } from 'huetiful-js' + + +console.log(qualitative("Accent")) +// [ + '#7fc97f', '#beaed4', + '#fdc086', '#ffff99', + '#386cb0', '#f0027f', + '#bf5b17', '#666666' +] + + */ + +function qualitative(scheme: QualitativeScheme): ColorToken[] { + const schemes = { + Set2: [ + '#66c2a5', + '#fc8d62', + '#8da0cb', + '#e78ac3', + '#a6d854', + '#ffd92f', + '#e5c494', + '#b3b3b3' + ], + Accent: [ + '#7fc97f', + '#beaed4', + '#fdc086', + '#ffff99', + '#386cb0', + '#f0027f', + '#bf5b17', + '#666666' + ], + Set1: [ + '#e41a1c', + '#377eb8', + '#4daf4a', + '#984ea3', + '#ff7f00', + '#ffff33', + '#a65628', + '#f781bf', + '#999999' + ], + Set3: [ + '#8dd3c7', + '#ffffb3', + '#bebada', + '#fb8072', + '#80b1d3', + '#fdb462', + '#b3de69', + '#fccde5', + '#d9d9d9', + '#bc80bd', + '#ccebc5', + '#ffed6f' + ], + Dark2: [ + '#1b9e77', + '#d95f02', + '#7570b3', + '#e7298a', + '#66a61e', + '#e6ab02', + '#a6761d', + '#666666' + ], + Paired: [ + '#a6cee3', + '#1f78b4', + '#b2df8a', + '#33a02c', + '#fb9a99', + '#e31a1c', + '#fdbf6f', + '#ff7f00', + '#cab2d6', + '#6a3d9a', + '#ffff99', + '#b15928' + ], + Pastel2: [ + '#b3e2cd', + '#fdcdac', + '#cbd5e8', + '#f4cae4', + '#e6f5c9', + '#fff2ae', + '#f1e2cc', + '#cccccc' + ], + Pastel1: [ + '#fbb4ae', + '#b3cde3', + '#ccebc5', + '#decbe4', + '#fed9a6', + '#ffffcc', + '#e5d8bd', + '#fddaec', + '#f2f2f2' + ] + }; + return schemeMapper(scheme, schemes); +} + +/** + * + * A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade. + * @param shade Any shade in the default TailwindCSS palette e.g amber,blue. + * @param value Any value from 100 to 900 in increments of 100 e.g "200". + * @returns color Returns a hex code string or array of hex codes depending on how the function is called. + * @example + * + * import { colors } from "huetiful-js"; + +let all300 = colors("all", 300); + +console.log(all300) +//[ + '#cbd5e1', '#d1d5db', '#d4d4d8', + '#d4d4d4', '#d6d3d1', '#fca5a5', + '#fdba74', '#fcd34d', '#fde047', + '#bef264', '#86efac', '#6ee7b7', + '#5eead4', '#7dd3fc', '#93c5fd', + '#c4b5fd', '#d8b4fe', '#f0abfc', + '#f9a8d4', '#fda4af' +] + +let red = colors("red"); +console.log(red); + +// [ + '#fef2f2', '#fee2e2', + '#fecaca', '#fca5a5', + '#f87171', '#ef4444', + '#dc2626', '#b91c1c', + '#991b1b', '#7f1d1d' +] + +let red100 = colors("red", 100); + +console.log(red100) +// #fee2e2 + */ +function colors( + shade: TailwindColorFamilies | 'all', + value?: ScaleValues +): ColorToken | ColorToken[] { + const { keys } = Object; + const defaultHue = 'all'; + const hueKeys = keys(tailwindHues); + + // @ts-ignore + shade = shade.toLowerCase(); + // First do an AND check on hue and val params. If true return the hue at the specified value. + // If only the hue is defined return the whole array of hex codes for that color. + // If only the value is defined return that color value for every hue. + // @ts-ignore + if (shade === defaultHue) { + return hueKeys.map((color) => tailwindHues[color][value || '500']); + } else if (hueKeys.some((hue) => hue === shade) && value) { + return tailwindHues[shade][value]; + } else if (shade && typeof value == 'undefined') { + const keyVals = keys(tailwindHues[shade]); + return keyVals.map((key) => tailwindHues[shade][key]); + } else if (typeof shade && typeof value == 'undefined') { + throw Error(`Both shade and value cannot be undefined`); + } +} + +/** + * + * Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale, + * @param val The tone value of the shade. Values are in incrementals of 100. Both numeric (100) and its string equivalent ('100') are valid. + * @returns color A hex string value or array of hex strings. + * @example + * + * import { tailwindColors } from "huetiful-js"; + +// We pass in red as the target hue. +// It returns a function that can be called with an optional value parameter +let red = tailwindColors("red"); +console.log(red()); +// [ + '#fef2f2', '#fee2e2', + '#fecaca', '#fca5a5', + '#f87171', '#ef4444', + '#dc2626', '#b91c1c', + '#991b1b', '#7f1d1d' +] + + +console.log(red(100)); +// '#fee2e2' + +console.log(red('900')); +// '#7f1d1d' + + + * + */ +function tailwindColors(shade: keyof TailwindColorFamilies) { + return (val?: ScaleValues): string | string[] => { + // This is a curried func that takes in the shade and returns a function that takes in a value from 100 thru 900 + // @ts-ignore + shade = shade.toLowerCase(); + const { keys } = Object; + // We check if the shade is a valid Tailwind shade if not we return pure black. + let targetHue: object; + + if (keys(tailwindHues).indexOf(shade as string) != -1) { + targetHue = tailwindHues[shade]; + } else { + throw Error( + `${ + shade as string + } is not a valid shade in the default Tailwind palette` + ); + } + + if (typeof val === 'undefined') { + return keys(targetHue).map((value) => targetHue[value]); + } else if (keys(targetHue).indexOf(val) > -1) { + return targetHue[val]; + } else { + throw Error( + `${val} is not a valid scale value. Values are in increments of 100 up to 900 e.g "200"` + ); + } + }; +} + +class Color { + constructor(c: ColorToken, options?: ColorOptions) { + let { + alpha, + colorspace, + luminance, + saturation, + lightMode, + darkMode, + lightness + } = checkArg(options, {}) as ColorOptions; + c = checkArg(c, '#000') as ColorToken; + + // Set the alpha of the color if its not explicitly passed in. + //@ts-ignore + this['alpha'] = checkArg(alpha, nativeAlpha(c)); + + // if the color is undefined we cast pure black + + this['_color'] = c; + + // set the color's luminance if its not explicitly passed in + this['_luminance'] = checkArg(luminance, getLuminance(c)); + + // set the color's lightness if its not explicitly passed in the default lightness is in Lch but will be refactored soon + this['lightness'] = checkArg(lightness, nativeGetChannel('lch.l')(c)); + + // set the default color space as jch if a color space is not specified. TODO: get the mode from object and array + this['colorspace'] = checkArg(colorspace, 'jch'); + + // set the default saturation to that of the passed in color if the value is not explicitly set + this['_saturation'] = checkArg( + saturation, + nativeGetChannel( + `${this['colorspace']}.${matchChromaChannel(this['colorspace'])}` + )(c) + ); + + // light mode default is gray-100 + this['lightMode'] = checkArg(lightMode, colors('gray', '100')); + + // dark mode default is gray-800 + this['darkMode'] = checkArg(darkMode, colors('gray', '800')); + } + + alpha(amount?: number | string): Color | number { + if (amount) { + this['_color'] = nativeAlpha(this['_color'], amount); + return this; + } else { + return nativeAlpha(this['_color']); + } + } + getChannel(channel: string) { + return nativeGetChannel(`${this['colorspace']}.${channel.toLowerCase()}`)( + this['_color'] + ); + } + setChannel(modeChannel: string, value: number | string): Color { + this['_color'] = nativeSetChannel(modeChannel)(this['_color'], value); + return this; + } + + via(origin: ColorToken, t?: number, options?: typeof interpolatorConfig) { + const result = interpolator( + [origin, this['_color']], + this['colorspace'], + options + ); + + return nativeToHex(result(t)); + } + + brighten(amount: number | string, colorspace) { + this['_color'] = nativeBrighten(this['_color'], amount, colorspace); + return this; + } + darken(amount: number | string) { + this['_color'] = nativeDarken(this['_color'], amount); + return this; + } + + toHex(): Color { + this['_color'] = nativeToHex(this['_color']); + return this['_color']; + } + pastel(): Color { + this['_color'] = nativePastel(this['_color']); + return this; + } + pairedScheme(options?: PairedSchemeOptions): ColorArray { + // @ts-ignore + this['colors'] = nativePairedScheme(this['_color'], options); + + return new ColorArray(this['colors']); + } + hueShift(options?: HueShiftOptions): ColorArray { + // @ts-ignore + this['colors'] = nativeHueShift(this['_color'], options); + + return new ColorArray(this['colors']); + } + getComplimentaryHue( + mode?: HueColorSpaces, + colorObj?: boolean + ): { hue: HueFamily; color: ColorToken } | ColorToken { + this['_color'] = nativeGetComplimentaryHue(this['_color'], mode, colorObj); + return this['_color']; + } + earthtone(options?: EarthtoneOptions): ColorArray | ColorToken { + // @ts-ignore + this['colors'] = nativeEarthtone(this['_color'], checkArg(options, {})); + + return this['colors']; + } + contrast(against: 'lightMode' | 'darkMode' | Color) { + let result: number; + switch (against) { + case 'lightMode': + result = getContrast(this['_color'], this['background']['lightMode']); + + break; + case 'darkMode': + result = getContrast(this['_color'], this['background']['darkMode']); + break; + default: + result = getContrast(this['_color'], this['background']['custom']); + break; + } + return result; + } + luminance(amount?: number): number { + if (amount) { + this['_luminance'] = amount; + this['_color'] = setLuminance(this['_color'], this['_color']); + // @ts-ignore + return this; + } + return getLuminance(this['_color']); + } + + output() { + return this['_color']; + } + + saturation(amount?: string | number) { + this['_saturation'] = nativeGetChannel( + `${this['colorspace']}.${matchChromaChannel(this['colorspace'])}` + )(this['_color']); + if (amount) { + this['_color'] = nativeSetChannel( + `${this['colorspace']}.${matchChromaChannel(this['colorspace'])}` + )(this['_color'], amount); + + return this; + } else { + return this['_saturation']; + } + } + isAchromatic() { + return nativeIsAchromatic(this['_color']); + } + isWarm() { + return nativeIsWarm(this['_color']); + } + isCool() { + return nativeIsCool(this['_color']); + } + + /** + * + * Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value. + * @param deficiencyType The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value. + * @see For a deep dive on color vision deficiency go to + * @param color The color to return its deficiency simulated variant. + * @param severity The intensity of the filter. The exepected value is between [0,1]. For example 0.5 + * @returns The color as its simulated variant as a hexadecimal string. + * @example + * + * import { colorDeficiency, toHex } from 'huetiful-js' + +// Here we are simulating color blindness of tritanomaly or we can't see 'blue'. +// We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1 +let tritanomaly = colorDeficiency('blue') +console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1)) +// #dd663680 + +// Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1 +let protanopia = colorDeficiency('red') +console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' })) +// #9f9f9f + */ + deficiency( + deficiencyType?: 'red' | 'blue' | 'green' | 'monochromacy', + severity = 1 + ): ColorToken { + this['_color'] = nativeColorDeficiency(deficiencyType)( + this['_color'], + severity + ); + return this; + } + + getFarthestHue(colors: ColorToken[], colorObj?: boolean) { + return nativeGetFarthestHue(colors, this['colorspace'], colorObj); + } + getNearestHue(colors: ColorToken[], colorObj?: boolean) { + return nativeGetNearestHue(colors, this['colorspace'], colorObj); + } + getNearestChroma(colors: ColorToken[]) { + return nativeGetNearestChroma(colors, this['colorspace']); + } + getNearestLightness(colors: ColorToken[], colorObj?: boolean) { + return nativeGetNearestLightness(colors, this['colorspace'], colorObj); + } + getFarthestChroma(colors: ColorToken[], colorObj?: boolean) { + return nativeGetFarthestChroma(colors, colorObj); + } + getFarthestLightness(colors: ColorToken[], colorObj?: boolean) { + return nativeGetFarthestLightness(colors, this['colorspace'], colorObj); + } + ovetone() { + return nativeOvertone(this['_color']); + } + getHue() { + return nativeGetHue(this['_color']); + } + scheme( + scheme: 'analogous' | 'triadic' | 'tetradic' | 'complementary', + easingFunc?: (t: number) => number + ): ColorToken[] | ColorArray { + return load(nativeScheme(scheme)(this['_color'], easingFunc)); + } +} + +/** + * Wrapper function over the Color class that returns a new Color method chain. + * @param color The color token to bind. + * @returns A new Color class with all the utilities that take a single color as the first parameter bound to its prototype. + */ +function color(color: ColorToken) { + return new Color(color); +} + +export { + diverging, + qualitative, + sequential, + colors, + tailwindColors, + ColorArray, + load, + Color, + color +}; diff --git a/src/colors/achromatic.ts b/src/colors/achromatic.ts deleted file mode 100644 index 32061959..00000000 --- a/src/colors/achromatic.ts +++ /dev/null @@ -1,70 +0,0 @@ -// @ts-nocheck -import type { Color } from '../paramTypes.ts'; -import { getChannel } from '../getters_and_setters/get.ts'; - -/** - * @function - * @description Checks if a color is achromatic(without hue or simply grayscale). - * @param color The color to test if it is achromatic or not. - * @returns boolean Returns true if the color is achromatic else false - * @example - * - * import { isAchromatic } from "huetiful-js"; -import { formatHex8, interpolate, samples } from "culori" - - -isAchromatic('pink') -// false - -let sample = [ - "#164100", - "#ffff00", - "#310000", - 'pink' -]; - -console.log(map(sample, isAchromatic)); - -// [false, false, false,false] - -isAchromatic('gray') -// Returns true - - -console.log(map(sample, isAchromatic)); - - -// we create an interpolation using black and white -let f = interpolate(["black", "white"]); - -//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function. -// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.) -let grays = map(samples(12), (c) => formatHex8(f(c))); -console.log(map(grays, isAchromatic)); - -// - [ false, true, true, - true, true, true, - true, true, true, - true, true, false -] - - */ -const isAchromatic = (color: Color): boolean => { - const cb = (mc: string) => getChannel(mc)(color); - - // Store the value of chroma and saturation channels. - const checkHsl = cb('hsl.s'); - const checkLch = cb('lch.c'); - - // 2) Check if the saturation channel is zero or falsy for color spaces with saturation/chroma channel - // OR - // 2)Check if the color's numerical represantation is within the defined ranges. - if ((checkHsl || checkLch) === (undefined || null || 0)) { - return true; - } else { - return false; - } -}; - -export { isAchromatic }; diff --git a/src/colors/chroma.ts b/src/colors/chroma.ts deleted file mode 100644 index 314650d3..00000000 --- a/src/colors/chroma.ts +++ /dev/null @@ -1,193 +0,0 @@ -// @ts-nocheck -// This module contains minChroma,maxChroma,getFarthestChroma,getNearestChroma - -import { Color, HueColorSpaces, Factor } from '../paramTypes'; -import { getChannel } from '../getters_and_setters/get.ts'; -import { matchChromaChannel } from '../fp/string/matchChromaChannel.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; - -// I must test if the passed in mode has a chroma/saturation channel. Should I use RegExp ? - -// The callback to invoke per color in the passed in collection. -// The subtrahend is each color in the collection -//This means that the color object with the smallest chroma value is the nearest chroma. -// First check which value is greater and then act accordingly. Refactor hue.ts so that it returns negative -const chromaDiff = - (color: Color, colorSpace: HueColorSpaces | string) => - (subtrahend: Color) => { - const cs = matchChromaChannel(colorSpace); - - if (getChannel(cs)(color) < getChannel(cs)(subtrahend)) { - return getChannel(cs)(subtrahend) - getChannel(cs)(color); - } else { - return getChannel(cs)(color) - getChannel(cs)(subtrahend); - } - }; - -// If the predicate returns undefined or false on the chroma channel then it means that it is an achromatic color. -// Callback func for the minHue and maxHue utils. The funny thing is that most of the code is similar with minor changes here and there -const predicate = (colorSpace: HueColorSpaces) => (color: Color) => - getChannel(matchChromaChannel(colorSpace))(color) || undefined; - -/** - * - * @function - * @description Returns the smallest chroma/saturation difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic with Array.map to remove grays from your color collection. - * @param color The color to use its chroma/saturation value as the minuend. - * @param colors The collection of colors to compare against. - * @param colorSpace The mode color space to perform the computation in. - * @returns The chroma/saturation value from the color with the smallest chroma distance. If the colors are achromatic, it returns undefined. - * @example - * - * import { getFarthestChroma } from 'huetiful-js' - -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(getFarthestChroma('lime', sample, 'lch')) -// 90.87480913244802 - */ -const getNearestChroma = ( - color: Color, - colors: Color[], - colorSpace?: HueColorSpaces -): number => { - // The factor being investigated. - - const factor: Factor = 'saturation'; - const cb = chromaDiff(color, colorSpace || 'lch'); - const sortedObjArr = sortedArr( - factor, - cb, - 'asc', - true - )(colors).filter((el) => el[factor] !== undefined); - - return sortedObjArr[0][factor]; -}; - -/** - * - * @function - * @description Returns the largest chroma/saturation difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic with Array.map to remove grays from your color collection. - * @param color The color to use its chroma/saturation value as the minuend. - * @param colors The collection of colors to compare against. - * @param colorSpace The mode color space to perform the computation in. - * @returns The chroma/saturation value from the color with the largest saturation distance. If the colors are achromatic, it returns undefined. - * @example - * - * import { getFarthestChroma } from 'huetiful-js' - -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(getFarthestChroma('lime', sample, 'lch')) -// 90.87480913244802 - */ - -const getFarthestChroma = ( - color: Color, - colors: Color[], - colorSpace?: HueColorSpaces -): number => { - // The factor being investigated. - - const factor: Factor = 'saturation'; - const cb = chromaDiff(color, colorSpace || 'lch'); - const sortedObjArr = sortedArr( - factor, - cb, - 'desc', - true - )(colors).filter((el) => el[factor] !== undefined); - - return sortedObjArr[0][factor]; -}; - -/** - *@function - * @description Gets the smallest chroma/saturation value from the passed in colors. - * @param colors The array of colors to query the color with the smallest chroma/saturation value. - * @param colorSpace The mode color space to perform the computation in. - * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. - * @returns The smallest chroma/saturation value in the colors passed in or a custom object. - * @example - * - * import { minChroma } from 'huetiful-js' - -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(minChroma(sample, 'lch')) -// 22.45669293295522 - */ -const minChroma = ( - colors: Color[], - colorSpace?: HueColorSpaces, - colorObj = false -): number | { factor: number; color: Color } => { - // The factor being investigated. - - const factor: Factor = 'saturation'; - const result: Array<{ factor: number; name: Color }> = sortedArr( - factor, - predicate(colorSpace || 'lch'), - 'asc', - true - )(colors).filter((el) => el[factor] !== undefined); - - let value: number | { factor: number; name: Color }; - - if (result.length > 0) { - if (colorObj) { - value = result[0]; - } else { - value = result[0][factor]; - } - } - - return value; -}; - -/** - *@function - * @description Gets the largest saturation value from the passed in colors. - * @param colors The array of colors to query the color with the largest saturation value. - * @param colorSpace The mode color space to perform the computation in. - * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. - * @returns The largest saturation value in the colors passed in or a custom object. - * @example - * - * import { maxChroma } from 'huetiful-js' - -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(maxChroma(sample, 'lch')) -// 67.22120855010492 - */ -const maxChroma = ( - colors: Color[], - colorSpace?: HueColorSpaces, - colorObj = false -): number | { factor: number; color: Color } => { - // The factor being investigated. - - const factor: Factor = 'saturation'; - const result: Array<{ factor: number; name: Color }> = sortedArr( - factor, - predicate(colorSpace || 'lch'), - 'desc', - true - )(colors).filter((el) => el[factor] !== undefined); - - let value: { factor: number; name: Color } | number; - - if (result.length > 0) { - if (colorObj) { - value = result[0]; - } else { - value = result[0][factor]; - } - } - - return value; -}; - -export { getFarthestChroma, getNearestChroma, maxChroma, minChroma }; diff --git a/src/colors/colorBrewer.ts b/src/colors/colorBrewer.ts deleted file mode 100644 index a8ec8189..00000000 --- a/src/colors/colorBrewer.ts +++ /dev/null @@ -1,526 +0,0 @@ -//@ts-nocheck -import type { - SequentialScheme, - DivergingScheme, - QualitativeScheme, - Color -} from '../paramTypes'; - -// Convert all the keys to lowercase -const cb = (str: string) => str.toLowerCase(); - -//Check if the scheme object has the passed in scheme -const schemeMapper = (scheme: string, schemesObject: object): Color[] => { - const { keys } = Object; - // Map all schemes keys to lower case - const schemeOptions = keys(schemesObject).map(cb); - - scheme = cb(scheme); - // Check if passed in scheme is available - if (schemeOptions.indexOf(scheme) > -1) { - return schemesObject[scheme]; - } else { - // Else throw error:Invalid scheme - throw Error(`${scheme} is an invalid scheme option.`); - } -}; - -/** - * @function - * @description A wrapper function for ColorBrewer's map of sequential color schemes. - * @param scheme The name of the scheme - * @returns An array of colors in hex represantation. - * @example - * import { sequential } from 'huetiful-js - - -console.log(sequential("OrRd")) - -// [ - '#fff7ec', '#fee8c8', - '#fdd49e', '#fdbb84', - '#fc8d59', '#ef6548', - '#d7301f', '#b30000', - '#7f0000' -] - - - - */ -const sequential = (scheme: SequentialScheme): Color[] => { - const schemes = { - OrRd: [ - '#fff7ec', - '#fee8c8', - '#fdd49e', - '#fdbb84', - '#fc8d59', - '#ef6548', - '#d7301f', - '#b30000', - '#7f0000' - ], - PuBu: [ - '#fff7fb', - '#ece7f2', - '#d0d1e6', - '#a6bddb', - '#74a9cf', - '#3690c0', - '#0570b0', - '#045a8d', - '#023858' - ], - BuPu: [ - '#f7fcfd', - '#e0ecf4', - '#bfd3e6', - '#9ebcda', - '#8c96c6', - '#8c6bb1', - '#88419d', - '#810f7c', - '#4d004b' - ], - Oranges: [ - '#fff5eb', - '#fee6ce', - '#fdd0a2', - '#fdae6b', - '#fd8d3c', - '#f16913', - '#d94801', - '#a63603', - '#7f2704' - ], - BuGn: [ - '#f7fcfd', - '#e5f5f9', - '#ccece6', - '#99d8c9', - '#66c2a4', - '#41ae76', - '#238b45', - '#006d2c', - '#00441b' - ], - YlOrBr: [ - '#ffffe5', - '#fff7bc', - '#fee391', - '#fec44f', - '#fe9929', - '#ec7014', - '#cc4c02', - '#993404', - '#662506' - ], - YlGn: [ - '#ffffe5', - '#f7fcb9', - '#d9f0a3', - '#addd8e', - '#78c679', - '#41ab5d', - '#238443', - '#006837', - '#004529' - ], - Reds: [ - '#fff5f0', - '#fee0d2', - '#fcbba1', - '#fc9272', - '#fb6a4a', - '#ef3b2c', - '#cb181d', - '#a50f15', - '#67000d' - ], - RdPu: [ - '#fff7f3', - '#fde0dd', - '#fcc5c0', - '#fa9fb5', - '#f768a1', - '#dd3497', - '#ae017e', - '#7a0177', - '#49006a' - ], - Greens: [ - '#f7fcf5', - '#e5f5e0', - '#c7e9c0', - '#a1d99b', - '#74c476', - '#41ab5d', - '#238b45', - '#006d2c', - '#00441b' - ], - YlGnBu: [ - '#ffffd9', - '#edf8b1', - '#c7e9b4', - '#7fcdbb', - '#41b6c4', - '#1d91c0', - '#225ea8', - '#253494', - '#081d58' - ], - Purples: [ - '#fcfbfd', - '#efedf5', - '#dadaeb', - '#bcbddc', - '#9e9ac8', - '#807dba', - '#6a51a3', - '#54278f', - '#3f007d' - ], - GnBu: [ - '#f7fcf0', - '#e0f3db', - '#ccebc5', - '#a8ddb5', - '#7bccc4', - '#4eb3d3', - '#2b8cbe', - '#0868ac', - '#084081' - ], - Greys: [ - '#ffffff', - '#f0f0f0', - '#d9d9d9', - '#bdbdbd', - '#969696', - '#737373', - '#525252', - '#252525', - '#000000' - ], - YlOrRd: [ - '#ffffcc', - '#ffeda0', - '#fed976', - '#feb24c', - '#fd8d3c', - '#fc4e2a', - '#e31a1c', - '#bd0026', - '#800026' - ], - PuRd: [ - '#f7f4f9', - '#e7e1ef', - '#d4b9da', - '#c994c7', - '#df65b0', - '#e7298a', - '#ce1256', - '#980043', - '#67001f' - ], - Blues: [ - '#f7fbff', - '#deebf7', - '#c6dbef', - '#9ecae1', - '#6baed6', - '#4292c6', - '#2171b5', - '#08519c', - '#08306b' - ], - PuBuGn: [ - '#fff7fb', - '#ece2f0', - '#d0d1e6', - '#a6bddb', - '#67a9cf', - '#3690c0', - '#02818a', - '#016c59', - '#014636' - ], - Viridis: [ - '#440154', - '#482777', - '#3f4a8a', - '#31678e', - '#26838f', - '#1f9d8a', - '#6cce5a', - '#b6de2b', - '#fee825' - ] - }; - - return schemeMapper(scheme, schemes); -}; - -/** - * @function - * @description A wrapper function for ColorBrewer's map of diverging color schemes. - * @param scheme The name of the scheme. - * @returns An array of colors in hex represantation. - * @example - * - * import { diverging } from 'huetiful-js' - - - -console.log(diverging("Spectral")) -//[ - '#7fc97f', '#beaed4', - '#fdc086', '#ffff99', - '#386cb0', '#f0027f', - '#bf5b17', '#666666' -] - */ - -const diverging = (scheme: DivergingScheme): Color[] => { - const schemes = { - Spectral: [ - '#9e0142', - '#d53e4f', - '#f46d43', - '#fdae61', - '#fee08b', - '#ffffbf', - '#e6f598', - '#abdda4', - '#66c2a5', - '#3288bd', - '#5e4fa2' - ], - RdYlGn: [ - '#a50026', - '#d73027', - '#f46d43', - '#fdae61', - '#fee08b', - '#ffffbf', - '#d9ef8b', - '#a6d96a', - '#66bd63', - '#1a9850', - '#006837' - ], - RdBu: [ - '#67001f', - '#b2182b', - '#d6604d', - '#f4a582', - '#fddbc7', - '#f7f7f7', - '#d1e5f0', - '#92c5de', - '#4393c3', - '#2166ac', - '#053061' - ], - PiYG: [ - '#8e0152', - '#c51b7d', - '#de77ae', - '#f1b6da', - '#fde0ef', - '#f7f7f7', - '#e6f5d0', - '#b8e186', - '#7fbc41', - '#4d9221', - '#276419' - ], - PRGn: [ - '#40004b', - '#762a83', - '#9970ab', - '#c2a5cf', - '#e7d4e8', - '#f7f7f7', - '#d9f0d3', - '#a6dba0', - '#5aae61', - '#1b7837', - '#00441b' - ], - RdYlBu: [ - '#a50026', - '#d73027', - '#f46d43', - '#fdae61', - '#fee090', - '#ffffbf', - '#e0f3f8', - '#abd9e9', - '#74add1', - '#4575b4', - '#313695' - ], - BrBG: [ - '#543005', - '#8c510a', - '#bf812d', - '#dfc27d', - '#f6e8c3', - '#f5f5f5', - '#c7eae5', - '#80cdc1', - '#35978f', - '#01665e', - '#003c30' - ], - RdGy: [ - '#67001f', - '#b2182b', - '#d6604d', - '#f4a582', - '#fddbc7', - '#ffffff', - '#e0e0e0', - '#bababa', - '#878787', - '#4d4d4d', - '#1a1a1a' - ], - PuOr: [ - '#7f3b08', - '#b35806', - '#e08214', - '#fdb863', - '#fee0b6', - '#f7f7f7', - '#d8daeb', - '#b2abd2', - '#8073ac', - '#542788', - '#2d004b' - ] - }; - - return schemeMapper(scheme, schemes); -}; - -/** - * @function - * @description A wrapper function for ColorBrewer's map of qualitative color schemes. - * @param scheme The name of the scheme - * @returns An array of colors in hex represantation. - * @example - * - * import { qualitative } from 'huetiful-js' - - -console.log(qualitative("Accent")) -// [ - '#7fc97f', '#beaed4', - '#fdc086', '#ffff99', - '#386cb0', '#f0027f', - '#bf5b17', '#666666' -] - - */ - -const qualitative = (scheme: QualitativeScheme): Color[] => { - const schemes = { - Set2: [ - '#66c2a5', - '#fc8d62', - '#8da0cb', - '#e78ac3', - '#a6d854', - '#ffd92f', - '#e5c494', - '#b3b3b3' - ], - Accent: [ - '#7fc97f', - '#beaed4', - '#fdc086', - '#ffff99', - '#386cb0', - '#f0027f', - '#bf5b17', - '#666666' - ], - Set1: [ - '#e41a1c', - '#377eb8', - '#4daf4a', - '#984ea3', - '#ff7f00', - '#ffff33', - '#a65628', - '#f781bf', - '#999999' - ], - Set3: [ - '#8dd3c7', - '#ffffb3', - '#bebada', - '#fb8072', - '#80b1d3', - '#fdb462', - '#b3de69', - '#fccde5', - '#d9d9d9', - '#bc80bd', - '#ccebc5', - '#ffed6f' - ], - Dark2: [ - '#1b9e77', - '#d95f02', - '#7570b3', - '#e7298a', - '#66a61e', - '#e6ab02', - '#a6761d', - '#666666' - ], - Paired: [ - '#a6cee3', - '#1f78b4', - '#b2df8a', - '#33a02c', - '#fb9a99', - '#e31a1c', - '#fdbf6f', - '#ff7f00', - '#cab2d6', - '#6a3d9a', - '#ffff99', - '#b15928' - ], - Pastel2: [ - '#b3e2cd', - '#fdcdac', - '#cbd5e8', - '#f4cae4', - '#e6f5c9', - '#fff2ae', - '#f1e2cc', - '#cccccc' - ], - Pastel1: [ - '#fbb4ae', - '#b3cde3', - '#ccebc5', - '#decbe4', - '#fed9a6', - '#ffffcc', - '#e5d8bd', - '#fddaec', - '#f2f2f2' - ] - }; - return schemeMapper(scheme, schemes); -}; - -export { diverging, qualitative, sequential }; diff --git a/src/colors/colors.ts b/src/colors/colors.ts deleted file mode 100644 index 5166c4b5..00000000 --- a/src/colors/colors.ts +++ /dev/null @@ -1,67 +0,0 @@ -//@ts-nocheck -import shades from '../color-maps/swatches/tailwind.ts'; -import type { HueMap, ScaleValues, Color } from '../paramTypes.ts'; - -/** - * @function - * @description A wrapper function for the default Tailwind palette. If called with both parameters it return the hex code at the specified shade and value. Else, if called with the shade parameter as "all" it will return all colors from the shades in the palette map at the specified value (if value is undefined it will default to "500"). When called with the shade parameter only it will return all the colors from 100 to 900 of the specified shade. - * @param shade Any shade in the default TailwindCSS palette e.g amber,blue. - * @param val Any value from 100 to 900 in increments of 100 e.g "200". - * @returns color Returns a hex code string or array of hex codes depending on how the function is called. - * @example - * - * import { colors } from "huetiful-js"; - -let all300 = colors("all", 300); - -console.log(all300) -//[ - '#cbd5e1', '#d1d5db', '#d4d4d8', - '#d4d4d4', '#d6d3d1', '#fca5a5', - '#fdba74', '#fcd34d', '#fde047', - '#bef264', '#86efac', '#6ee7b7', - '#5eead4', '#7dd3fc', '#93c5fd', - '#c4b5fd', '#d8b4fe', '#f0abfc', - '#f9a8d4', '#fda4af' -] - -let red = colors("red"); -console.log(red); - -// [ - '#fef2f2', '#fee2e2', - '#fecaca', '#fca5a5', - '#f87171', '#ef4444', - '#dc2626', '#b91c1c', - '#991b1b', '#7f1d1d' -] - -let red100 = colors("red", 100); - -console.log(red100) -// #fee2e2 - */ -const colors = (shade: keyof HueMap, val: ScaleValues): Color | Color[] => { - const { keys } = Object; - const defaultHue = 'all'; - const hueKeys = keys(shades); - - shade = shade.toLowerCase(); - // First do an AND check on hue and val params. If true return the hue at the specified value. - // If only the hue is defined return the whole array of hex codes for that color. - - // If only the value is defined return that color value for every hue. - - if (shade === defaultHue) { - return shades.map((color) => color[val || '500']); - } else if (hueKeys.some((hue) => hue === shade) && val) { - return shades[shade][val]; - } else if (shade && typeof val == 'undefined') { - const keyVals = keys(shades[shade]); - return keyVals.map((key) => shades[shade][key]); - } else if (typeof shade && typeof val == 'undefined') { - throw Error(`Both shade and value cannot be undefined`); - } -}; - -export { colors }; diff --git a/src/colors/hue.ts b/src/colors/hue.ts deleted file mode 100644 index 37915613..00000000 --- a/src/colors/hue.ts +++ /dev/null @@ -1,189 +0,0 @@ -//This module contains getNearestHue,getFarthestHue,minHue and maxHue which are collection based utils that return the color with the queried factor. -// @ts-nocheck - -import { getChannel } from '../getters_and_setters/get.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; -import type { Color, HueColorSpaces, Factor } from '../paramTypes'; - -// Globals - -const { abs } = Math; - -const factor: Factor = 'hue'; -const mode = (colorSpace: HueColorSpaces): string => `${colorSpace || 'lch'}.h`; -// The hue value of our color which we are using for comparison -const targetHue = (color: Color, colorSpace: HueColorSpaces): number => - getChannel(mode(colorSpace))(color); - -// The callback to invoke per color in the passed in collection. -// Return the absolute value since hue is a cyclic value which can either be in clockwise/anti-clockwise. -//This means that the color object with the smallest hue value is the nearest color/hue. -const cb = - (color: Color, colorSpace: HueColorSpaces | string) => - (subtrahend: Color) => { - return abs( - targetHue(color, colorSpace) - getChannel(mode(colorSpace))(subtrahend) - ); - }; - -// Callback func for the minHue and maxHue utils. The funny thing is that most of the code is similar with minor changes here and there -const predicate = (colorSpace: HueColorSpaces) => (color: Color) => { - return getChannel(mode(colorSpace))(color) || undefined; -}; - -/** - * - * @function - * @description Returns the smallest hue difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic to remove grays from your color collection as a predicate to Array.map() - * @param color The color to use its hue value as the minuend. - * @param colors The collection of colors to compare against. - * @param colorSpace The mode color space to perform the computation in. - * @returns The hue value from the color with the smallest hue distance. If the colors are achromatic, it returns undefined. - * @example - * - * import { getNearestHue } from 'huetiful-js' - -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(getNearestHue('lime', sample, 'lch')) - -// 23.962083662849253 - */ -const getNearestHue = ( - color: Color, - colors: Color[], - colorSpace?: HueColorSpaces -): number => { - return sortedArr( - factor, - cb(color, mode(colorSpace)), - 'asc', - true - )(colors).filter((el) => el[factor] !== undefined)[0][factor]; -}; - -/** - * - * @function - * @description Returns the largest hue difference between the passed in color and each element in the colors collection. Achromatic colors are excluded from the final result array. Use isAchromatic to remove grays from your color collection as a predicate to Array.map() - * @param color The color to use its hue value as the minuend. - * @param colors The collection of colors to compare against. - * @param colorSpace The mode color space to perform the computation in. - * @returns The hue value from the color with the largest hue distance. If the colors are achromatic, it returns undefined. - *@example -import { getFarthestHue } from 'huetiful-js' - - let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -console.log(getFarthestHue('lime', sample, 'lch')) -// 139.16534433552653 - -*/ - -const getFarthestHue = ( - color: Color, - colors: Color[], - colorSpace?: HueColorSpaces -): number => { - return sortedArr( - factor, - cb(color, mode(colorSpace)), - 'desc', - true - )(colors).filter((el) => el[factor] !== undefined)[0][factor]; -}; - -/** - *@function - * @description Gets the smallest hue value from the passed in colors. - * @param colors The array of colors to query the color with the smallest hue value. - * @param colorSpace The mode color space to perform the computation in. - * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. - * @returns The smallest hue value in the colors passed in or a custom object. - * @example - * - * import { minHue } from 'huetiful-js' - -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(minHue(sample, 'lch')) -// 12.462831644544274 - */ -const minHue = ( - colors: Color[], - colorSpace?: HueColorSpaces, - colorObj = false -): number | { factor: number; color: Color } => { - const result: Array<{ factor: number; name: Color }> = sortedArr( - factor, - predicate(colorSpace), - 'asc', - true - )(colors).filter((el) => el[factor] !== undefined); - - let value; - - if (result.length > 0) { - if (colorObj) { - value = result[0]; - } else { - value = result[0][factor]; - } - } - - return value; -}; - -/** - *@function - * @description Gets the largest hue value from the passed in colors. - * @param colors The array of colors to query the color with the largest hue value. - * @param colorSpace The mode color space to perform the computation in. - * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. - * @returns The largest hue value in the colors passed in or a custom object. - * @example - * - * import { maxHue } from 'huetiful-js' -let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] - -console.log(maxHue(sample, 'lch')) -// 273.54920266436477 - */ -const maxHue = ( - colors: Color[], - colorSpace?: HueColorSpaces, - colorObj = false -): number | { factor: number; color: Color } => { - const result: Array<{ factor: number; name: Color }> = sortedArr( - factor, - predicate(colorSpace), - 'desc', - true - )(colors).filter((el) => el[factor] !== undefined); - - let value; - - if (result.length > 0) { - if (colorObj) { - value = result[0]; - } else { - value = result[0][factor]; - } - } - - return value; -}; - -export { getFarthestHue, getNearestHue, maxHue, minHue }; diff --git a/src/colors/index.ts b/src/colors/index.ts deleted file mode 100644 index dbde2c79..00000000 --- a/src/colors/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export { - getFarthestChroma, - getNearestChroma, - maxChroma, - minChroma -} from './chroma'; -export { diverging, qualitative, sequential } from './colorBrewer'; -export { colors } from './colors'; -export { - getFarthestLightness, - getNearestLightness, - maxLightness, - minLightness -} from './lightness'; -export { getFarthestHue, getNearestHue, maxHue, minHue } from './hue'; -export { overtone } from './overtone'; -export { tailwindColors } from './tailwindColors'; -export { isCool, isWarm, maxTemp, minTemp } from './temperature'; -export { isAchromatic } from './achromatic'; diff --git a/src/colors/lightness.ts b/src/colors/lightness.ts deleted file mode 100644 index 578aa2e8..00000000 --- a/src/colors/lightness.ts +++ /dev/null @@ -1,166 +0,0 @@ -// @ts-nocheck - -import { getChannel } from '../getters_and_setters/get.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; -import type { Color, Factor } from '../paramTypes'; - -const lightness = 'lab.l'; - -// The subtrahend is each color in the collection -//This means that the color object with the smallest lightness value is the nearest lightness. -// First check which value is greater and then act accordingly. Refactor hue.ts so that it returns negative -const lightnessDiff = (color: Color) => (subtrahend: Color) => { - if (getChannel(lightness)(color) < getChannel(lightness)(subtrahend)) { - return getChannel(lightness)(subtrahend) - getChannel(lightness)(color); - } else { - return getChannel(lightness)(color) - getChannel(lightness)(subtrahend); - } -}; - -/** - * - * @function - * @description Returns the smallest lightness difference between the passed in color and each element in the colors collection. Alightnesstic colors are excluded from the final result array. Use isAlightnesstic with Array.map to remove grays from your color collection. - * @param color The color to use its lightness value as the minuend. - * @param colors The collection of colors to compare against. - * @returns The lightness value from the color with the smallest lightness distance. If the colors are alightnesstic, it returns undefined. - * @example - * - * import { getNearestLightness } from 'huetiful-js' - -let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] - -console.log(getNearestLightness("green", sample)) - -//26.338769793418493 - * - */ -const getNearestLightness = (color: Color, colors: Color[]): number => { - // The factor being investigated. - - const factor: Factor = 'lightness'; - const cb = lightnessDiff(color); - const sortedObjArr = sortedArr(factor, cb, 'asc', true)(colors); - return sortedObjArr[0][factor]; -}; - -/** - * - * @function - * @description Returns the largest lightness difference between the passed in color and each element in the colors collection. Alightnesstic colors are excluded from the final result array. Use isAlightnesstic with Array.map to remove grays from your color collection. - * @param color The color to use its lightness value as the minuend. - * @param colors The collection of colors to compare against. - * @returns The lightness value from the color with the largest lightness distance. - * @example - * - * import { getFarthestLightness } from 'huetiful-js' - -let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] - -console.log(getFarthestLightness("green", sample)) - -// 34.668980006120606 - - */ - -const getFarthestLightness = (color: Color, colors: Color[]): number => { - // The factor being investigated. - - const factor: Factor = 'lightness'; - const cb = lightnessDiff(color); - const sortedObjArr = sortedArr(factor, cb, 'desc', true)(colors); - return sortedObjArr[0][factor]; -}; - -/** - *@function - * @description Gets the smallest lightness value from the passed in colors. - * @param colors The array of colors to query the color with the smallest lightness value. - * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. - * @returns The smallest lightness value in the colors passed in or a custom object. - * @example - * - * import { minLightness } from 'huetiful-js' - -let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] - -console.log(minLightness(sample, true)) - -// { lightness: 72.61647882089876, name: '#a1bd2f' } - - */ -const minLightness = ( - colors: Color[], - colorObj = false -): number | { factor: number; color: Color } => { - // The factor being investigated. - - const factor: Factor = 'lightness'; - const cb = getChannel(lightness); - const result: Array<{ factor: number; name: Color }> = sortedArr( - factor, - cb, - 'asc', - true - )(colors); - let value: number | { factor: number; name: Color }; - - if (gt(result.length, 0)) { - if (colorObj) { - value = result[0]; - } else { - value = result[0][factor]; - } - } - return value; -}; - -/** - *@function - * @description Gets the largest lightness value from the passed in colors. - * @param colors The array of colors to query the color with the largest lightness value. - * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. - * @returns The largest lightness value in the colors passed in or a custom object. - * @example - * - * import { maxLightness } from 'huetiful-js' - -let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] - -console.log(maxLightness(sample, true)) - -// { lightness: 80.94668903360088, name: '#f3bac1' } - - */ -const maxLightness = ( - colors: Color[], - colorObj = false -): number | { factor: number; color: Color } => { - // The factor being investigated. - - const factor: Factor = 'lightness'; - const cb = getChannel(lightness); - const result: Array<{ factor: number; name: Color }> = sortedArr( - factor, - cb, - 'desc', - true - )(colors); - let value: number | { factor: number; name: Color }; - - if (gt(result.length, 0)) { - if (colorObj) { - value = result[0]; - } else { - value = result[0][factor]; - } - } - return value; -}; - -export { - getFarthestLightness, - getNearestLightness, - maxLightness, - minLightness -}; diff --git a/src/colors/overtone.ts b/src/colors/overtone.ts deleted file mode 100644 index f1dcd2c6..00000000 --- a/src/colors/overtone.ts +++ /dev/null @@ -1,42 +0,0 @@ -//@ts-nocheck -import { getChannel } from '../getters_and_setters/get.ts'; -import hueTempMap from '../color-maps/samples/hueTemperature'; -import { isAchromatic } from './achromatic.ts'; -import { customFindKey } from '../fp/object/customFindKey.ts'; -import type { Color } from '../paramTypes.ts'; - -/** - * @function - * @description Returns the hue which is biasing the passed in color - * @param color The color to query its overtone. - * @returns The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false. - * @example - * - * import { overtone } from "huetiful-js"; - * -console.log(overtone("fefefe")) -// 'gray' - -console.log(overtone("cyan")) -// 'green' - -console.log(overtone("blue")) -// false - */ -const overtone = (color: Color): string | boolean => { - const factor = getChannel('lch.h')(color); - let hue = customFindKey(hueTempMap, factor); - - // We check if the color can be found in the defined ranges - - if (isAchromatic(color)) { - return 'gray'; - } else if (/-/.test(hue)) { - hue = hue.split('-'); - return hue[1]; - } else { - return false; - } -}; - -export { overtone }; diff --git a/src/colors/tailwindColors.ts b/src/colors/tailwindColors.ts deleted file mode 100644 index adf4dd69..00000000 --- a/src/colors/tailwindColors.ts +++ /dev/null @@ -1,65 +0,0 @@ -//@ts-nocheck - -import tailwindHues from '../color-maps/swatches/tailwind'; -import type { HueMap, ScaleValues } from '../paramTypes.ts'; - -/** - * @function - * @description Wrapper function that returns TailwindCSS color value(s) of the specified shade. If invoked with no parameters it returns an array of colors from 100 to 900. If invoked with parameter will return the specified shade vale, - * @param val The tone value of the shade. Values are in incrementals of 100. Both numeric (100) and its string equivalent ('100') are valid. - * @returns color A hex string value or array of hex strings. - * @example - * - * import { tailwindColors } from "huetiful-js"; - -// We pass in red as the target hue. -// It returns a function that can be called with an optional value parameter -let red = tailwindColors("red"); -console.log(red()); -// [ - '#fef2f2', '#fee2e2', - '#fecaca', '#fca5a5', - '#f87171', '#ef4444', - '#dc2626', '#b91c1c', - '#991b1b', '#7f1d1d' -] - - -console.log(red(100)); -// '#fee2e2' - -console.log(red('900')); -// '#7f1d1d' - - - * - */ -const tailwindColors = - (shade: keyof HueMap) => - (val?: ScaleValues): string | string[] => { - // This is a curried func that takes in the shade and returns a function that takes in a value from 100 thru 900 - shade = shade.toLowerCase(); - const { keys } = Object; - // We check if the shade is a valid Tailwind shade if not we return pure black. - let targetHue: object; - - if (keys(tailwindHues).indexOf(shade) != -1) { - targetHue = tailwindHues[shade]; - } else { - throw Error( - `${shade} is not a valid shade in the default Tailwind palette` - ); - } - - if (typeof val === 'undefined') { - return keys(targetHue).map((value) => targetHue[value]); - } else if (keys(targetHue).indexOf(val) > -1) { - return targetHue[val]; - } else { - throw Error( - `${val} is not a valid scale value. Values are in increments of 100 up to 900 e.g "200"` - ); - } - }; - -export { tailwindColors }; diff --git a/src/colors/temperature.ts b/src/colors/temperature.ts deleted file mode 100644 index 68386c1a..00000000 --- a/src/colors/temperature.ts +++ /dev/null @@ -1,165 +0,0 @@ -//@ts-nocheck - -import { getChannel } from '../getters_and_setters/get.ts'; -import hueTempMap from '../color-maps/samples/hueTemperature'; -import { getTemp } from '../converters/getTemp.ts'; -import { floorCeil } from '../fp/number/floorCeil.ts'; -import { inRange } from '../fp/number/inRange.ts'; -import { min, max } from '../fp/array/min_max.ts'; -import { customConcat } from '../fp/object/customConcat.ts'; -import { customFindKey } from '../fp/object/customFindKey.ts'; -import type { Color } from '../paramTypes.ts'; - -const predicate = (factor: number, temp: 'warm' | 'cool'): boolean => { - const hueKeys = Object.keys(hueTempMap); - if ( - hueKeys.some((val) => - inRange( - floorCeil(factor), - hueTempMap[val][temp][0], - hueTempMap[val][temp][1] - ) - ) - ) { - return true; - } else { - return false; - } -}; -/** - * @function - * @description Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false. - * @param color The color to check the temperature. - * @returns True or false. - * @example - * - * import { isCool } from 'huetiful-js' - -let sample = [ - "#00ffdc", - "#00ff78", - "#00c000" -]; - - -console.log(isCool(sample[7])); -// false - -console.log(map(sample, isCool)); - -// [ true, false, truee] - - - - */ -const isCool = (color: Color): boolean => { - // First we need to get the hue value which we'll pass to the predicate - const factor = getChannel('lch.h')(color); - - return predicate(factor, 'cool'); -}; - -/** - * @function - * @description Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false. - * @param color The color to check the temperature. - * @returns True or false. - * @example import { isWarm } from 'huetiful-js' - -let sample = [ - "#00ffdc", - "#00ff78", - "#00c000" -]; - - - -console.log(isWarm(sample[7])); -//true - -console.log(map(sample, isWarm)); - - -// [ false, true, false] - - */ -const isWarm = (color: Color): boolean => { - const factor = getChannel('lch.h')(color); - - return predicate(factor, 'cool'); -}; - -/** - * @function - * @description Checks the approximate maximum temperature that a color can have without losing its original hue. Does not take into account overtones (for now) - * @param color The color to check its maximum temperature. - * @returns The maximum temperature in Kelvins. - * @example - * - * import { maxTemp } from "huetiful-js"; - * - * console.log(maxTemp("#a1bd2f")) -// 7926 - -console.log(maxTemp("b2c3f1")) -// 9570 - */ -const maxTemp = (color: Color): number => { - // Get the hue value of the color - const factor = getChannel('lch.h')(color); - - // Then we check to see in what hue family it is and check the highest hue value for that family - const hue: string = customFindKey(hueTempMap, factor); - - // Get accurate hue start/ends in HSL - - const maxHue: number = max(...customConcat(hueTempMap[hue])); - - const result = getTemp({ - l: getChannel('lch.l')(color), - c: getChannel('lch.c')(color), - h: maxHue, - mode: 'lch' - }); - - return result; -}; - -/** - * @function - * @description Checks the approximate minimum temperature that a color can have without losing its original hue. Does not take into account overtones (for now) - * @param color The color to check its minimum temperature. - * @returns The minimum temperature in Kelvins. - * @example - * - * import { minTemp } from 'huetiful-js' - * - * console.log(minTemp("#a1bd2f")) -// 2528 - -console.log(minTemp("b2c3f1")) -// 20107 - * - */ -const minTemp = (color: Color): number => { - // Get the hue value of the color - // eslint-disable-next-line prefer-const - let factor = getChannel('lch.h')(color); - - // Then we check to see in what hue family it is and check the highest hue value for that family - const hue: string = customFindKey(hueTempMap, factor); - - const minHue: number = min(...customConcat(hueTempMap[hue])); - - // Get accurate hue start/ends in HSL - - const result = getTemp({ - l: getChannel('lch.l')(color), - c: getChannel('lch.c')(color), - h: minHue, - mode: 'lch' - }); - - return result; -}; -export { isCool, isWarm, maxTemp, minTemp }; diff --git a/src/converters.ts b/src/converters.ts new file mode 100644 index 00000000..b2c56e9e --- /dev/null +++ b/src/converters.ts @@ -0,0 +1,275 @@ +/* eslint-disable no-ternary */ +/* + * @license + * converters.ts - Converter functions for huetiful-js. + * Contains parts of chroma.js released under the same license. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { + useMode, + modeRgb, + converter, + modeDlch, + modeJch, + modeLch, + modeLch65, + modeLchuv, + modeOklch, + formatHex +} from 'culori/fn'; +import type { + ColorTuple, + Colorspaces, + ColorToken, + UniformColorSpaces +} from './types'; +import 'culori/all'; +import { formatHex8, colorsNamed } from 'culori/fn'; +import { getModeChannel } from './helpers'; + +/** + * Converter function with mode definitions for uniform color spaces. The function is curried to return a converter in the passed colospace. + * @param colorspace The mode converter to return. + * @returns The converter function in the mode colorspace. + */ +function ucsConverter(colorspace: UniformColorSpaces) { + const ucsDefinitions = { + jch: modeJch, + lch: modeLch, + dlch: modeDlch, + lchuv: modeLchuv, + oklch: modeOklch, + lch65: modeLch65 + }; + + return useMode(ucsDefinitions[colorspace.toLowerCase()]); +} +/** + * + Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal. + * @param color The color to convert to hexadecimal. Works on color objects and CSS named colors. + * @returns A hexadecimal representation of the passed in color. + * @example + * import { toHex } from "huetiful-js"; + * +console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" })) +// #7b794180 + +console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" })) +// #7b7941 + */ +function toHex(color: ColorToken): string { + // the result to return at the end of the function + var output; + // if its of type string and not a CSS named color then its probably hex so we don't convert it + if (typeof color === 'string') { + // @ts-ignore + if (!Object.keys(colorsNamed).some((el) => el === color.toLowerCase())) { + return color; + } + return formatHex(color); + } else if (typeof color === 'boolean') { + return (color !== true && '#ffffff') || '#000000'; + } else if (typeof color === 'number') { + output = num2rgb(color, true) as string; + } else { + // Get the mode variable + const mode = Array.isArray(color) + ? (color[0] as ColorTuple[0]) + : (color['mode'] as Colorspaces); + const alpha = + Array.isArray(color) && color.length === 5 + ? (color[color.length - 1] as ColorTuple[4]) + : (color['alpha'] as number); + + var channelKeys = getModeChannel(mode).split(''); + var colorTupleToObj = (colorTuple: number[]) => { + var channels = colorTuple.slice(0, 3); + + if ( + ['rgb', 'lrgb'].indexOf(mode) !== -1 && + channels.some((ch: number) => 1 < Math.abs(ch)) + ) { + // @ts-ignore + channels = channels.map((ch) => ch / 255); + } + + let output = { + [channelKeys[0]]: channels[0], + [channelKeys[1]]: channels[1], + [channelKeys[2]]: channels[2], + mode: mode, + alpha: alpha + }; + + return output; + }; + if (mode) { + // coerce color tuple to object + if (Array.isArray(color)) { + // @ts-ignore + output = colorTupleToObj(color.slice(1)); + } else { + output = colorTupleToObj( + toColorTuple(color, mode as Colorspaces).slice(1) as number[] + ); + } + } + } + + return (output['alpha'] < 1 && formatHex8(output)) || formatHex(output); +} + +// Ported from chroma-js with slight modifications +/** + * + * Returns the RGB color equivalent of any number between 0 and 16,777,215. + * @param num The number to convert to RGB + * @returns color An RGB color object or hex string. + * @example + * + * import { num2rgb } from 'huetiful-js' + +console.log(num2rgb(900, true)) +// #000384 + */ + +function num2rgb(num: number, hex = false): ColorToken { + if (typeof num === 'number' && num >= 0 && num <= 0xffffff) { + const r = num >> 16; + const g = (num >> 8) & 0xff; + const b = num & 0xff; + + const output = { + r: r / 255, + g: g / 255, + b: b / 255, + mode: 'lrgb' + }; + // @ts-ignore + return (hex && formatHex(output)) || output; + } else { + throw Error('unknown num color: ' + num); + } +} + +/** + * + * Returns the numerical equivalent of a color. + * @param color The color to convert to its numerical equivalent. + * @returns value The numerical value of the color from 0 to 16,777,215. + * @example + * + * import { rgb2num } from 'huetiful-js' + +console.log(rgb2num("b2c3f1")) +// 11715569 + */ + +function rgb2num(color: ColorToken): number { + // @ts-ignore + const rgb: ColorToken = useMode(modeRgb)(toHex(color)); + return ((255 * rgb['r']) << 16) + ((255 * rgb['g']) << 8) + 255 * rgb['b']; +} + +/* eslint-disable no-ternary */ + +//ported from chroma-js + +/** + * + * Converts the temperature value (in Kelvins) to an RGB color. + * @param kelvin The number of Kelvins. From 0 to 30,000 . + * @param hex Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true. + * @returns color The color as a hexadecimal or RGB color object. + * @example + * + * import { temp2Color } from 'huetiful-js' + +console.log(temp2Color(2542)) +// #ffa44a + */ + +function temp2Color(kelvin: number, hex = false): ColorToken { + const { log } = Math; + const temp = kelvin / 100; + + var r: number, g: number, b: number; + if (temp < 66) { + r = 255; + g = + temp < 6 + ? 0 + : -155.25485562709179 - + 0.44596950469579133 * (g = temp - 2) + + 104.49216199393888 * log(g); + b = + temp < 20 + ? 0 + : -254.76935184120902 + + 0.8274096064007395 * (b = temp - 10) + + 115.67994401066147 * log(b); + } else { + r = + 351.97690566805693 + + 0.114206453784165 * (r = temp - 55) - + 40.25366309332127 * log(r); + g = + 325.4494125711974 + + 0.07943456536662342 * (g = temp - 50) - + 28.0852963507957 * log(g); + b = 255; + } + const result = { + r: r / 255, + g: g / 255, + b: b / 255, + mode: 'rgb' + }; + + // @ts-ignore + return (hex && formatHex(result)) || result; +} + +/** + * + * Returns an array of channel values in the mode color space. It does not mutate the values of the passed in color token. + * @param color Expects the color to be in hexadecimal represantation or as a plain color object. + * @param mode The mode color space to return channel values for + * @returns An array of channel values with the colorspace as first element and the alpha channel if its explicitly defined in the passed in color. + * @example + * + * +let rgbColor = { + r: 0.4, + g: 0.3, + b: 0.7, + mode: "rgb", +}; +console.log(toColorTuple(rgbColor,'rgb')); + +// [ 'rgb', 0.4, 0.3, 0.7 ] + + */ + +function toColorTuple(color: string | object, mode: Colorspaces) { + // @ts-ignore + const colorObject: ColorToken = converter(mode)(color); + + var arr = Object.keys(colorObject) + .filter((ch) => ch !== 'mode') + .map((key) => colorObject[key]); + arr.unshift(mode); + + return arr; +} + +export { num2rgb, rgb2num, temp2Color, toColorTuple, ucsConverter, toHex }; diff --git a/src/converters/getTemp.ts b/src/converters/getTemp.ts deleted file mode 100644 index 77cbc624..00000000 --- a/src/converters/getTemp.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Based on implementation by Neil Bartlett - * https://github.com/neilbartlett/color-temperature and chroma-js - **/ - -// @ts-nocheck -import { useMode, modeLrgb } from 'culori/fn'; -import { temp2Color } from './temp2Color.ts'; -import type { Color } from '../paramTypes.js'; -import { toHex } from './toHex.ts'; - -/** - * @description Returns the temperature value in Kelvins of the passed in color. - * @param color The color to query its temperature value - * @returns The color's temperature in Kelvins. - * @example - * - * import { getTemp } from 'huetiful-js' - -console.log(getTemp('#a1bd2f')) -// 2542 - */ - -const getTemp = (color: Color): number => { - const { round } = Math; - //Store the color in an object with the RGB channels normalized to [0,1] - // Add a color obj for rgb using culori - const toRgb = useMode(modeLrgb); - const src = toRgb(toHex(color)); - // Allocate the red and blue channels to variables - const r: number = src['r'], - b: number = src['b']; - let minTemp = 1000; - let maxTemp = 40000; - const eps = 0.4; - let temp: number; - while (maxTemp - minTemp > eps) { - temp = (maxTemp + minTemp) * 0.5; - const rgb = temp2Color(temp, false); - if (rgb['b'] / rgb['r'] >= b / r) { - maxTemp = temp; - } else { - minTemp = temp; - } - } - return round(temp); -}; - -export { getTemp }; diff --git a/src/converters/index.ts b/src/converters/index.ts deleted file mode 100644 index 03946ac2..00000000 --- a/src/converters/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { temp2Color } from './temp2Color'; -export { toHex } from './toHex'; -export { rgb2num } from './rgb2num'; -export { num2rgb } from './num2rgb'; -export { getTemp } from './getTemp'; diff --git a/src/converters/num2rgb.ts b/src/converters/num2rgb.ts deleted file mode 100644 index 29da156e..00000000 --- a/src/converters/num2rgb.ts +++ /dev/null @@ -1,46 +0,0 @@ -//@ts-nocheck - -import 'culori/css'; -import type { Color } from '../paramTypes.ts'; -import { toHex } from './toHex.ts'; -// If the value is a floating point then we treat the decimal value as the opacity of the color. - -// If the value passedin is a float then the decimal is treated as opacity -// The outputn is rounded up when you pass in an integer - -/** - * @function - * @description Returns the RGB color equivalent of any number between 0 and 16,777,215. - * @param num The number to convert to RGB - * @returns color An RGB color object or hex string. - * @example - * - * import { num2rgb } from 'huetiful-js' - -console.log(num2rgb(900, true)) -// #000384 - */ -const num2rgb = (num: number, hex = false): Color => { - if (typeof num === 'number' && num >= 0 && num <= 0xffffff) { - const r = num >> 16; - const g = (num >> 8) & 0xff; - const b = num & 0xff; - - const output = { - r: r / 255, - g: g / 255, - b: b / 255, - mode: 'rgb' - }; - - if (hex) { - return toHex(output); - } else { - return output; - } - } else { - throw Error('unknown num color: ' + num); - } -}; - -export { num2rgb }; diff --git a/src/converters/rgb2num.ts b/src/converters/rgb2num.ts deleted file mode 100644 index 9997d9bb..00000000 --- a/src/converters/rgb2num.ts +++ /dev/null @@ -1,27 +0,0 @@ -//@ts-nocheck - -import { useMode, modeRgb } from 'culori/fn'; -import { toHex } from './toHex.ts'; -import type { Color } from '../paramTypes.ts'; - -/** - * @function - * @description Returns the numerical equivalent of a color. - * @param color The color to convert to its numerical equivalent. - * @returns value The numerical value of the color from 0 to 16,777,215. - * @example - * - * import { rgb2num } from 'huetiful-js' - -console.log(rgb2num("b2c3f1")) -// 11715569 - */ - -const rgb2num = (color: Color): number => { - const toRgb = useMode(modeRgb); - const rgb: Color = toRgb(toHex(color)); - - return ((255 * rgb['r']) << 16) + ((255 * rgb['g']) << 8) + 255 * rgb['b']; -}; - -export { rgb2num }; diff --git a/src/converters/temp2Color.ts b/src/converters/temp2Color.ts deleted file mode 100644 index 3e7a9d88..00000000 --- a/src/converters/temp2Color.ts +++ /dev/null @@ -1,68 +0,0 @@ -// @ts-nocheck - -//ported from chroma-js - -import { toHex } from './toHex'; -import type { Color } from '../paramTypes'; -/** - * @function - * @description Converts the temperature value (in Kelvins) to an RGB color. - * @param kelvin The number of Kelvins. From 0 to 30,000 . - * @param hex Optional boolean parameter to either return an RGB color object or hexadecimal string. Default is true. - * @returns color The color as a hexadecimal or RGB color object. - * @example - * - * import { temp2Color } from 'huetiful-js' - -console.log(temp2Color(2542)) -// #ffa44a - */ -const temp2Color = (kelvin: number, hex = false): Color => { - //Hue change starts at approx 655 Kelvins ??? - const { log } = Math; - // Checking if the passed in value is within a problematic range that returns negative values on the blue channel. - //inRange(kelvin, 400, 650) ? (kelvin = eps) : kelvin; - // - const temp = kelvin / 100; - - let r: number, g: number, b: number; - if (temp < 66) { - r = 255; - g = - temp < 6 - ? 0 - : -155.25485562709179 - - 0.44596950469579133 * (g = temp - 2) + - 104.49216199393888 * log(g); - b = - temp < 20 - ? 0 - : -254.76935184120902 + - 0.8274096064007395 * (b = temp - 10) + - 115.67994401066147 * log(b); - } else { - r = - 351.97690566805693 + - 0.114206453784165 * (r = temp - 55) - - 40.25366309332127 * log(r); - g = - 325.4494125711974 + - 0.07943456536662342 * (g = temp - 50) - - 28.0852963507957 * log(g); - b = 255; - } - const result = { - r: r / 255, - g: g / 255, - b: b / 255, - mode: 'rgb' - }; - - if (hex) { - return toHex(result); - } else { - return result; - } -}; - -export { temp2Color }; diff --git a/src/converters/toHex.ts b/src/converters/toHex.ts deleted file mode 100644 index d1ccf77b..00000000 --- a/src/converters/toHex.ts +++ /dev/null @@ -1,95 +0,0 @@ -// @ts-nocheck - -import 'culori/css'; -import { formatHex8, formatHex, colorsNamed } from 'culori/fn'; -import { num2rgb } from './num2rgb'; -import type { Color } from '../paramTypes'; - -/** - *@function - @description Converts a wide range of color tokens which are color objects, and CSS named colors (for example 'red'), numbers from 0 to 166,777,215 and arrays in the form of [string,number,number,number,numer?] the first element in the array being the mode color space and the fourth optional number element as the opacity value to hexadecimal. - * @param color The color to convert to hexadecimal. Works on color objects and CSS named colors. - * @returns A hexadecimal representation of the passed in color. - * @example - * import { toHex } from "huetiful-js"; - * -console.log(toHex({ l: 50, c: 31, h: 100, alpha: 0.5, mode: "lch" })) -// #7b794180 - -console.log(toHex({ l: 50, c: 31, h: 100, mode: "lch" })) -// #7b7941 - */ -const toHex = (color: Color): Color => { - // the result to return at the end of the function - let src = {}; - - // if its of type string and not a CSS named color then its probably hex so we don't convert it - if ( - typeof color === 'string' && - !Object.keys(colorsNamed).some((el) => el === color) - ) { - return color; - } else { - // If our color is an array - if (Array.isArray(color)) { - // capture the mode - const mode: string = color[0]; - - // set mode to a substring which trims the string at an index that is the reslt of length - 3 - const modeChannels = mode.substring(mode.length - 3); - // Gets the channel key from the passed in mode - const getModeChan = (mode: string, key: number) => mode.charAt(key); - - // Store the channels excluding alpha - const channels = ( - src: object, - colorArr: [string, number, number, number?] - ) => { - // Remove the mode element - colorArr.shift(); - if (colorArr.length === 4) { - colorArr = colorArr.slice(0, 3); - } - return colorArr; - }; - - /** - * Returns a color object with normalized RGB values or just maps value to keys and return the resultant object - * @param src The object to manipulate - * @param mode The color space. - * @param colorArr The array of the color's channel values excluding the alpha/opacity channel - * @returns A color object - */ - const channelMapper = ( - src = {}, - mode: string, - colorArr: [number, number, number] - ): number[] => { - src['mode'] = mode; - // If our mode is rgb... - if (src['mode'] === 'rgb') { - // if our rgb values are [0,255] we normalize them to [0,1] - // for Culori to make sense of the channel values else it defaults o white - if (colorArr.some((ch) => Math.abs(ch) > 1)) { - colorArr.map((ch, key) => (src[getModeChan(mode, key)] = ch / 255)); - } - } else { - colorArr.map((ch, key) => (src[getModeChan(mode, key)] = ch)); - } - return src; - }; - src['alpha'] = color[4] || 1; - src = channelMapper(src, modeChannels, channels(src, color)); - src = (src['alpha'] < 1 && formatHex8(src)) || formatHex(src); - } - // if its a number use num2rgb - else if (typeof color === 'number') { - src = num2rgb(color, true); - } else { - src = (color['alpha'] && formatHex8(color)) || formatHex(color); - } - return src; - } -}; - -export { toHex }; diff --git a/src/filterBy.ts b/src/filterBy.ts new file mode 100644 index 00000000..3d34e697 --- /dev/null +++ b/src/filterBy.ts @@ -0,0 +1,341 @@ +/** + * @license + * filterBy.ts - Utilities for filtering collections of colors. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { differenceHyab } from 'culori/fn'; +import { toHex } from './converters'; +import type { ColorToken, HueColorSpaces, Factor } from './types'; +import { getLuminance, getContrast, getChannel } from './utils'; +import { + matchChromaChannel, + matchLightnessChannel, + filteredArr, + normalize, + matchComparator, + matchDigits +} from './helpers'; + +/** + * @internal + * + * @param factor The color property in query. + * @param cb The predicate to get the equatable value used during comparison + * @param colors The collection to map over. Can either be an array or object whose values are valid color tokens. + * @param start The minimum end of the filtering range. + * @param end The maximum end of the filtering range. It can also be omitted and all colors greater than the starting value of the factor being queried will be returned. + * @returns An array of colors + */ +function baseFilterBy( + factor: Factor, + cb: (color: ColorToken) => number, + collection: ColorToken[] | object | object, + start: string | number, + end?: number, + colorspace?: HueColorSpaces +) { + const obj = { + saturation: matchChromaChannel, + lightness: matchLightnessChannel + }; + + var [sym, val, normalVal] = [ + matchComparator(start as string), + matchDigits(start as string), + normalize(Number(val), obj[factor](colorspace)) + ]; + start = + (typeof start === 'string' && sym && normalVal.toString().concat(sym)) || + start; + end = normalize(end, obj[factor](colorspace)); + return filteredArr(factor, cb)(collection as Array, start, end); +} + +/** + * + * Returns an array of colors in the specified saturation range. The range is normalised to [0,1]. + * @param colors The array of colors to filter. + * @param startSaturation The minimum end of the saturation range. + * @param endSaturation The maximum end of the saturation range. + * @param colorspace The color space to fetch the saturation value from. Any color space with a chroma channel e.g 'lch' or 'hsl' will do. + * @returns Array of filtered colors. + * @example + * import { filterByContrast } from 'huetiful-js' + +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(filterByContrast(sample, 'green', '>=3')) +// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] + */ + +function filterBySaturation( + collection: ColorToken[] | object, + startSaturation = 0.05, + endSaturation = 1, + colorspace?: HueColorSpaces +): ColorToken[] { + const modeChannel = matchChromaChannel(colorspace); + // Normalize properly later + const factor: Factor = 'saturation'; + + return baseFilterBy( + factor, + getChannel(modeChannel), + collection, + startSaturation, + endSaturation, + colorspace + ); +} + +/** + * + * Returns an array of colors in the specified luminance range. The range is normalised to [0,1]. + * @param colors The array of colors to filter. + * @param startLuminance The minimum end of the luminance range. + * @param endLuminance The maximum end of the luminance range. + * @returns Array of filtered colors. + * @example + * + * import { filterByLuminance } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByLuminance(sample, 0.4, 0.9) + +// [ '#00ffdc', '#00ff78' ] + */ + +function filterByLuminance( + collection: ColorToken[] | object, + startLuminance = 0.05, + endLuminance = 1 +): ColorToken[] { + return baseFilterBy( + 'luminance', + getLuminance, + collection, + startLuminance, + endLuminance + ); +} + +/** + * + * Returns an array of colors in the specified lightness range. The range is between 0 and 100. + * @param colors The array of colors to filter. + * @param startLightness The minimum end of the lightness range. + * @param endLightness The maximum end of the lightness range. + * @param colorspace The mode colorspace to retrieve the lightness value from. The default is lch65 + * @returns Array of filtered colors. + * @example + * + * import { filterByLightness } from 'huetiful-js' +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByLightness(sample, 20, 80) + +// [ '#00c000', '#007e00', '#164100', '#720000' ] + */ + +function filterByLightness( + collection: ColorToken[] | object, + startLightness = 5, + endLightness = 100, + colorspace?: HueColorSpaces +): ColorToken[] { + const factor: Factor = 'lightness'; + + return baseFilterBy( + factor, + getChannel(matchLightnessChannel(colorspace)), + collection, + startLightness, + endLightness, + colorspace + ); +} + +//filterByHue takes an array of colors and +/** + * + * Returns colors in the specified hue ranges between 0 to 360. + * @param colors The array of colors to filter. + * @param startHue The minimum end of the hue range. + * @param endHue The maximum end of the hue range. + * @returns Array of the filtered colors. + * @example + * let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +filterByHue(sample, 20, 80) + +// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] + */ + +function filterByHue( + collection: ColorToken[] | object, + startHue = 0, + endHue = 360, + colorspace?: HueColorSpaces +): ColorToken[] { + return baseFilterBy( + 'hue', + getChannel(`${colorspace}.h`), + collection, + startHue, + endHue + ); +} + +/** + * + * Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. Uses the differenceHyab metric for calculating the distances. + * @param colors The array of colors to filter. + * @param startDistance The minimum end of the distance range. + * @param endDistance The maximum end of the distance range. + * @returns Array of filtered colors. + * @example + * import { filterByDistance } from 'huetiful-js' + +let sample = [ + "#ffff00", + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#720000", + "#600000", +] + +console.log(filterByDistance(sample, "yellow", 0.1)) +// [ '#ffff00' ] + */ + +function filterByDistance( + collection: ColorToken[] | object, + against: ColorToken, + startDistance = 0.05, + endDistance?: number +): ColorToken[] { + const cb = (against) => (color) => differenceHyab()(against, color); + + return baseFilterBy( + 'distance', + cb(toHex(against)), + collection, + startDistance, + endDistance + ); +} + +/** + * + * Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges. + * @param colors The array of colors to filter. + * @param startContrast The minimum end of the contrast range. + * @param endContrast The maximum end of the contrast range. + * @returns Array of filtered colors. + * + * @example + * + * import { filterByContrast } from 'huetiful-js' + +let sample = [ + '#00ffdc', + '#00ff78', + '#00c000', + '#007e00', + '#164100', + '#ffff00', + '#310000', + '#3e0000', + '#4e0000', + '#600000', + '#720000', +] + +console.log(filterByContrast(sample, 'green', '>=3')) +// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] + */ + +function filterByContrast( + collection: ColorToken[] | object, + against: ColorToken, + startContrast = 1, + endContrast = 21 +): ColorToken[] { + const cb = (against: ColorToken) => (color: ColorToken) => + getContrast(color, against); + return baseFilterBy( + 'contrast', + cb(against), + collection, + startContrast, + endContrast + ); +} + +export { + filterByContrast, + filterByDistance, + filterByLuminance, + filterBySaturation, + filterByHue, + filterByLightness +}; diff --git a/src/filterBy/filterByContrast.ts b/src/filterBy/filterByContrast.ts deleted file mode 100644 index 66e756ac..00000000 --- a/src/filterBy/filterByContrast.ts +++ /dev/null @@ -1,53 +0,0 @@ -// @ts-nocheck -import { filteredArr } from '../fp/array/filteredArr.ts'; -import { wcagContrast } from 'culori/fn'; - -import type { Color, Factor } from '../paramTypes.ts'; -import { toHex } from '../converters/toHex.ts'; - -/** - * @function - * Returns an array of colors with the specified contrast range. The contrast is tested against a comparison color (the 'against' param) and the specified contrast ranges. - * @param colors The array of colors to filter. - * @param startContrast The minimum end of the contrast range. - * @param endContrast The maximum end of the contrast range. - * @returns Array of filtered colors. - * - * @example - * - * import { filterByContrast } from 'huetiful-js' - -let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -console.log(filterByContrast(sample, 'green', '>=3')) -// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] - */ - -const filterByContrast = ( - colors: Color[], - against: Color, - startContrast = 0.05, - endContrast?: number -): Color[] => { - // Formatting color tokens to parseable type - // Create an object that has the contrast and name of color as properties. - const factor: Factor = 'contrast'; - const cb = (against: Color) => (color: Color) => - wcagContrast(...[color, against].map(toHex)); - - return filteredArr(factor, cb(against))(colors, startContrast, endContrast); -}; - -export { filterByContrast }; diff --git a/src/filterBy/filterByDistance.ts b/src/filterBy/filterByDistance.ts deleted file mode 100644 index d325621f..00000000 --- a/src/filterBy/filterByDistance.ts +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-nocheck - -import { filteredArr } from '../fp/array/filteredArr.ts'; -import { differenceEuclidean } from 'culori/fn'; -import type { Color, Factor, ColorSpaces } from '../paramTypes.ts'; -import { toHex } from '../converters/toHex.ts'; -/** - * @function - * Returns an array of colors with the specified distance range. The distance is tested against a comparison color (the 'against' param) and the specified distance ranges. - * @param colors The array of colors to filter. - * @param startDistance The minimum end of the distance range. - * @param endDistance The maximum end of the distance range. - * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0]. - * @param mode The color space to calculate the distance in . - * @returns Array of filtered colors. - * @example - * import { filterByDistance } from 'huetiful-js' - -let sample = [ - "#ffff00", - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#720000", - "#600000", -] - -console.log(filterByDistance(sample, "yellow", 0.1)) -// [ '#ffff00' ] - */ - -const filterByDistance = ( - colors: Color[], - against: Color, - startDistance = 0.05, - endDistance?: number, - mode?: ColorSpaces, - weights?: [number, number, number, number] -): Color[] => { - // Formatting color tokens to parseable type - // How do I get the distance - - // Create an object that has the distance and name of color as properties. - const factor: Factor = 'distance'; - const cb = (against: Color, mode: ColorSpaces) => (color: Color) => - differenceEuclidean( - mode || 'lch', - weights || [1, 1, 1, 0] - )(...[against, color].map(toHex)); - - return filteredArr(factor, cb(against, mode))( - colors, - startDistance, - endDistance - ); -}; - -export { filterByDistance }; - -// Make modes lower case and string keys -// Debug 'Cannot destructure mode' diff --git a/src/filterBy/filterByHue.ts b/src/filterBy/filterByHue.ts deleted file mode 100644 index de76bc7a..00000000 --- a/src/filterBy/filterByHue.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { filteredArr } from '../fp/array/filteredArr.ts'; -import { getChannel } from '../getters_and_setters/get.ts'; -import type { Color, Factor } from '../paramTypes.ts'; - -//filterByHue takes an array of colors and -/** - * @function - * Returns colors in the specified hue ranges between 0 to 360. - * @param colors The array of colors to filter. - * @param startHue The minimum end of the hue range. - * @param endHue The maximum end of the hue range. - * @returns Array of the filtered colors. - * @example - * let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -filterByHue(sample, 20, 80) - -// [ '#310000', '#3e0000', '#4e0000', '#600000', '#720000' ] - */ - -const filterByHue = (colors: Color[], startHue = 0, endHue = 360): Color[] => { - const factor: Factor = 'hue'; - const cb = getChannel('lch.h'); - - return filteredArr(factor, cb)(colors, startHue, endHue); -}; - -export { filterByHue }; diff --git a/src/filterBy/filterByLightness.ts b/src/filterBy/filterByLightness.ts deleted file mode 100644 index 3c558c9d..00000000 --- a/src/filterBy/filterByLightness.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { filteredArr } from '../fp/array/filteredArr.ts'; -import { getChannel } from '../getters_and_setters/get.ts'; -import { Color, Factor } from '../paramTypes.ts'; -/** - * @function - * @description Returns an array of colors in the specified lightness range. The range is between 0 and 100. - * @param colors The array of colors to filter. - * @param startLightness The minimum end of the lightness range. - * @param endLightness The maximum end of the lightness range. - * @returns Array of filtered colors. - * @example - * - * import { filterByLightness } from 'huetiful-js' -let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -filterByLightness(sample, 20, 80) - -// [ '#00c000', '#007e00', '#164100', '#720000' ] - */ - -const filterByLightness = ( - colors: Color[], - startLightness = 5, - endLightness = 100 -): Color[] => { - // Formatting color tokens to parseable type - // Create an object that has the lightness and name of color as properties. - const factor: Factor = 'lightness'; - const cb = getChannel('lch.l'); - - return filteredArr(factor, cb)(colors, startLightness, endLightness); -}; - -export { filterByLightness }; diff --git a/src/filterBy/filterByLuminance.ts b/src/filterBy/filterByLuminance.ts deleted file mode 100644 index 02d687f2..00000000 --- a/src/filterBy/filterByLuminance.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { filteredArr } from '../fp/array/filteredArr.ts'; -import { Color } from '../paramTypes.ts'; -import { getLuminance } from '../getters_and_setters/luminance.ts'; -import type { Factor } from '../paramTypes'; -/** - * @function - * @description Returns an array of colors in the specified luminance range. The range is normalised to [0,1]. - * @param colors The array of colors to filter. - * @param startLuminance The minimum end of the luminance range. - * @param endLuminance The maximum end of the luminance range. - * @returns Array of filtered colors. - * @example - * - * import { filterByLuminance } from 'huetiful-js' -let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -filterByLuminance(sample, 0.4, 0.9) - -// [ '#00ffdc', '#00ff78' ] - */ - -const filterByLuminance = ( - colors: Color[], - startLuminance = 0.05, - endLuminance = 1 -): Color[] => { - // Formatting color tokens to parseable type - // Create an object that has the luminance and name of color as properties. - const factor: Factor = 'luminance'; - const cb = getLuminance; - - return filteredArr(factor, cb)(colors, startLuminance, endLuminance); -}; - -export { filterByLuminance }; diff --git a/src/filterBy/filterBySaturation.ts b/src/filterBy/filterBySaturation.ts deleted file mode 100644 index 70b00396..00000000 --- a/src/filterBy/filterBySaturation.ts +++ /dev/null @@ -1,50 +0,0 @@ -// @ts-nocheck -import { getChannel } from '../getters_and_setters/get.ts'; -import { filteredArr } from '../fp/array/filteredArr.ts'; -import type { Color, Factor } from '../paramTypes'; - -/** - * @function - * @description Returns an array of colors in the specified saturation range. The range is normalised to [0,1]. - * @param colors The array of colors to filter. - * @param startSaturation The minimum end of the saturation range. - * @param endSaturation The maximum end of the saturation range. - * @returns Array of filtered colors. - * @example - * import { filterByContrast } from 'huetiful-js' - -let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -console.log(filterByContrast(sample, 'green', '>=3')) -// [ '#00ffdc', '#00ff78', '#ffff00', '#310000', '#3e0000', '#4e0000' ] - */ - -const filterBySaturation = ( - colors: Color[], - startSaturation = 0.05, - endSaturation = 1 -): Color[] => { - const factor: Factor = 'saturation'; - const cb = getChannel('lch.c'); - - // Normalize saturation ranges later - return filteredArr(factor, cb)( - colors, - 100 * startSaturation, - 100 * endSaturation - ); -}; - -export { filterBySaturation }; diff --git a/src/filterBy/filterByTemp.ts b/src/filterBy/filterByTemp.ts deleted file mode 100644 index 143f62ff..00000000 --- a/src/filterBy/filterByTemp.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { getTemp } from '../converters/getTemp.ts'; -import { filteredArr } from '../fp/array/filteredArr.ts'; -import type { Color, Factor } from '../paramTypes.ts'; - -/** - * @function - * @description Returns an array of colors in the specified temperature range between 0 and 30,000 Kelvins. - * @param colors The array of colors to filter. - * @param startTemp The minimum end of the temperature range. - * @param endTemp The maximum end of the temperature range. - * @returns Array of the filtered colors. - * @see Based on Neil Bartlett's implementation https://github.com/neilbartlett/color-temperature - * @example - * - * import { filterByTemp } from "huetiful-js"; -let sample = [ -"#00ffdc", -"#00ff78", -"#00c000", -"#007e00", -"#164100", -"#ffff00", -"#310000", -"#3e0000", -"#4e0000", -"#600000", -"#720000", -]; - - -filterByTemp(sample, 1000, 20000); - -// [ -'#00c000', '#007e00', -'#164100', '#ffff00', -'#310000', '#3e0000', -'#4e0000', '#600000', -'#720000' -] - */ - -const filterByTemp = ( - colors: Color[], - startTemp = 1000, - endTemp = 6000 -): Color[] => { - // This variable stores the array that matches the filtering criteria defined by the start and end hues - const factor: Factor = 'temp'; - const cb = getTemp; - - return filteredArr(factor, cb)(colors, startTemp, endTemp); -}; - -export { filterByTemp }; - -//TODO Could also specify warm|cool to quickly return the filtered array. This param overrides startTemp and endTemp. diff --git a/src/filterBy/index.ts b/src/filterBy/index.ts deleted file mode 100644 index ca3653c0..00000000 --- a/src/filterBy/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { filterByTemp } from './filterByTemp'; -export { filterBySaturation } from './filterBySaturation'; -export { filterByLuminance } from './filterByLuminance'; -export { filterByHue } from './filterByHue'; -export { filterByLightness } from './filterByLightness'; -export { filterByDistance } from './filterByDistance'; -export { filterByContrast } from './filterByContrast'; diff --git a/src/fp/array/colorObjArr.ts b/src/fp/array/colorObjArr.ts deleted file mode 100644 index 71e6c246..00000000 --- a/src/fp/array/colorObjArr.ts +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-nocheck - -import { colorObj } from '../object/colorObj'; -import { Factor, Color } from '../../paramTypes'; - -/* - * @function - * @private - * Creates a custom object with a factor to pass to the predicate function. - * @param factor The quality being queried. - * @param cb The callback function for computing the factor's start. - * @param colors The array of colors to iterate over. - * @returns An array of objects. - */ -export const colorObjArr = - (factor: Factor, callback) => - (colors: Color[]): Array<{ factor: Factor; name: Color }> => { - const cb = colorObj(factor, callback); - return colors.map((color) => cb(color)); - }; diff --git a/src/fp/array/customSort.ts b/src/fp/array/customSort.ts deleted file mode 100644 index d14a5483..00000000 --- a/src/fp/array/customSort.ts +++ /dev/null @@ -1,21 +0,0 @@ -//@ts-nocheck -import { Factor, Order } from '../../paramTypes'; - -/** - * @description Helper function for native sorting method for arrays. - * @param factor The property to query. - * @param order Either ascending or descending. - * @returns A sorted array. - */ -export const customSort = (order: Order, factor?: Factor) => { - // Special thanks to deechris27 on youtube - // a-b gives asc order & b-a gives desc order - factor = factor || 'factor'; - return (a, b) => { - if (order === 'asc') { - return a[factor] - b[factor]; - } else if (order === 'desc') { - return b[factor] - a[factor]; - } - }; -}; diff --git a/src/fp/array/filteredArr.ts b/src/fp/array/filteredArr.ts deleted file mode 100644 index 1b0f111e..00000000 --- a/src/fp/array/filteredArr.ts +++ /dev/null @@ -1,66 +0,0 @@ -// @ts-nocheck -import { Factor, Color, callback } from '../../paramTypes'; -import { gt, gte, lt, lte } from '../number/comparison'; -import { inRange } from '../number/inRange'; -import { colorObjArr } from './colorObjArr'; - -/** - * @description Filters an array according to the value of a color's queried factor - * @param factor The property to query and use as filtering criteria - * @param cb The function to use for comparison - * @returns The filtered array - */ -const filteredArr = - (factor: Factor, cb?: callback) => - (colors: Color[], start: number | string, end: number): Color[] => { - let result: Color[]; - - if (typeof start === 'number') { - result = colorObjArr( - factor, - cb - )(colors) - .filter((color) => inRange(color[factor], start, end)) - .map((color) => color['name']); - - return result; - - // If string split the the string to an array of signature [sign,value] with sign being the type of predicate returned to mapFilter. - } else if (typeof start === 'string') { - //The pattern to match - const reOperator = /^(>=|<=|<|>)/; - - const value = /[0-9]*\.?[0-9]+/; - - // Array - const val = value.exec(start), - op = reOperator.exec(start); - - const mapFilter = (test: (x: number, y: number) => boolean): Color[] => { - return colorObjArr( - factor, - cb - )(colors) - .filter((el) => test(el[factor], parseFloat(val['0']))) - .map((el) => el['name']); - }; - switch (op['0']) { - case '<': - result = mapFilter(lt); - - break; - case '>': - result = mapFilter(gt); - break; - case '<=': - result = mapFilter(lte); - break; - case '>=': - result = mapFilter(gte); - break; - } - } - return result; - }; - -export { filteredArr }; diff --git a/src/fp/array/index.ts b/src/fp/array/index.ts deleted file mode 100644 index 42a2e514..00000000 --- a/src/fp/array/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// This module has array methods -// @ts-nocheck -export { min, max } from './min_max'; -export { colorObjArr } from './colorObjArr'; -export { customSort } from './customSort'; -export { filteredArr } from './filteredArr'; -export { sortedArr } from './sortedArr'; diff --git a/src/fp/array/min_max.ts b/src/fp/array/min_max.ts deleted file mode 100644 index b1ea4c0b..00000000 --- a/src/fp/array/min_max.ts +++ /dev/null @@ -1,46 +0,0 @@ -// @ts-nocheck - -import { gt, lt } from '../number/comparison'; - -// from the lodash implementation of _.min and _.max -const identity = (value) => { - return value; -}; -const baseExtremum = (array: number[], iteratee, comparator) => { - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index], - current = iteratee(value); - - if ( - current != null && - (computed === undefined - ? current === current - : comparator(current, computed)) - ) { - var computed = current, - result = value; - } - } - return result; -}; -/** - * @description Gets the smallest value in an array - * @param array The array to retrieve minimum value - * @returns The smallest number in the array - */ -const min = (array: number[]): number => { - return array && array.length ? baseExtremum(array, identity, lt) : undefined; -}; -/** - * @description Gets the largest value in an array - * @param array The array to retrieve maximum value - * @returns The largest number in the array - */ -const max = (array: number[]): number => { - return array && array.length ? baseExtremum(array, identity, gt) : undefined; -}; - -export { min, max }; diff --git a/src/fp/array/sortedArr.ts b/src/fp/array/sortedArr.ts deleted file mode 100644 index 0ae00f57..00000000 --- a/src/fp/array/sortedArr.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Factor, Color, callback, Order } from '../../paramTypes'; -import { colorObjArr } from './colorObjArr'; -import { customSort } from './customSort'; - -/** - * Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param. - * @param factor The property to query - * @param callback The function to use for comparison. - * @returns An array of colors or color objects. - */ -const sortedArr = - (factor: Factor, callback: callback, order: Order, colorObj = false) => - (colors: Color[]) => { - const results: Color[] | Array<{ factor: number; name: Color }> = - colorObjArr(factor, callback)(colors); - // (colorObj && color) || color['name']; - // Assign the value of colorObj to results variable - // Sort the array using our customSort helper function - results.sort(customSort(order, factor)); - - // colorObj parameter is true return the array of color objects - // else just return the color's name value. - if (colorObj) { - return results; - } else { - return results.map((color) => color['name']); - } - }; - -export { sortedArr }; diff --git a/src/fp/index.ts b/src/fp/index.ts deleted file mode 100644 index 2f921de2..00000000 --- a/src/fp/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './number'; -export * from './array'; -export * from './object'; -export * from './string'; diff --git a/src/fp/number/adjustHue.ts b/src/fp/number/adjustHue.ts deleted file mode 100644 index 38ef9e56..00000000 --- a/src/fp/number/adjustHue.ts +++ /dev/null @@ -1,11 +0,0 @@ -// @ts-nocheck - -const adjustHue = (value = 0) => { - if (value > 0) { - return (value += Math.ceil(-value / 360) * 360); - } else { - return value % 360; - } -}; - -export { adjustHue }; diff --git a/src/fp/number/comparison.ts b/src/fp/number/comparison.ts deleted file mode 100644 index 743598dd..00000000 --- a/src/fp/number/comparison.ts +++ /dev/null @@ -1,9 +0,0 @@ -// @ts-nocheck - -// Comparison operators -const gt = (x: number, y: number): boolean => x > y; -const lt = (x: number, y: number): boolean => x < y; -const gte = (x: number, y: number): boolean => x >= y; -const lte = (x: number, y: number): boolean => x <= y; - -export { lt, gt, gte, lte }; diff --git a/src/fp/number/floorCeil.ts b/src/fp/number/floorCeil.ts deleted file mode 100644 index 32580ff6..00000000 --- a/src/fp/number/floorCeil.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { isInt } from './isInt'; - -const { ceil, floor } = Math; -/** - * @function - * Rounds up or down a number based on the float value. - * @param num The number to round up or down. - * @returns An integer - */ -const floorCeil = (num: number): number => { - if (isInt(num)) { - return num; - } else { - const strArr = num.toString().split('.'); - const float = strArr[1]; - - //If the decimal value is .4 and below it will be rounded down else it will be rounded up. - const reFloorCeil = (float: string) => /^[0-4]$/.test(float.charAt(0)); - - if (reFloorCeil(float)) { - num = floor(num); - } else { - num = ceil(num); - } - } - return num; -}; - -export { floorCeil }; diff --git a/src/fp/number/inRange.ts b/src/fp/number/inRange.ts deleted file mode 100644 index e98bfca1..00000000 --- a/src/fp/number/inRange.ts +++ /dev/null @@ -1,10 +0,0 @@ -// @ts-nocheck - -const inRange = (number: number, start: number, end?: number): boolean => { - /* Built-in method references for those with the same name as other `lodash` methods. */ - var nativeMax = Math.max, - nativeMin = Math.min; - return number >= nativeMin(start, end) && number < nativeMax(start, end); -}; - -export { inRange }; diff --git a/src/fp/number/index.ts b/src/fp/number/index.ts deleted file mode 100644 index 9895d298..00000000 --- a/src/fp/number/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This module has number methods -// @ts-nocheck - -export { lt, gt, gte, lte } from './comparison'; -export { random } from './random'; -export { isInt } from './isInt'; -export { normalize } from './normalize'; -export { floorCeil } from './floorCeil'; -export { adjustHue } from './adjustHue'; -export { inRange } from './inRange'; diff --git a/src/fp/number/isInt.ts b/src/fp/number/isInt.ts deleted file mode 100644 index 2377046e..00000000 --- a/src/fp/number/isInt.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Checks if a number is a float. - * @param num The number to query - * @returns True if the number is an integer else false - */ -const isInt = (num: number) => { - const reInt = /^-?[0-9]+$/; - return reInt.test(num.toString()); -}; - -export { isInt }; diff --git a/src/fp/number/normalize.ts b/src/fp/number/normalize.ts deleted file mode 100644 index 743639a9..00000000 --- a/src/fp/number/normalize.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @description Normalizes passed in values to 0 and 1 - * @param start - * @param end - */ -const normalize = (num: number, start: number, end: number): number => { - return num * (end - start); -}; - -export { normalize }; diff --git a/src/fp/number/random.ts b/src/fp/number/random.ts deleted file mode 100644 index d29ea33a..00000000 --- a/src/fp/number/random.ts +++ /dev/null @@ -1,12 +0,0 @@ -const random = (min: number, max: number): number => { - if (min > max) { - const mn = min; - const mx = max; - max = mn; - min = mx; - } else { - return Math.random() * (max - min) + min; - } -}; - -export { random }; diff --git a/src/fp/object/colorObj.ts b/src/fp/object/colorObj.ts deleted file mode 100644 index 240fbed1..00000000 --- a/src/fp/object/colorObj.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { callback, Factor, Color } from '../../paramTypes'; - -// @ts-nocheck -const colorObj = (factor: Factor, callback: callback) => (color: Color) => { - return { [factor]: callback(color), name: color }; -}; - -export { colorObj }; diff --git a/src/fp/object/customConcat.ts b/src/fp/object/customConcat.ts deleted file mode 100644 index 922c7496..00000000 --- a/src/fp/object/customConcat.ts +++ /dev/null @@ -1,15 +0,0 @@ -// @ts-nocheck - -const customConcat = (hue: object) => { - const res: number[] = []; - const { keys } = Object; - if (typeof hue == 'object') { - const hueKeys = keys(hue); - - //@ts-ignore - res.push(...hueKeys.map((key) => hue[key])); - } - return res; -}; - -export { customConcat }; diff --git a/src/fp/object/customFindKey.ts b/src/fp/object/customFindKey.ts deleted file mode 100644 index 2e8f58b6..00000000 --- a/src/fp/object/customFindKey.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { inRange } from '../number/inRange.js'; -import { max, min } from '../array/min_max.js'; -import { customConcat } from '../object/customConcat.js'; - -/** - - * - * @private - * @param {Array|Object} collection The collection to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {number} factor The value to compare against - * @returns {*} Returns the found element or its key, else `undefined`. - */ -export const customFindKey = (collection: object, factor: number) => { - // If the color is achromatic return the string gray - const propKeys = Object.keys(collection); - - const result: string = propKeys - .filter((key) => { - const hueVals = customConcat(collection[key]); - // @ts-ignore - const minVal = min(...hueVals); - // @ts-ignore - const maxVal = max(...hueVals); - // Capture the min and max values and see if the passed in color is within that range - return inRange(factor, minVal, maxVal); - }) - .toString(); - - return result; -}; diff --git a/src/fp/object/index.ts b/src/fp/object/index.ts deleted file mode 100644 index bea29240..00000000 --- a/src/fp/object/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This module has object methods - -export { colorObj } from './colorObj'; -export { customConcat } from './customConcat'; -export { customFindKey } from './customFindKey'; diff --git a/src/fp/string/expressionParser.ts b/src/fp/string/expressionParser.ts deleted file mode 100644 index f87b85d9..00000000 --- a/src/fp/string/expressionParser.ts +++ /dev/null @@ -1,40 +0,0 @@ -// @ts-nocheck -import { Color } from '../../paramTypes'; - -/** - * Performs arithmetic operations on colors by passing the arithmetic operator from the value if it is a string. It requires the src variable to be declared in the global scope of the invoking func. - * @param src The color object. - * @param channel The channel to set. - * @param value The value to apply. - */ -function expressionParser(src: Color, channel: string, value: string): number { - // regExp to match arithmetic operator and the value - const reOperator = /^(\*|\+|\-|\/)/; - const reValue = /[0-9]*\.?[0-9]+/; - - // Storing the arithmetic sign and value - const sign = reOperator.exec(value); - const amt = reValue.exec(value); - - const cb = (amt: string) => parseFloat(amt); - - // Match an operator against the first truthy case and perform the relevant math operation - switch (sign['0']) { - case '+': - src[channel] += +cb(amt['0']); - break; - case '-': - src[channel] -= +cb(amt['0']); - break; - case '*': - src[channel] *= +cb(amt['0']); - break; - case '/': - src[channel] /= +cb(amt['0']); - break; - default: - src[channel] = +cb(amt['0']); - } - return src; -} -export { expressionParser }; diff --git a/src/fp/string/index.ts b/src/fp/string/index.ts deleted file mode 100644 index a45c8a6d..00000000 --- a/src/fp/string/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { matchChromaChannel } from './matchChromaChannel'; -export { expressionParser } from './expressionParser'; diff --git a/src/fp/string/matchChromaChannel.ts b/src/fp/string/matchChromaChannel.ts deleted file mode 100644 index 8d37c40d..00000000 --- a/src/fp/string/matchChromaChannel.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { HueColorSpaces } from '../../paramTypes'; - -/** - * @function - * Matches the chroma/saturation channel of any compliant color space - * @param colorSpace The color space to match saturation/chroma channel. - * @returns The mode channel string passed to getChannel() - */ -const matchChromaChannel = (colorSpace: HueColorSpaces | string): string => { - // Matches any string with c or s - const reChroma = /(s|c)/; - const ch = reChroma.exec(colorSpace); - - if (reChroma.test(colorSpace)) { - return `${colorSpace}.${ch[0]}`; - } else { - throw Error( - `The color space ${colorSpace} has no chroma/saturation channel.` - ); - } -}; - -export { matchChromaChannel }; diff --git a/src/generators.ts b/src/generators.ts new file mode 100644 index 00000000..a5c84415 --- /dev/null +++ b/src/generators.ts @@ -0,0 +1,581 @@ +/* eslint-disable prefer-const */ +/** + * @license + * generators.ts - Palette utilities for generating color scales for huetiful-js. + * Contains colors from TailwindCSS released under the MIT permissive licence. + * Contains parts of chroma.js released under the Apache-2.0 license. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { + useMode, + modeLch, + easingSmoothstep, + samples as nativeSamples, + modeJch, + averageNumber, + differenceEuclidean, + interpolate, + interpolatorSplineBasis, + interpolatorSplineBasisClosed, + interpolatorSplineMonotone, + interpolatorSplineMonotoneClosed, + interpolatorSplineNatural, + interpolatorSplineNaturalClosed, + modeHsv, + nearest, + Color +} from 'culori/fn'; + +import type { + ColorToken, + EarthtoneOptions, + HueColorSpaces, + HueShiftOptions, + InterpolatorOptions, + PairedSchemeOptions, + UniformColorSpaces +} from './types'; +import { + adjustHue, + random, + checkArg, + matchChromaChannel, + matchLightnessChannel, + max, + min +} from './helpers'; + +import { toHex, ucsConverter } from './converters'; +import { setChannel } from './utils'; + +/** + * + * Generates a randomised classic color scheme from a single base color. + * @param schemeType Any classic color scheme either "analogous"|"triadic"|"tetradic"|"complementary"|"splitComplementary". + * @returns An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme. + * @example + * + import { base } from 'huetiful-js' + +console.log(base("triadic")("#a1bd2f", true)) +// [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ] + */ + +function scheme( + schemeType: 'analogous' | 'triadic' | 'tetradic' | 'complementary' | string +) { + /** + * @param color The color to use as the starting point. + * @param easingFunc Optional parameter to pass in a custom easing function. The default is smoothstep + */ + return ( + color: ColorToken, + easingFunc?: (t: number) => number + ): ColorToken[] => { + const cb = (iterations: number, distance: number, color: ColorToken) => + nativeSamples(iterations).map((val) => + adjustHue((color['h'] + distance) * (val * easingSmoothstep(val))) + ); + schemeType = schemeType.toLowerCase(); + easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc; + + // @ts-ignore + color = useMode(modeJch)(color); + const lowMin = 0.05, + lowMax = 0.495, + highMin = 0.5, + highMax = 0.995; + const targetHueSteps = { + analogous: cb(3, 12, color), + triadic: cb(3, 120, color), + tetradic: cb(4, 90, color), + complementary: cb(2, 180, color) + }; + // For each step return a random value between lowMin && lowMax multipied by highMin && highMax and 0.9 of the step + for (const scheme of Object.keys(targetHueSteps)) { + targetHueSteps[scheme].map( + (step: number) => + random(step * lowMax, step * lowMin) + + random(step * highMax, step * highMin) / 2 + ); + } + // The map for steps to obtain the targeted palettes + const colors = targetHueSteps[schemeType].map((step: number) => ({ + l: color['l'], + c: color['c'], + h: step * easingFunc(1 / targetHueSteps[schemeType].length), + mode: 'lch' + })); + + return colors.map(toHex); + }; +} + +/** + * + * Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function. + * @param colors The array of colors to create palettes from. Preferably use 5 or more colors for better results. + * @param schemeType (Optional) The palette type you want to return. + * @returns An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results. + * @example + * + * import { discoverPalettes } from 'huetiful-js' + +let sample = [ + "#ffff00", + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#720000", + "#600000", + "#4e0000", + "#3e0000", + "#310000", +] + +console.log(discoverPalettes(sample, "tetradic")) +// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ] + */ +function discoverPalettes( + colors: ColorToken[], + schemeType?: 'analogous' | 'triadic' | 'tetradic' | 'complementary' +): ColorToken[] | object { + const { keys } = Object; + const isColorEqual = (c1: ColorToken, c2: ColorToken): boolean => { + return c1['h'] === c2['h'] && c1['l'] === c2['l'] && c1['c'] === c2['c']; + }; + + const toLch = useMode(modeLch); + colors = colors.map((color) => toLch(toHex(color))); + const palettes = {}; + const schemeKeys = ['analogous', 'triadic', 'tetradic', 'complementary']; + const targetPalettes = {}; + for (const color of colors) { + schemeKeys.forEach((s) => (targetPalettes[s] = scheme(s)(color))); + + for (const paletteType of keys(targetPalettes)) { + const palette = []; + let variance = 0; + + for (const targetColor of targetPalettes[paletteType]) { + // filter out colors already in the palette + const availableColors = colors.filter( + (color1) => !palette.some((color2) => isColorEqual(color1, color2)) + ); + + const match = nearest( + availableColors, + differenceEuclidean('lch') + )(targetColor)[0]; + + // @ts-ignore + variance += differenceEuclidean('lch')(targetColor, match); + + palette.push(match); + } + + if (!palettes[paletteType] || variance < palettes[paletteType].variance) { + palettes[paletteType] = palette.map(toHex); + } + } + } + + if (typeof schemeType === 'string') { + return palettes[schemeType.toLowerCase()]; + } else if (typeof schemeType === 'undefined') { + return palettes; + } else { + throw Error( + `${schemeType} is not a valid scheme. The schemes are triadic | tetradic | analogous | complementary` + ); + } +} + +/** + * + * Creates a scale of a spline based interpolation between an earthtone and a color. + * @param color The color to interpolate an earth tone with. + * @param options Optional overrides for customising interpolation and easing functions. + * @returns The array of colors resulting from the earthtone interpolation as hex codes. + * @example + * + * import { earthtone } from 'huetiful-js' + + +console.log(earthtone("pink",{earthtones:'clay',iterations:5 })) +// [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ] + + */ + +function earthtone( + color: ColorToken, + colorspace?: HueColorSpaces, + options?: EarthtoneOptions +): ColorToken[] { + let { samples: iterations, earthtones } = options || {}; + + iterations = checkArg(iterations, 1) as number; + + earthtones = checkArg(earthtones, 'dark') as typeof earthtones; + const tones = { + 'light-gray': '#e5e5e5', + silver: '#f5f5f5', + sand: '#c2b2a4', + tupe: '#a79e8a', + mahogany: '#958c7c', + 'brick-red': '#7d7065', + clay: '#6a5c52', + cocoa: '#584a3e', + 'dark-brown': '#473b31', + dark: '#352a21' + }; + + const base: ColorToken = tones[earthtones.toLowerCase()]; + + const f = interpolator([base, toHex(color)], colorspace); + + return ((iterations === 1 && toHex(f(0.5))) || + nativeSamples(iterations).map((t) => toHex(f(t)))) as ColorToken[]; +} + +/** + * + * Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme. + * @param color The color to use as the base of the hueshift. Colors are internally converted to LCH. + * @param options The optional overrides object to customize per channel options like interpolation methods and channel fixups. + *@returns An array of colors in hex. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1 + * @example + * import { hueShift } from "huetiful-js"; + +let hueShiftedPalette = hueShift("#3e0000"); + +console.log(hueShiftedPalette); + +// [ + '#ffffe1', '#ffdca5', + '#ca9a70', '#935c40', + '#5c2418', '#3e0000', + '#310000', '#34000f', + '#38001e', '#3b002c', + '#3b0c3a' +] + */ + +function hueShift( + color: ColorToken, + colorspace?: UniformColorSpaces, + options?: HueShiftOptions +): ColorToken[] { + const lightnessMapper = + (n: number) => + (start1: number, end1: number) => + (start2: number, end2: number) => + ((n - start1) / (end1 - start1)) * (end2 - start2) + start2; + + color = ucsConverter(colorspace)(color as string); + + let { + samples: iterations, + hueStep, + minLightness, + maxLightness, + easingFunc + } = options || {}; + const [l, c] = [ + matchLightnessChannel(colorspace).split('.')[1], + matchChromaChannel(colorspace).split('.')[1] + ]; + // Pass default values in case the options object is overridden + easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc; + iterations = (checkArg(iterations, 6) as number) + 1; + hueStep = checkArg(hueStep, 5) as number; + (minLightness = checkArg(minLightness, 10) as number), + (maxLightness = checkArg(maxLightness, 90) as number); + // Pass in default values if any of the opts is undefined + const palette: ColorToken[] = [color]; + // Maximum number of iterations possible. + //Each iteration add a darker shade to the start of the array and a lighter tint to the end. + for (let i = 1; i < iterations; i++) { + //adjustHue checks hue values are clamped. + + // Here we use lightnessMapper to calculate our lightness values which takes a number that exists in range [0,1]. + + const [colorShiftDown, colorShiftUp] = [ + { + [l]: lightnessMapper(i)(0.1, iterations)(color[l], minLightness), + [c]: color[c], + h: adjustHue(color['h'] - hueStep * (i * easingFunc(i))), + mode: colorspace + }, + { + [l]: lightnessMapper(i)(0.15, iterations)(color[l], maxLightness), + [c]: color[c], + h: adjustHue(color['h'] + hueStep * (i * easingFunc(i))), + mode: colorspace + } + ]; + palette.push(colorShiftUp); + palette.unshift(colorShiftDown); + } + return Array.from(new Set(palette)).map(toHex); +} +/** + * + * Returns a spline based interpolator function with customizable interpolation methods (passed in as 'kind') and optional channel specific overrides. + * @param colors The array of colors to interpolate. If a color has a falsy channel for example black has an undefined hue channel some interpolation methods may return NaN affecting the final result. + * @param colorspace The colorspace to perform the color space in. Prefer uniform color spaces for better results such as Lch or Jch. + * @param kind The type of the spline interpolation method. Default is basis. + * @param closed Optional parameter to return the 'closed' variant of the 'kind' of interpolation method which can be useful for cyclical color scales. Default is false + * @param options Optional channel specific overrides. + * @returns A hexadecimal representation of the resultant color. + */ +function interpolateSpline( + colors: ColorToken[], + colorspace?: HueColorSpaces, + samples?: number, + kind?: 'natural' | 'monotone' | 'basis', + closed = false, + options?: InterpolatorOptions +): ColorToken[] { + let { + chromaInterpolator, + hueFixup, + hueInterpolator, + lightnessInterpolator, + easingFunc + } = checkArg(options, {}) as InterpolatorOptions; + // Set the internal defaults + easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc; + kind = checkArg(kind, 'basis') as typeof kind; + + let func; + switch (kind) { + case 'basis': + func = + (closed && interpolatorSplineBasisClosed) || interpolatorSplineBasis; + break; + case 'monotone': + func = + (closed && interpolatorSplineMonotoneClosed) || + interpolatorSplineMonotone; + break; + case 'natural': + func = + (closed && interpolatorSplineNaturalClosed) || + interpolatorSplineNatural; + break; + } + // @ts-ignore + let f = interpolate([...colors, easingFunc], colorspace, { + h: { + //@ts-ignore + fixup: hueFixup, + use: checkArg(hueInterpolator, func) + }, + [matchChromaChannel(colorspace as string)]: { + use: checkArg(chromaInterpolator, func) + }, + [matchLightnessChannel(colorspace as string)]: { + use: checkArg(lightnessInterpolator, func) + } + }); + + // make sure samples is an absolute integer + samples = + (typeof samples === 'number' && samples >= 1 && samples) || + Math.ceil(Math.abs(samples)); + + let result: string[]; + if (samples > 1) { + result = nativeSamples(samples).map((s) => toHex(f(s))); + } else { + //@ts-ignore + result = result.push(toHex(f(0.5))); + } + return result; +} + +function interpolator( + colors: ColorToken[], + colorspace?: HueColorSpaces, + options?: object +) { + let { + chromaInterpolator, + hueFixup, + hueInterpolator, + lightnessInterpolator, + easingFunc + } = checkArg(options, {}) as InterpolatorOptions; + return interpolate( + [ + ...(colors as Array), + checkArg(easingFunc, interpolator['easingFunc']) as typeof easingFunc + ], + checkArg(colorspace, 'jch') as typeof colorspace, + { + //@ts-ignore + h: { + fixup: hueFixup, + + // @ts-ignore + use: checkArg(hueInterpolator, interpolator['hueInterpolator']) + }, + [matchChromaChannel(colorspace)]: { + use: checkArg(chromaInterpolator, interpolator['chromaInterpolator']) + }, + [matchLightnessChannel(colorspace)]: { + use: checkArg( + lightnessInterpolator, + interpolator['lightnessInterpolator'] + ) + } + } + ); +} + +/** + * pairedScheme + * Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black. + * @param color The color to return a paired color scheme from. + * @param options The optional overrides object to customize per channel options like interpolation methods and channel fixups. + * @returns An array containing the paired scheme. + * @example + * + * import { pairedScheme } from 'huetiful-js' + +console.log(pairedScheme("green",{hueStep:6,iterations:4,tone:'dark'})) +// [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ] + */ +function pairedScheme( + color: ColorToken, + options?: PairedSchemeOptions +): ColorToken[] | ColorToken { + // eslint-disable-next-line prefer-const + let { samples: iterations, via, hueStep, easingFunc } = options || {}; + + iterations = checkArg(iterations, 1) as number; + easingFunc = checkArg(easingFunc, easingSmoothstep) as typeof easingFunc; + via = checkArg(via, 'light') as typeof via; + hueStep = checkArg(hueStep, 5) as number; + + const toLch = useMode(modeLch); + color = toLch(toHex(color)); + + // get the hue of the passed in color and add it to the step which will result in the final color to pair with + const derivedHue = setChannel('lch.h')(color, color['h'] + hueStep); + + // Set the tones to color objects with hardcoded hue values and lightness channels clamped at extremes + const tones = { + dark: { l: 0, c: 0, h: 0, mode: 'lch65' }, + light: { l: 100, c: 0, h: 0, mode: 'lch65' } + }; + + const scale = interpolate( + [color as unknown as string, tones[via as string], derivedHue, easingFunc], + 'lch', + checkArg(options, interpolator) + ); + + if (iterations <= 1) { + return toHex(scale(0.5)); + } else { + // Declare the num of iterations in samples() which will be used as the t value + // Since the interpolation returns half duplicate values we double the sample value + // Guard the num param against negative values and floats + const smp = nativeSamples(iterations * 2); + + //The array to capture the different iterations + const results: ColorToken[] = smp.map((t) => toHex(scale(easingFunc(t)))); + // Return a slice of the array from the start to the half length of the array + return results.slice(0, results.length / 2); + } +} + +/** + * + * Returns a random pastel variant of the passed in color. + * @param color The color to return a pastel variant of. + * @returns A random pastel color. + * @example + * + * +import { pastel } from 'huetiful-js' + +console.log(pastel("green")) +// #036103ff + */ +function pastel(color: ColorToken): ColorToken { + const samplePastelObj = [ + { + color: '#fea3aa', + saturation: 0.35826771653543305, + value: 0.996078431372549 + }, + { + color: '#f8b88b', + saturation: 0.43951612903225806, + value: 0.9725490196078431 + }, + { color: '#faf884', saturation: 0.472, value: 0.9803921568627451 }, + { + color: '#f2a2e8', + saturation: 0.3305785123966942, + value: 0.9490196078431372 + }, + { + color: '#b2cefe', + saturation: 0.2992125984251969, + value: 0.996078431372549 + }, + { + color: '#baed91', + saturation: 0.3881856540084388, + value: 0.9294117647058824 + } + ]; + + const sampleSaturation = samplePastelObj.map((el) => el['saturation']); + const sampleValues = samplePastelObj.map((el) => el['value']); + + const pastelSample = { + averageSaturation: averageNumber(sampleValues), + averageValue: averageNumber(sampleSaturation), + minSampleSaturation: min(sampleSaturation), + maxSampleSaturation: max(sampleSaturation), + minSampleValue: min(sampleValues), + maxSampleValue: max(sampleValues) + }; + + const toHsv = useMode(modeHsv); + color = toHsv(toHex(color)); + // For now we're simply returning an hsv object with the s and v channel set to the averages + return toHex({ + h: color['h'], + s: pastelSample['averageSaturation'], + v: random(pastelSample['minSampleValue'], pastelSample['maxSampleValue']), + mode: 'hsv' + }); +} + +export { + discoverPalettes, + hueShift, + pairedScheme, + pastel, + scheme, + interpolateSpline, + interpolator, + earthtone, + ucsConverter +}; diff --git a/src/getters_and_setters/alpha.ts b/src/getters_and_setters/alpha.ts deleted file mode 100644 index 49b4d3ac..00000000 --- a/src/getters_and_setters/alpha.ts +++ /dev/null @@ -1,50 +0,0 @@ -//@ts-nocheck -import { useMode, modeLch } from 'culori/fn'; -import { inRange } from '../fp/number/inRange.js'; -import { expressionParser } from '../fp/string/expressionParser.js'; -import { toHex } from '../converters/toHex.js'; -import type { Color } from '../paramTypes.js'; - -/** - * @function - * @description Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted - * @param color The color with the targeted opacity/alpha channel. - * @param value The value to apply to the opacity channel. The value is between [0,1] - * @returns color The resulting color. Returns an 8 character hex code. - * @example - * - * // Getting the alpha -console.log(alpha('#a1bd2f0d')) -// 0.050980392156862744 - -// Setting the alpha - -let myColor = alpha('b2c3f1', 0.5) - -console.log(myColor) - -// #b2c3f180 - */ - -const alpha = (color: Color, value?: number | string): Color | number => { - // We never perfom an operation on an undefined color. Defaults to pure black - color = color || 'black'; - - const channel = 'alpha'; - const lch = useMode(modeLch); - const src: Color = lch(toHex(color)); - if (typeof value === 'undefined' || null) { - return src[channel]; - } else if (typeof value === 'number') { - if (inRange(value, 0, 1)) { - src[channel] = value; - } else { - src[channel] = value / 100; - } - } else if (typeof value === 'string') { - expressionParser(src, channel, value); - } - return formatHex8(src); -}; - -export { alpha }; diff --git a/src/getters_and_setters/darken.ts b/src/getters_and_setters/darken.ts deleted file mode 100644 index 96c3169a..00000000 --- a/src/getters_and_setters/darken.ts +++ /dev/null @@ -1,55 +0,0 @@ -//@ts-nocheck -import { easingSmootherstep, modeLab, useMode } from 'culori/fn'; -import { toHex } from '../converters/toHex'; -import { expressionParser } from '../fp/string/expressionParser'; -import type { Color } from '../paramTypes'; -// ported froma chroma-js brighten -const toLab = useMode(modeLab); -/** - * @function - * @description Darkens the color by reducing the lightness channel. . - * @param color The color to darken. - * @param value The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") - * @returns color The darkened color. - * @example - * - * - - */ -const darken = (color: Color, value: number | string): Color => { - const Kn = 18; - const channel = 'l'; - - const src = toLab(toHex(color)); - - if (typeof value === 'number') { - src['l'] -= Kn * easingSmootherstep(value / 100); - } else if (typeof value === 'string') { - expressionParser(src, channel, value || 1); - } - - return toHex(src); -}; - -/** - * - * @param color The color to brighten. - * @param value The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") - * @param mode The color space to compute the color in. Any color space with a lightness channel will do (including HWB) - * @returns - */ -const brighten = (color: Color, value: number | string): Color => { - const src = toLab(toHex(color)); - const channel = 'l'; - - if (typeof value == 'number') { - value = Math.abs(value); - src['l'] -= Kn * easingSmootherstep(value / 100); - } else if (typeof value == 'string') { - expressionParser(src, channel, value); - } - - return toHex(src); -}; - -export { brighten, darken }; diff --git a/src/getters_and_setters/get.ts b/src/getters_and_setters/get.ts deleted file mode 100644 index 9c8c08a7..00000000 --- a/src/getters_and_setters/get.ts +++ /dev/null @@ -1,34 +0,0 @@ -//@ts-nocheck - -import { converter } from 'culori/fn'; -import 'culori/css'; -import { toHex } from '../converters/toHex.ts'; -import type { Color } from '../paramTypes'; - -/** - * @function - * @description Gets the value specifified channel on the color. - * @param mc The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color. - * @param color The color being queried. - * @returns value The value of the queried channel. - * @example - * - * import { getChannel } from 'huetiful-js' - -console.log(getChannel('rgb.g')('#a1bd2f')) -// 0.7411764705882353 - * */ -const getChannel = - (mc: string) => - (color: Color): number => { - const [mode, channel] = mc.split('.'); - const src = converter(mode)(toHex(color)); - - if (channel) { - return src[channel]; - } else { - throw Error(`unknown channel ${channel} in mode ${mode}`); - } - }; - -export { getChannel }; diff --git a/src/getters_and_setters/index.ts b/src/getters_and_setters/index.ts deleted file mode 100644 index 2b1caf28..00000000 --- a/src/getters_and_setters/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { alpha } from './alpha'; -export { brighten, darken } from './darken'; -export { getChannel } from './get'; -export { setLuminance, getLuminance } from './luminance'; -export { setChannel } from './set'; diff --git a/src/getters_and_setters/luminance.ts b/src/getters_and_setters/luminance.ts deleted file mode 100644 index 767428de..00000000 --- a/src/getters_and_setters/luminance.ts +++ /dev/null @@ -1,102 +0,0 @@ -//@ts-nocheck -import { interpolate, wcagLuminance, useMode, modeRgb } from 'culori/fn'; -import type { Color } from '../paramTypes.js'; -import { toHex } from '../converters/toHex.js'; -/** @alias - * Gets the luminance value of that color as defined by WCAG. - * @param color The color to query. - * @returns value The color's luminance value. - * @example - * - * import { getLuminance } from 'huetiful-js' - -console.log(getLuminance('#a1bd2f')) -// 0.4417749513730954 - */ -const getLuminance = (color: Color): number => wcagLuminance(hex(color)); - -const { pow, abs } = Math; -const toRgb = useMode(modeRgb); -/** - * @function - * @description Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance). - * @param color The color to set luminance - * @param lum The amount of luminance to set. The value range is normalised between [0,1] - * @returns The mutated color with the modified properties. - * @example - * - * import { setLuminance, getLuminance } from 'huetiful-js' - -let myColor = setLuminance('#a1bd2f', 0.5) - -console.log(getLuminance(myColor)) -// 0.4999999136285792 - */ -const setLuminance = (color: Color, lum: number): Color => { - const white = '#ffffff', - black = '#000000'; - - const EPS = 1e-7; - let MAX_ITER = 20; - - if (lum !== undefined && typeof lum == 'number') { - (lum == 0 && lum) || black || (lum == 1 && !lum) || white; - - // compute new color using... - - const cur_lum = wcagLuminance(color); - - color = toRgb(hex(color)); - - const test = (low: Color, high: Color) => { - //Must add the overrides object to change parameters like easings, fixups, and the mode to perform the computations in. - const mid = interpolate([low, high])(0.5); - const lm = wcagLuminance(mid); - if (abs(lum - lm > EPS) || !MAX_ITER--) { - // close enough - return mid; - } - - if (lm > lum) { - return test(low, mid); - } else { - return test(mid, high); - } - }; - - let rgb: Color; - if (cur_lum > lum) { - rgb = test(black, color); - } else { - rgb = test(color, white); - } - color = rgb; - return color; - } - // spreading the array values (r,g,b) - - return rgb2luminance(color); -}; - -const rgb2luminance = (color: Color): number => { - color = toRgb(toHex(color)); - - // relative luminance - // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef - return ( - 0.7152 * luminance_x(color['g']) + - 0.2126 * luminance_x(color['r']) + - 0.0722 * luminance_x(color['b']) - ); -}; - -const luminance_x = (x: number) => { - x /= 255; - if (x <= 0.03928) { - return x / 12.92; - } else { - return pow((x + 0.055) / 1.055, 2.4); - } -}; - -export { setLuminance, getLuminance }; diff --git a/src/getters_and_setters/set.ts b/src/getters_and_setters/set.ts deleted file mode 100644 index 98196f47..00000000 --- a/src/getters_and_setters/set.ts +++ /dev/null @@ -1,49 +0,0 @@ -//@ts-nocheck - -// ported from chroma-js Color.set - -import { converter } from 'culori/fn'; -import 'culori/css'; -import { toHex } from '../converters/toHex.js'; -import { expressionParser } from '../fp/string/expressionParser.js'; -import type { Color } from '../paramTypes.js'; -/** - * @function - *@description Sets the value for the specified channel in a color. - * @param color Any recognizable color token. - * @param mc The mode and channel to work with. For example 'rgb.b'. - * @param value The value to set on the queried channel. Also supports expressions as strings e.g set('lch.c)("#fc23a1","*0.5") - * @returns color The mutated color. - * - * @example - * - * import { setChannel } from 'huetiful-js' - -let myColor = setChannel('lch.h')('green',10) - -console.log(getChannel('lch.h')(myColor)) -// 10 - */ - -const setChannel = - (mc: string) => - (color: Color, value: number | string): Color => { - const [mode, channel] = mc.split('.'); - const src: Color = converter(mode)(toHex(color)); - - if (channel) { - if (typeof value === 'number') { - src[channel] = value; - } else if (typeof value === 'string') { - expressionParser(src, channel, value); - } else { - throw new Error(`unsupported value for setChannel`); - } - - return src; - } else { - throw new Error(`unknown channel ${channel} in mode ${mode}`); - } - }; - -export { setChannel }; diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 00000000..56760108 --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,668 @@ +/* eslint-disable no-ternary */ +/** + * @preserve + * @license + * helpers.ts - Helper functions for huetiful-js. + * Contains colors from TailwindCSS released under the MIT permissive licence. + * Contains parts of chroma.js released under the Apache-2.0 license. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import { getChannel } from './utils'; +import type { + callback, + Factor, + ColorToken, + HueColorSpaces, + Order, + Options, + Colorspaces +} from './types'; +import modeRanges from './color-maps/samples/modeRanges'; + +import { + interpolatorSplineNatural, + fixupHueShorter, + interpolatorSplineBasisClosed, + interpolatorSplineMonotone, + easingSmoothstep +} from 'culori/fn'; +import { ucsConverter } from './generators'; + +/** + * @internal + * Returns the first truthy value. + * @param arg The value to check + * @param def The value to cast if arg is falsy + * @returns The first truthy value + */ +function checkArg(arg: unknown, def: unknown): unknown { + return arg || def; +} + +// Global interpolator options defaults +let { + chromaInterpolator, + easingFunc, + hueFixup, + hueInterpolator, + lightnessInterpolator +}: Options = {}; + +chromaInterpolator = interpolatorSplineNatural; +hueFixup = fixupHueShorter; +hueInterpolator = interpolatorSplineBasisClosed; +easingFunc = easingSmoothstep; +lightnessInterpolator = interpolatorSplineMonotone; + +const interpolatorConfig = { + easingFunc, + chromaInterpolator, + hueFixup, + hueInterpolator, + lightnessInterpolator +}; + +/** + * @internal + * Gets the clipped string of a passed in colorspace by removing non-channel characters. + * @param colorspace The colorspace to get the channel keys. + * @param index Optional index to return a single specified channel. + * @returns A string. + * + @example + + console.log(getModeChannel("oklch")); +// lch + +console.log(getModeChannel("okhsl", 2)); +// l + + */ +function getModeChannel(colorspace: Colorspaces | string, index?: number) { + const result = colorspace.substring(colorspace.length - 3); + + return (index && result.charAt(index)) || result; +} + +/** + * @internal + * Takes an arithmetic operator followed by a value and passes the result of the expression to the specified channel. Currently supports addition,subtraction,division and multiplication symbols only. + * @param color The color. + * @param modeChannel The colorspace channel to set. + * @param expression The expression assignment as a string. + * @example + * + * console.log(lch('blue')); +// { mode: 'lch',l: 29.568297153444703,c: 131.2014771995311,h: 301.36428148973533} + +console.log(expressionParser('blue', 'lch.l', '*0.3')); +// { mode: 'lch',l: 8.87048914603341,c: 131.2014771995311,h: 301.36428148973533 } + + */ +function expressionParser( + color: ColorToken, + modeChannel: string, + expression: string +): number { + // regExp to match arithmetic operator and the value + const sign = /^(\*|\+|\-|\/)/.exec(expression)['0'], + value = /[0-9]*\.?[0-9]+/.exec(expression)['0']; + const [mode, channel] = modeChannel.split('.'); + + // @ts-ignore + color = ucsConverter(mode.toString().toLowerCase())(color); + const cb = (value: string) => parseFloat(value); + + // Match an operator against the first truthy case and perform the relevant math operation + switch (sign) { + case '+': + color[channel] += +cb(value); + break; + case '-': + color[channel] -= +cb(value); + break; + case '*': + color[channel] *= +cb(value); + break; + case '/': + color[channel] /= +cb(value); + break; + // throw error alert + } + + // @ts-ignore + return color; +} + +/** + * @internal + * @function + * Matches the chroma/saturation channel of any compliant color space + * @param colorspace The color space to match saturation/chroma channel. + * @returns The mode channel string passed to getChannel() + * @example + * + * import { matchChromaChannel } from 'huetiful-js' + * console.log(matchChromaChannel("jch")); +// jch.c + +console.log(matchChromaChannel("okhsl")); +// okhsl.s + */ +function matchChromaChannel(colorspace: HueColorSpaces | string): string { + // Matches any string with c or s + colorspace = checkArg(colorspace, 'jch') as HueColorSpaces; + const reChroma = /(s|c)/i; + // @ts-ignore + const ch = reChroma.exec(colorspace)['0']; + + // @ts-ignore + if (reChroma.test(colorspace)) { + return `${colorspace}.${ch}`; + } else { + throw Error( + `The color space ${colorspace} has no chroma/saturation channel.` + ); + } +} + +/** + * @internal + * @function + * Matches the lightness channel of any compliant color space + * @param colorspace The color space to match lightness channel. + * @returns The mode channel string passed to getChannel + * + * @example + * + * console.log(matchLightnessChannel("jch")); +// jch.j + +console.log(matchLightnessChannel("okhsl")); +// okhsl.l + */ +function matchLightnessChannel(colorspace: HueColorSpaces | string): string { + // Matches any string with c or s + colorspace = checkArg(colorspace, 'jch') as HueColorSpaces; + const reLightness = /(j|l)/i; + // @ts-ignore + const ch = reLightness.exec(colorspace)['0']; + + // @ts-ignore + if (reLightness.test(colorspace)) { + // @ts-ignore + return `${colorspace}.${ch}`; + } else { + throw Error(`The color space ${colorspace} has no lightness channel.`); + } +} + +function colorObj(factor: Factor, callback: callback) { + return (color: ColorToken) => { + // @ts-ignore + return { [factor]: callback(color), color: color }; + }; +} + +/** + * @internal + + * + * @private + * @param {Array|Object} collection The collection to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} factor The value to compare against + * @returns {*} Returns the found element or its key, else `undefined`. + */ +function customFindKey(collection: object, factor: number) { + // If the color is achromatic return the string gray + const propKeys = Object.keys(collection); + + const result: string | undefined = propKeys + .filter((key) => { + const hueVals = customConcat(collection[key]); + // @ts-ignore + const minVal = min(...hueVals); + // @ts-ignore + const maxVal = max(...hueVals); + // Capture the min and max values and see if the passed in color is within that range + return inRange(factor, minVal, maxVal); + }) + .toString(); + + return result; +} + +function customConcat(hue: object): number[] { + const res = []; + const { keys } = Object; + if (typeof hue == 'object') { + const hueKeys = keys(hue); + + //@ts-ignore + res.push(...hueKeys.map((key) => hue[key])); + } + // @ts-ignore + return res.flat(1); +} + +/** + * @internal + * + * @param value The hue angle to normalize. + * @returns The normalized hue angle or passed in value if it was within [0,360] + * + * @example + * + * console.log(adjustHue(4)); +// 4 + +console.log(adjustHue(444)); +// 84 + */ +function adjustHue(value: number) { + return (value > 0 && (value += Math.ceil(-value / 360) * 360)) || value % 360; +} + +/** + * @internal + * Returns the channel value difference between the passed in colors. They are both converted to the colorspace in the modeChannel parameter before values are computed. + * @param color The color to subtract values from/ + * @param modeChannel The colorspace and channel string to perform the operation in. + * @returns The difference between the color channel(s) + * @example + * + * + */ +function channelDifference(color: ColorToken, modeChannel: string) { + /** + * @internal + * @param subtrahend The color to use as subtrahend + */ + return (subtrahend: ColorToken) => { + const cb = (color: ColorToken) => getChannel(modeChannel)(color); + if (cb(color) < cb(subtrahend)) { + return cb(subtrahend) - cb(color); + } else { + return cb(color) - cb(subtrahend); + } + }; +} + +// Comparison operators +function gt(x: number, y: number): boolean { + return x > y; +} +function lt(x: number, y: number): boolean { + return x < y; +} +function gte(x: number, y: number): boolean { + return x >= y; +} +function lte(x: number, y: number): boolean { + return x <= y; +} +function eq(x: number, y: number): boolean { + return x === y; +} + +/** + * @internal + * @function + * Checks if a value is within the start and end range. + * @param number The number to check. + * @param start The minimum or starting value. + * @param end The maximum or starting value. + * @returns True if the number is in range else false. + */ + +function inRange(number: number, start: number, end?: number): boolean { + /* Built-in method references for those with the same name as other `lodash` methods. */ + Math.min; + return number >= Math.min(start, end) && number < Math.max(start, end); +} + +/** + * @internal + * Checks if a number is an integer or float. + * @param num The number to query + * @returns True if the number is an integer else false if it is a float. + */ +function isInteger(num: number | string) { + const reInt = /^-?[0-9]+$/; + return reInt.test(num.toString()); +} + +/** + * @internal + * @function + * Normalizes passed in channel value to a range accepted by color spaces as defined in Culori. + * @param value The value to chec if its in the accepted range for the passed in mode channel + * @param modeChannel A string defining the mode and channel ranges to use for comparison + * @returns The normalized channel value or the passed in value if it was within range + */ +function normalize(value: number, modeChannel: string): number { + const [mode, channel]: string[] = modeChannel.split('.'); + const [start, end]: number[] = modeRanges[mode][channel]; + const range = inRange(value, start, end); + + if (!range) { + if (inRange(value, 0, 1)) { + value = end * value; + } else if (inRange(value, 1, 150)) { + value = end * (value / 100); + } else { + throw Error( + `The value ${value} is out of range for channel ${channel} of colorspace ${mode} can only accept a value between [0,1] or [0,100] with 0.5 or 50 being half of the channel range.` + ); + } + } + return value; +} + +/** + * @internal + * @function + * Returns a random number between minimum and maximum bounds. + * @param min The lower bound. + * @param max The upper bound. + * @returns A number. + */ +function random(min: number, max: number): number { + if (min > max) { + const mn = min; + const mx = max; + max = mn; + min = mx; + } else { + return Math.random() * (max - min) + min; + } +} + +const { ceil, floor } = Math; +/** + * @internal + * @function + * Rounds up or down a number based on the float value. + * @param num The number to round up or down. + * @returns An integer + * @example + * console.log(floorCeil(1.45)); +// 1 +console.log(floorCeil(1.501)); +// 2 + + */ + +function floorCeil(num: number): number { + if (!isInteger(num)) { + const strArr = num.toString().split('.'); + const float = strArr[1]; + + //If the decimal value is .4 and below it will be rounded down else it will be rounded up. + const reFloorCeil = (float: string) => /^[0-4]$/.test(float.charAt(0)); + + if (reFloorCeil(float)) { + num = floor(num); + } else { + num = ceil(num); + } + } + + return num; +} + +/** + * @internal + * Helper function for native sorting method for arrays. + * @param factor The property to query. + * @param order Either ascending or descending. + * @returns A sorted array. + */ +function customSort(order: Order, factor?: Factor | string) { + // Special thanks to deechris27 on youtube + // a-b gives asc order & b-a gives desc order + factor = factor || 'factor'; + return (a, b) => { + if (order === 'asc') { + return a[factor] - b[factor]; + } else if (order === 'desc') { + return b[factor] - a[factor]; + } + }; +} + +/* + * @function + * Creates a custom object with a factor to pass to the predicate function. + * @param factor The quality being queried. + * @param cb The callback function for computing the factor's start. + * @returns An array of objects. + */ +function colorObjArr(factor: Factor, callback) { + /** + * @internal + * + * @param colors The array or object of colors to iterate over. If an object is passed, its values are expected to be valid color tokens. + */ + return ( + collection: ColorToken[] | object | object + ): Array<{ factor: Factor; color: ColorToken }> => { + const cb = colorObj(factor, callback); + // @ts-ignore + return Object.keys(collection).map((color) => cb(colors[color])); + }; +} + +/** + * @internal + * Returns the passed in value + * @param value The value to return + * @returns + */ +function identity(value) { + return value; +} +function baseExtremum(array: number[], iteratee, comparator) { + var [index, length] = [-1, array.length]; + + while (++index < length) { + var [value, current] = [array[index], iteratee(value)]; + + if ( + current != null && + (computed === undefined + ? current === current + : comparator(current, computed)) + ) { + var computed = current, + result = value; + } + } + return result; +} +/** + * @internal + * Gets the smallest value in an array + * @param array The array to retrieve minimum value + * @returns The smallest number in the array + * @example + * console.log(min([0, 3, 4])); +// 0 + * + */ +function min(array: number[]): number { + return ( + (array && array.length && baseExtremum(array, identity, lt)) || undefined + ); +} +/** + * @internal + * Gets the largest value in an array + * @param array The array to retrieve maximum value + * @returns The largest number in the array + * @example + * console.log(max([0, 3, 4])); +// 4 + */ +function max(array: number[]): number { + return ( + (array && array.length && baseExtremum(array, identity, gt)) || undefined + ); +} + +/** + * @internal + * Gets the digits in the expression string + * @param s Thestring to match + * @returns The matched digits, if any, as a string. + */ +function matchDigits(s: string): string { + s = s.toString(); + var reDigits = /[0-9]*\.?[0-9]+/; + return (reDigits.test(s) && reDigits.exec(s)['0']) || undefined; +} + +/** + * @internal + * Matches the comparison symbols used in the expression string. + * @param s The string to match. + * @returns The matched comparator, if any, as a string. + */ +function matchComparator(s: string): string { + s = s.toString(); + var reComparator = /^(>=|<=|<|>)/; + + return (reComparator.test(s) && reComparator.exec(s)['0']) || undefined; +} + +/** + * @internal + * Filters an array of color objects with a "factor" property whose value is determined by a predicate or getter via the cb param. + * @param factor The property to query + * @param callback The function to use for comparison. + * @returns An array of colors or color objects. + */ +function sortedArr( + factor: Factor, + callback: callback, + order: Order, + colorObj = false +) { + return (collection: ColorToken[] | object) => { + const results: ColorToken[] | Array<{ factor: number; color: ColorToken }> = + colorObjArr(factor, callback)(collection); + + // Assign the value of colorObj to results variable + // Sort the array using our customSort helper function + results.sort(customSort(order, factor)); + + // colorObj parameter is true return the array of color objects + // else just return the color's name value. + if (colorObj) { + return results; + } else { + return results.map((color) => color['color']); + } + }; +} + +/** + * @internal + * Filters an array according to the value of a color's queried factor + * @param factor The property to query and use as filtering criteria + * @param cb The function to use for comparison + * @returns The filtered array + */ +function filteredArr(factor: Factor, cb?: callback) { + return ( + collection: ColorToken[] | object, + start: number | string, + end?: number + ): ColorToken[] => { + let result: ColorToken[]; + + if (typeof start === 'number') { + result = colorObjArr( + factor, + cb + )(collection) + .filter((color) => inRange(color[factor], start, end)) + .map((color) => color['color']); + + // If string split the the string to an array of signature [sign,value] with sign being the type of predicate returned to mapFilter. + } else if (typeof start === 'string') { + //The patterns to match + + const val = matchDigits(start), + op = matchComparator(start); + + const mapFilter = ( + test: (x: number, y: number) => boolean + ): ColorToken[] => { + return colorObjArr( + factor, + cb + )(collection) + .filter((el) => test(el[factor], parseFloat(val))) + .map((el) => el['color']); + }; + switch (op) { + case '<': + result = mapFilter(lt); + + break; + case '>': + result = mapFilter(gt); + break; + case '<=': + result = mapFilter(lte); + break; + case '>=': + result = mapFilter(gte); + break; + } + } + return result; + }; +} + +export { + expressionParser, + matchLightnessChannel, + matchChromaChannel, + min, + max, + customSort, + colorObjArr, + sortedArr, + filteredArr, + customFindKey, + colorObj, + customConcat, + inRange, + random, + isInteger, + floorCeil, + adjustHue, + channelDifference, + lt, + gt, + gte, + lte, + eq, + normalize, + checkArg, + getModeChannel, + interpolatorConfig, + matchComparator, + matchDigits +}; diff --git a/src/index.ts b/src/index.ts index 1101299d..8eeb97a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ export * from './colors'; -export * from './getters_and_setters'; -export * from './palettes'; +export * from './utils'; +export * from './generators'; export * from './filterBy'; export * from './sortBy'; -export * from './fp'; export * from './converters'; +export * from './helpers'; diff --git a/src/palettes/adaptive.ts b/src/palettes/adaptive.ts deleted file mode 100644 index 2b510e9c..00000000 --- a/src/palettes/adaptive.ts +++ /dev/null @@ -1,3 +0,0 @@ -// @ts-nocheck - -// This module will make use of contrast ratio to create adaptive palettes diff --git a/src/palettes/base.ts b/src/palettes/base.ts deleted file mode 100644 index 8a974a7e..00000000 --- a/src/palettes/base.ts +++ /dev/null @@ -1,71 +0,0 @@ -//@ts-nocheck -import { useMode, modeLch, easingSmoothstep, samples } from 'culori/fn'; - -import type { Color } from '../paramTypes.ts'; -import { adjustHue } from '../fp/number/adjustHue.ts'; -import { random } from '../fp/number/random.ts'; -import { toHex } from '../converters/toHex.ts'; -// Globals -const cb = (iterations: number, distance: number, color: Color) => - samples(iterations).map((val) => - adjustHue((color['h'] + distance) * (val * easingSmoothstep(val))) - ); - -/** - * @function - * @description Generates a randomised classic color scheme from a single base color. - * @param scheme Any classic color scheme either "analogous"|"triadic"|"tetradic"|"complementary"|"splitComplementary". - * @param hex Optional boolen to return lch color objects or hex codes in the result array. Default is false which returns LCH color objects. - * @param easingFunc Optional parameter to pass in a custom easing function. The default is smoothstep - * @returns An array of 8 character hex codes. Elements in the array depend on the number of sample colors in the targeted scheme. - * @example - * - import { base } from 'huetiful-js' - -console.log(base("triadic")("#a1bd2f", true)) -// [ '#a1bd2fff', '#00caffff', '#ff78c9ff' ] - */ - -const base = - (scheme: 'analogous' | 'triadic' | 'tetradic' | 'complementary') => - (color: Color, hex = false, easingFunc: (t: number) => number): Color[] => { - scheme = scheme.toLowerCase(); - easingFunc = easingFunc || easingSmoothstep; - // Converting the color to lch - const lch = useMode(modeLch); - color = lch(color); - const lowMin = 0.05, - lowMax = 0.495, - highMin = 0.5, - highMax = 0.995; - const targetHueSteps = { - analogous: cb(3, 12, color), - triadic: cb(3, 120, color), - tetradic: cb(4, 90, color), - complementary: cb(2, 180, color) - }; - // For each step return a random value between lowMin && lowMax multipied by highMin && highMax and 0.9 of the step - for (const scheme of Object.keys(targetHueSteps)) { - targetHueSteps[scheme].map( - (step: number) => - random(step * lowMax, step * lowMin) + - random(step * highMax, step * highMin) / 2 - ); - } - // The map for steps to obtain the targeted palettes - - const colors = targetHueSteps[scheme].map((step: number) => ({ - l: color['l'], - c: color['c'], - h: step * easingFunc(1 / targetHueSteps[scheme].length), - mode: 'lch' - })); - - if (hex) { - return colors.map(toHex); - } else { - return colors; - } - }; - -export { base }; diff --git a/src/palettes/discoverPalettes.ts b/src/palettes/discoverPalettes.ts deleted file mode 100644 index 6208f8e2..00000000 --- a/src/palettes/discoverPalettes.ts +++ /dev/null @@ -1,88 +0,0 @@ -// @ts-nocheck -import { nearest, differenceEuclidean, useMode, modeLch } from 'culori/fn'; -import { Color } from '../paramTypes'; -import { base } from './base.ts'; -import { toHex } from '../converters/toHex.ts'; - -const { keys } = Object; -const isColorEqual = (c1: Color, c2: Color): boolean => { - return c1['h'] === c2['h'] && c1['l'] === c2['l'] && c1['c'] === c2['c']; -}; - -/** - * @function - * @description Takes an array of colors and finds the best matches for a set of predefined palettes. The function does not work on achromatic colors, you may use isAchromatic to filter grays from your collection before passing it to the function. - * @param colors The array of colors to create palettes from. Preferably use 5 or more colors for better results. - * @param scheme (Optional) The palette type you want to return. - * @returns An array of colors if the scheme parameter is specified else it returns an object of all the palette types as keys and their values as an array of colors. If no colors are valid for the palette types it returns an empty array for the palette results. - * @example - * - * import { discoverPalettes } from 'huetiful-js' - -let sample = [ - "#ffff00", - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#720000", - "#600000", - "#4e0000", - "#3e0000", - "#310000", -] - -console.log(discoverPalettes(sample, "tetradic")) -// [ '#ffff00ff', '#00ffdcff', '#310000ff', '#720000ff' ] - */ -const discoverPalettes = ( - colors: Color[], - scheme: 'analogous' | 'triadic' | 'tetradic' | 'complementary' -): Color[] | object => { - const toLch = useMode(modeLch); - colors = colors.map((color) => toLch('lch')(toHex(color))); - const palettes = {}; - const schemes = ['analogous', 'triadic', 'tetradic', 'complementary']; - const targetPalettes = {}; - for (const color of colors) { - schemes.forEach((s) => (targetPalettes[s] = base(s)(color, false))); - - for (const paletteType of keys(targetPalettes)) { - const palette = []; - let variance = 0; - - for (const targetColor of targetPalettes[paletteType]) { - // filter out colors already in the palette - const availableColors = colors.filter( - (color1) => !palette.some((color2) => isColorEqual(color1, color2)) - ); - - const match = nearest( - availableColors, - differenceEuclidean('lch') - )(targetColor)[0]; - - variance += differenceEuclidean('lch')(targetColor, match); - - palette.push(match); - } - - if (!palettes[paletteType] || variance < palettes[paletteType].variance) { - palettes[paletteType] = palette.map(formatHex8); - } - } - } - - if (typeof scheme === 'string') { - return palettes[scheme.toLowerCase()]; - } else if (typeof scheme === 'undefined') { - return palettes; - } else { - throw Error( - `${scheme} is not a valid scheme. The schemes are triadic | tetradic | analogous | complementary` - ); - } -}; - -export { discoverPalettes }; diff --git a/src/palettes/earthtone.ts b/src/palettes/earthtone.ts deleted file mode 100644 index 1ab83612..00000000 --- a/src/palettes/earthtone.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Use eathtone colors as control points for the bezier interpolation -// takes one color and returns the specified amount of samples -// @ts-nocheck -import { - interpolate, - samples, - easingSmootherstep, - interpolatorSplineNatural, - fixupHueShorter, - interpolatorSplineMonotone, - interpolatorSplineBasisClosed -} from 'culori/fn'; -import type { Color, Earthtones, EarthtoneOptions } from '../paramTypes.ts'; -import { toHex } from '../converters/toHex.ts'; - -//Add an overrides object with interpolation function and - -/** - * @function - * @description Creates a scale of a spline based interpolation between an earthtone and a color. - * @param color The color to interpolate an earth tone with. - * @param earthtone The earthtone to interpolate with. - * @param iterations The number of iterations to produce from the color and earthtone. - * @param options Optional overrides for customising interpolation and easing functions. - * @returns The array of colors resulting from the earthtone interpolation as hex codes. - * @example - * - * import { earthtone } from 'huetiful-js' - - -console.log(earthtone("pink", "clay", 5)) -// [ '#6a5c52ff', '#8d746aff', '#b38d86ff', '#d9a6a6ff', '#ffc0cbff' ] - - */ - -const earthtone = ( - color: Color, - earthtone?: Earthtones, - iterations?: number, - options?: EarthtoneOptions -): Color[] => { - options = { - easingFunc: easingSmootherstep, - hueInterpolator: interpolatorSplineBasisClosed, - chromaInterpolator: interpolatorSplineNatural, - hueFixup: fixupHueShorter, - lightnessInterpolator: interpolatorSplineMonotone - }; - iterations = iterations < 1 ? 1 : iterations; - earthtone = earthtone.toLowerCase(); - const tones = { - 'light-gray': '#e5e5e5', - silver: '#f5f5f5', - sand: '#c2b2a4', - tupe: '#a79e8a', - mahogany: '#958c7c', - 'brick-red': '#7d7065', - clay: '#6a5c52', - cocoa: '#584a3e', - 'dark-brown': '#473b31', - dark: '#352a21' - }; - const base: Color = tones[earthtone || 'dark']; - - const f = interpolate([base, toHex(color), options['easingFunc']], 'lch', { - h: { - fixup: options['hueFixup'], - use: options['hueInterpolator'] - }, - c: { - use: options['chromaInterpolator'] - }, - l: { - use: options['lightnessInterpolator'] - } - }); - - if (iterations === 1) { - return toHex(f(0.5)); - } else { - return samples(iterations).map((t) => toHex(f(t))); - } -}; - -export { earthtone }; diff --git a/src/palettes/getComplimentaryHue.ts b/src/palettes/getComplimentaryHue.ts deleted file mode 100644 index 4deff96e..00000000 --- a/src/palettes/getComplimentaryHue.ts +++ /dev/null @@ -1,77 +0,0 @@ -// @ts-nocheck -import hueTempMap from '../color-maps/samples/hueTemperature'; -import { getChannel } from '../getters_and_setters/get.ts'; -import { min, max } from '../fp/array/min_max.ts'; -import { customConcat } from '../fp/object/customConcat.ts'; -import { inRange } from '../fp/number/inRange.ts'; -import { adjustHue } from '../fp/number/adjustHue.ts'; -import { setChannel } from '../getters_and_setters/set.ts'; -import type { Color } from '../paramTypes'; -import { toHex } from '../converters/toHex.ts'; - -const { keys } = Object; -const hueKeys = keys(hueTempMap); - -/** - * @function - * @description Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors. - * @param color The color to retrieve its complimentary hue. - * @param colorObj Optional boolean whether to return an object with the result color hue family or just the result color. Default is false. - * @returns An object with the hue family and complimentary color as keys. - * @example - *import { getComplimentaryHue } from "huetiful-js"; - * - * -console.log(getComplimentaryHue("pink", true)) -//// { hue: 'blue-green', color: '#97dfd7ff' } - -console.log(getComplimentaryHue("purple")) -// #005700ff - */ -const getComplimentaryHue = ( - color: Color, - colorObj = false -): { hue: string; color: Color } | Color => { - const modeChannel = 'lch.h'; - // A complementary hue is 180 deg from the hue value of the passed in color - - const complementaryHue: number = adjustHue( - getChannel(modeChannel)(color) + 180 - ); - - // Find the hue family which the color belongs to - - // eslint-disable-next-line prefer-const - - const hueFamily = hueKeys - .map((hue) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - - const hueVals = customConcat(hueTempMap[hue]); - const minVal = min(...hueVals); - const maxVal = max(...hueVals); - const bool = customConcat(hueTempMap[hue]).some(() => - inRange(complementaryHue, minVal, maxVal) - ); - - if (bool) { - return hue; - } - }) - .filter((val) => typeof val === 'string') - .toString(); - - let result: Color | { hue: string; color: Color }; - if (complementaryHue) { - result = { - hue: hueFamily, - color: toHex(setChannel(modeChannel)(color, complementaryHue)) - }; - } else { - result = { hue: 'gray', color: color }; - } - - return (colorObj && result) || result['color']; -}; - -export { getComplimentaryHue }; diff --git a/src/palettes/getHue.ts b/src/palettes/getHue.ts deleted file mode 100644 index 69dba642..00000000 --- a/src/palettes/getHue.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// Returns the hue range where a color is found. If the hue Channel is falsy we return gray ? -// @ts-nocheck -import hueTempMap from '../color-maps/samples/hueTemperature'; -import { useMode, modeLch } from 'culori/fn'; -import { inRange } from '../fp/number/inRange.ts'; -import { min, max } from '../fp/array/min_max.ts'; -import { customConcat } from '../fp/object/customConcat.ts'; -import { toHex } from '../converters/toHex.ts'; -import type { Color, Hue } from '../paramTypes'; - -/** - *@function - @description Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray". - * @param color The color to query its shade or hue family. - * @returns The name of the hue family for example red or green. - * @example - * - * import { getHue } from 'huetiful-js' - - -console.log(getHue("#310000")) -// red - */ -const getHue = (color: Color): Hue => { - // First convert the color to LCH - const lch = useMode(modeLch); - color = lch(toHex(color)); - - //Capure the hue value - const factor: number | undefined = color['h']; - - // First check if hue is falsy. If true return the string "gray" - // The predicate-func - - // We then pick the truthy key by returning an object which returns true for the inRange predicate - - const hueKeys = Object.keys(hueTempMap); - const hueFamily = hueKeys - .map((hue) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - - const hueVals = customConcat(hueTempMap[hue]); - const minVal = min(...hueVals); - const maxVal = max(...hueVals); - const bool = customConcat(hueTempMap[hue]).some(() => - inRange(factor, minVal, maxVal) - ); - - if (bool) { - return hue; - } - }) - .filter((val) => typeof val === 'string') - .toString(); - - return hueFamily; -}; - -export { getHue }; diff --git a/src/palettes/hueShift.ts b/src/palettes/hueShift.ts deleted file mode 100644 index def39c49..00000000 --- a/src/palettes/hueShift.ts +++ /dev/null @@ -1,122 +0,0 @@ -//@ts-nocheck -// Original source from George Francis: Coloring with Code -// Can we also lightnessMapper palette types to create hue shifted variants per each color in the palette ? - -import { easingSmootherstep, modeLch, samples, useMode } from 'culori/fn'; -import type { Color } from '../paramTypes.ts'; -import { adjustHue } from '../fp/number/adjustHue.ts'; -import { toHex } from '../converters/toHex.ts'; - -const lightnessMapper = - (n: number) => - (start1: number, end1: number) => - (start2: number, end2: number) => - ((n - start1) / (end1 - start1)) * (end2 - start2) + start2; - -/** - * @function - * @description Generates a palette of hue shifted colors (as a color becomes lighter, its hue shifts up and darker when its hue shifts down. ) from a single base color. Min and max lightness value determine how light or dark our colour will be at either extreme. - * @param color The color to use as the base of the hueshift. Colors are internally converted to LCH. - * @param minLightness Minimum lightness value (range 0-100). - * @param maxLightness Maximum lightness value (range 0-100). - * @param iterations The number of iterations to perform on the color. The length of the resultant array is the number of iterations multiplied by 2 plus the base color passed or (iterations*2)+1. - * @param hueStep Controls how much the hue will shift at each iteration. - * @param hex Optional boolen to return lch color objects or hex codes in the result array. Default is false which returns LCH color objects. - * @returns An array of colors in either hex or as LCH color objects. - * @example - * - * import { hueShift } from "huetiful-js"; - -let hueShiftedPalette = hueShift("#3e0000",true); - -console.log(hueShiftedPalette); - -// [ - '#ffffe1', '#ffdca5', - '#ca9a70', '#935c40', - '#5c2418', '#3e0000', - '#310000', '#34000f', - '#38001e', '#3b002c', - '#3b0c3a' -] - */ - -const hueShift = ( - color: Color, - hex = false, - { - minLightness, - maxLightness, - hueStep, - iterations, - easingFn - }: { - minLightness?: number; - maxLightness?: number; - hueStep?: number; - iterations?: number; - easingFn: (t: number) => number; - } -): Color[] => { - minLightness = minLightness || 10; - maxLightness = maxLightness || 90; - hueStep = hueStep || 5; - iterations = iterations || 6; - easingFn = easingSmootherstep || easingFn; - const toLch = useMode(modeLch); - - color = toLch(toHex(color)); - - // Pass in default values if any of the opts is undefined - - const palette: Color[] = [color]; - - // Maximum number of iterations possible. - const MAX_SAFE_ITERATIONS = 360 / hueStep; - //Each iteration add a darker shade to the start of the array and a lighter tint to the end. - - if (iterations <= MAX_SAFE_ITERATIONS) { - samples(iterations).map((t) => { - //adjustHue checks hue values are clamped. - const hueDark = adjustHue(color['h'] - hueStep * t); - const hueLight = adjustHue(color['h'] + hueStep * t); - - // Here we use lightnessMapper to calculate our lightness values which takes a number that exists in range [0,1]. - const lightnessDark = lightnessMapper(easingFn(t))(0.1, iterations)( - color['l'], - minLightness - ); - - const lightnessLight = lightnessMapper(easingFn(t))(0.05, iterations)( - color['l'], - maxLightness - ); - - palette.push({ - l: lightnessDark, - c: color['c'], - h: hueDark, - mode: 'lch' - }); - - palette.unshift({ - l: lightnessLight, - c: color['c'], - h: hueLight, - mode: 'lch' - }); - }); - } else { - throw Error( - `The number of iterations exceeds the maximum number of iterations. The maximum iterations are determined by the size of the hueStep. To find the maximum iterations possible, use this formula: 360/hueStep` - ); - } - - if (hex) { - return palette.map(toHex); - } else { - return palette; - } -}; - -export { hueShift }; diff --git a/src/palettes/index.ts b/src/palettes/index.ts deleted file mode 100644 index 2fce808a..00000000 --- a/src/palettes/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { hueShift } from './hueShift'; -export { discoverPalettes } from './discoverPalettes'; -export { getComplimentaryHue } from './getComplimentaryHue'; -export { earthtone } from './earthtone'; -export { getHue } from './getHue'; -export { pairedScheme } from './paired'; -export { base } from './base'; -export { pastel } from './pastel'; diff --git a/src/palettes/paired.ts b/src/palettes/paired.ts deleted file mode 100644 index d3f69a0d..00000000 --- a/src/palettes/paired.ts +++ /dev/null @@ -1,91 +0,0 @@ -// @ts-nocheck -// From colorbrewr - -import { toHex } from '../converters/toHex.ts'; -import { setChannel } from '../getters_and_setters/set.ts'; -import { Color, EarthtoneOptions, Tone } from '../paramTypes.ts'; -import { - interpolate, - samples, - easingSmootherstep, - interpolatorSplineNatural, - fixupHueShorter, - interpolatorSplineMonotone, - interpolatorSplineBasisClosed, - useMode, - modeLch -} from 'culori/fn'; - -/** - * @function pairedScheme - * @description Creates a scheme that consists of a base color that is incremented by a hueStep to get the final hue to pair with.The colors are interpolated via white or black. - * @param color The color to return a paired color scheme from. - * @param via The tone to interpolate through (either white or black). Default is white. - * @param hueStep The value to increment the base color's hue channel with. - * @param iterations The number of color samples to generate. - * @param overrides The optional overrides object to customize per channel options like interpolation methods and channel fixups. - * @returns An array containing the paired scheme. - * @example - * - * import { pairedScheme } from 'huetiful-js' - -console.log(pairedScheme("green", 6, 5, "dark")) -// [ '#008116ff', '#006945ff', '#184b4eff', '#007606ff' ] - */ -const pairedScheme = ( - color: Color, - hueStep: number, - iterations: number, - via: Tone, - options: EarthtoneOptions -): Color[] => { - // eslint-disable-next-line prefer-const - options = { - easingFunc: defaultArg(easingSmootherstep), - hueInterpolator: defaultArg(interpolatorSplineBasisClosed), - chromaInterpolator: defaultArg(interpolatorSplineNatural), - hueFixup: defaultArg(fixupHueShorter), - lightnessInterpolator: defaultArg(interpolatorSplineMonotone) - }; - const toLch = useMode(modeLch); - color = toLch(toHex(color)); - - // get the hue of the passed in color and add it to the step which will result in the final color to pair with - const derivedHue = setChannel('lch.h')(color, color['h'] + hueStep || 12); - - // Set the tones to color objects with hardcoded hue values and lightness channels clamped at extremes - const tones = { - dark: '#263238', - light: { l: 100, c: 0, h: 0, mode: 'lch' } - }; - - const scale = interpolate([color, tones[via || 'dark'], derivedHue], 'lch', { - h: { - fixup: options['hueFixup'], - use: options['hueInterpolator'] - }, - c: { - use: options['chromaInterpolator'] - }, - l: { - use: options['lightnessInterpolator'] - } - }); - - const { abs, round } = Math; - - // Declare the num of iterations in samples() which will be used as the t value - // Since the interpolation returns half duplicate values we double the sample value - // Guard the num param against negative values and floats - const smp = samples((round(abs(iterations)) || 4) * 2); - - //The array to capture the different iterations - const results: Color[] = smp.map((t) => - formatHex8(scale(easingSmootherstep(t))) - ); - - // Return a slice of the array from the start to the half length of the array - return results.slice(0, results.length / 2); -}; - -export { pairedScheme }; diff --git a/src/palettes/pastel.ts b/src/palettes/pastel.ts deleted file mode 100644 index 344396a4..00000000 --- a/src/palettes/pastel.ts +++ /dev/null @@ -1,95 +0,0 @@ -//@ts-nocheck -// Pastels.mjs. - This module creates pastel versions of a color. It will take an arr or single value , tweak it and then return the result. Optional overrides for min max values when iterating over an arr. -import type { Color } from '../paramTypes.ts'; -import { averageNumber, modeHsv, useMode } from 'culori/fn'; -import { min, max } from '../fp/array/min_max.ts'; -import { random } from '../fp/number/random.ts'; -import { toHex } from '../converters/toHex.js'; - -const samplePastelObj = [ - { - color: '#fea3aa', - saturation: 0.35826771653543305, - value: 0.996078431372549 - }, - { - color: '#f8b88b', - saturation: 0.43951612903225806, - value: 0.9725490196078431 - }, - { color: '#faf884', saturation: 0.472, value: 0.9803921568627451 }, - { - color: '#f2a2e8', - saturation: 0.3305785123966942, - value: 0.9490196078431372 - }, - { - color: '#b2cefe', - saturation: 0.2992125984251969, - value: 0.996078431372549 - }, - { - color: '#baed91', - saturation: 0.3881856540084388, - value: 0.9294117647058824 - } -]; - -const sampleSaturation = samplePastelObj.map((el) => el['saturation']); -const sampleValues = samplePastelObj.map((el) => el['value']); - -const pastelSample = { - averageSaturation: averageNumber(sampleValues), - averageValue: averageNumber(sampleSaturation), - minSampleSaturation: min(sampleSaturation), - maxSampleSaturation: max(sampleSaturation), - minSampleValue: min(sampleValues), - maxSampleValue: max(sampleValues) -}; - -//Normalize the s and v channels between low and max values for each - -/** - * @function - * @description Returns a random pastel variant of the passed in color. - * @param color The color to return a pastel variant of. - * @param hex Pass in true to return an 8-character hex code else it will return an HSV color object. - * @returns A random pastel color. - * @example - * - * -import { pastel } from 'huetiful-js' - -console.log(pastel("green", true)) -// #036103ff - */ -const pastel = (color: Color, hex = true): Color => { - const toHsv = useMode(modeHsv); - color = toHsv(toHex(color)); - // For now we're simply returning an hsv object with the s and v channel set to the averages - if (hex) { - return toHex({ - h: color['h'], - s: pastelSample['averageSaturation'], - v: pastelSample['averageValue'], - mode: 'hsv' - }); - } else { - return { - h: color['h'], - s: random( - pastelSample['minSampleSaturation'], - pastelSample['maxSampleSaturation'], - true - ), - v: random( - pastelSample['minSampleValue'], - pastelSample['maxSampleValue'], - true - ), - mode: 'hsv' - }; - } -}; - -export { pastel }; diff --git a/src/paramTypes.d.ts b/src/paramTypes.d.ts deleted file mode 100644 index e6226f52..00000000 --- a/src/paramTypes.d.ts +++ /dev/null @@ -1,407 +0,0 @@ -export type EarthtoneOptions = { - easingFunc?: (t: number) => number; - hueInterpolator?: Interpolator; - chromaInterpolator?: Interpolator; - hueFixup?: (arr: number[]) => number[]; - lightnessInterpolator?: Interpolator; -}; -export type Interpolator = (arr: number[]) => (t: number) => number; -export type Tone = 'light' | 'dark'; -export type Hue = - | 'red-purple' - | 'red' - | 'yellow-red' - | 'yellow' - | 'green-yellow' - | 'green' - | 'blue-green' - | 'blue' - | 'purple-blue' - | 'purple'; - -export type Earthtones = - | 'light gray' - | 'silver' - | 'sand' - | 'tupe' - | 'mahogany' - | 'brick red' - | 'clay' - | 'cocoa' - | 'dark brown' - | 'dark'; -export type DivergingScheme = - | 'Spectral' - | 'RdYlGn' - | 'RdBu' - | 'PiYG' - | 'PRGn' - | 'RdYlBu' - | 'BrBG' - | 'RdGy' - | 'PuOr'; -export type QualitativeScheme = - | 'Set2' - | 'Accent' - | 'Set1' - | 'Set3' - | 'Dark2' - | 'Paired' - | 'Pastel2' - | 'Pastel1'; - -export type SequentialScheme = - | 'OrRd' - | 'PuBu' - | 'BuPu' - | 'Oranges' - | 'BuGn' - | 'YlOrBr' - | 'YlGn' - | 'Reds' - | 'RdPu' - | 'Greens' - | 'YlGnBu' - | 'Purples' - | 'GnBu' - | 'Greys' - | 'YlOrRd' - | 'PuRd' - | 'Blues' - | 'PuBuGn' - | 'Viridis'; - -export type Color = - | number - | string - | object - | [string, number, number, number, number?]; - -/** - * @param - * An array of baseColor tokens (in short, just an array of valid colors) - */ - -/** - * @type - * The property being queried. Used in utilities that perform operations on collections. - */ -export type Factor = - | 'luminance' - | 'temp' - | 'saturation' - | 'contrast' - | 'distance' - | 'lightness' - | 'hue'; - -type Order = 'asc' | 'desc'; - -type callback = (arg: Color, colorSpace?: HueColorSpaces) => number; - -type FactorMapper = ( - factor: Factor, - callback: callback, - order?: Order, - colorObj = false -) => (colors: Color[]) => Color[]; - -export type ColorSpaces = - | 'a98' - | 'cubehelix' - | 'dlab' - | 'dlch' - | 'hsi' - | 'hsl' - | 'hsv' - | 'hwb' - | 'jab' - | 'jch' - | 'lab' - | 'lab65' - | 'lch' - | 'lch65' - | 'lchuv' - | 'lrgb' - | 'luv' - | 'okhsl' - | 'okhsv' - | 'oklab' - | 'rgb'; - -export type HueColorSpaces = 'hsl' | 'hsv' | 'hsi' | 'oklch' | 'lch' | 'hwb'; - -export type ScaleValues = - | '100' - | '50' - | '200' - | '300' - | '400' - | '500' - | '600' - | '700' - | '800' - | '900'; - -export type HueMap = { - indigo: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - gray: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - zinc: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - neutral: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - stone: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - red: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - orange: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - amber: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - yellow: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - lime: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - green: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - emerald: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - teal: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - sky: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - blue: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - violet: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - purple: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - fuchsia: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - pink: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; - - rose: { - '50': string; - '100': string; - '200': string; - '300': string; - '400': string; - '500': string; - '600': string; - '700': string; - '800': string; - '900': string; - }; -}; - -type ColorTemp = 'warm' | 'cool' | 'daylight' | 'incadescent' | 'fluorescent'; diff --git a/src/sortBy.ts b/src/sortBy.ts new file mode 100644 index 00000000..fb549fc7 --- /dev/null +++ b/src/sortBy.ts @@ -0,0 +1,377 @@ +/** + * @license + * sortBy.ts - Utilities for sorting collections of colors. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +import type { + Factor, + ColorToken, + Colorspaces, + ColorDistanceOptions, + HueColorSpaces, + callback, + Order +} from './types'; +import { + checkArg, + sortedArr, + matchLightnessChannel, + matchChromaChannel +} from './helpers'; +import { wcagContrast, differenceEuclidean } from 'culori/fn'; +import { getLuminance, getChannel } from './utils'; + +function baseSortBy( + factor: Factor, + cb: callback, + order: Order, + collection: ColorToken[] | object +) { + return sortedArr(factor, cb, order)(collection); +} + +/** + * + * Sorts colors according to their saturation. + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @param mode The mode color space to compute the saturation value in. The default is jch . + * @returns An array of the sorted color values. + * @example + * import { sortBySaturation } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + +let sorted = sortBySaturation(sample); +console.log(sorted); + +// [ + '#310000', '#3e0000', + '#164100', '#4e0000', + '#600000', '#720000', + '#00ffdc', '#007e00', + '#00ff78', '#00c000', + '#ffff00' +] + +let sortedDescending = sortBySaturation(sample,'desc'); +console.log(sortedDescending) +// [ + '#ffff00', '#00c000', + '#00ff78', '#007e00', + '#00ffdc', '#720000', + '#600000', '#4e0000', + '#164100', '#3e0000', + '#310000' +] + + */ + +function sortBySaturation( + collection: ColorToken[] | object, + order: 'asc' | 'desc', + mode?: HueColorSpaces +): ColorToken[] { + return baseSortBy( + 'saturation', + getChannel(matchChromaChannel(mode)), + order, + collection + ); +} + +/** + * + * Sorts colors according to the relative brightness as defined by WCAG definition. + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @returns An array of the sorted color values. + * @example + * import { sortByLuminance } from "huetiful-js"; +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + + + +let sorted = sortByLuminance(sample) +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + +let sortedDescending = sortByLuminance(sample, "desc"); +console.log(sortedDescending) +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + + */ + +function sortByLuminance( + collection: ColorToken[] | object, + order: 'asc' | 'desc' +): ColorToken[] { + return baseSortBy('luminance', getLuminance, order, collection); +} + +/** + * + * Sorts colors according to their lightness. + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @param colorspace The mode colorspace to use for filtering color lightness. Defaut is lch65 + * @returns An array of the sorted color values. + * @example + * import { sortByLightness } from "huetiful-js"; + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +] + +sortByLightness(sample) + +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#164100', + '#007e00', '#00c000', + '#00ff78', '#00ffdc', + '#ffff00' +] + + +sortByLightness(sample,'desc') + +// [ + '#ffff00', '#00ffdc', + '#00ff78', '#00c000', + '#007e00', '#164100', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + */ +// For lightness use a different color space +function sortByLightness( + collection: ColorToken[] | object, + order?: Order, + colorspace?: HueColorSpaces +): ColorToken[] { + return baseSortBy( + 'lightness', + getChannel(matchLightnessChannel(colorspace)), + order, + collection + ); +} + +/** + * + * Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') +* @param colorspace The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results. +* @returns An array of the sorted color values. + * @example + * let sample = [ + "#00ffdc", + "#00ff78", + "#00c000", + "#007e00", + "#164100", + "#ffff00", + "#310000", + "#3e0000", + "#4e0000", + "#600000", + "#720000", +]; + + +let sorted = sortByHue(sample); +console.log(sorted) +// [ + '#310000', '#3e0000', + '#4e0000', '#600000', + '#720000', '#ffff00', + '#164100', '#00c000', + '#007e00', '#00ff78', + '#00ffdc' +] + +let sortedDescending = sortByHue(sample,'desc'); +console.log(sortedDescending) +// [ + '#00ffdc', '#00ff78', + '#007e00', '#00c000', + '#164100', '#ffff00', + '#720000', '#600000', + '#4e0000', '#3e0000', + '#310000' +] + + */ + +// Todo: Add the mode param so that users can select mode to work with. The default is lch +function sortByHue( + collection: ColorToken[] | object, + order?: Order, + colorspace?: HueColorSpaces +): ColorToken[] { + const reHue = /h/i.test(colorspace); + return ( + reHue && + baseSortBy( + 'lightness', + getChannel(`${checkArg(colorspace, 'jch')}.h`), + order, + collection + ) + ); +} + +/** + * + * Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param) + * @param colors The array of colors to sort + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @returns An array of the sorted color values. + * @example + * + * import { sortByContrast } from 'huetiful-js' + +let sample = ['purple', 'green', 'red', 'brown'] +console.log(sortByContrast(sample, 'yellow')) +// [ 'red', 'green', 'brown', 'purple' ] + +console.log(sortByContrast(sample, 'yellow', 'desc')) +// [ 'purple', 'brown', 'green', 'red' ] + + */ + +function sortByContrast( + collection: ColorToken[] | object, + against: ColorToken, + order?: Order +): ColorToken[] { + // @ts-ignore + const cb = (against: ColorToken) => (color: ColorToken) => + wcagContrast(color as string, against as string); + return baseSortBy('contrast', cb(against), order, collection); +} + +/** + * + * Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is computed from against a color which is used for comparison for all the colors in the array i.e it sorts the colors against the dist + * @param colors The array of colors to sort. + * @param against The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array. + * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') + * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0]. + * @param mode The color space to calculate the distance in . The default is the cylindrical variant of the CIELUV colorspace ('lchuv') + * @returns An array of the sorted color values. + * @example + * import { sortByDistance } from 'huetiful-js' + +let sample = ['purple', 'green', 'red', 'brown'] +console.log( + sortByDistance(sample, 'yellow', 'asc', { + mode: 'lch', + }) +) + +// [ 'brown', 'red', 'green', 'purple' ] + +let sample = ['purple', 'green', 'red', 'brown'] +console.log( + sortByDistance(sample, 'yellow', 'asc', { + mode: 'lch', + }) +) + +// [ 'green', 'brown', 'red', 'purple' ] + */ + +function sortByDistance( + collection: ColorToken[] | object, + against: ColorToken, + order?: 'asc' | 'desc', + options?: ColorDistanceOptions +): ColorToken[] { + var { mode, weights } = options || {}; + + const cb = (against: string, mode: Colorspaces) => (color: string) => { + // @ts-ignore + return differenceEuclidean( + checkArg(mode, 'lchuv') as typeof mode, + checkArg(weights, [1, 1, 1, 0]) as typeof weights + )(against, color); + }; + + return baseSortBy( + 'contrast', + cb(against as string, checkArg(mode, 'lchuv') as typeof mode), + order, + collection + ); +} + +export { + sortByContrast, + sortByDistance, + sortByLightness, + sortBySaturation, + sortByHue, + sortByLuminance +}; diff --git a/src/sortBy/index.ts b/src/sortBy/index.ts deleted file mode 100644 index 2a111189..00000000 --- a/src/sortBy/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { sortByContrast } from './sortByContrast'; -export { sortByDistance } from './sortByDistance'; -export { sortByHue } from './sortByHue'; -export { sortByLightness } from './sortByLightness'; -export { sortByLuminance } from './sortByLuminance'; -export { sortBySaturation } from './sortBySaturation'; -export { sortByTemp } from './sortByTemp'; diff --git a/src/sortBy/sortByContrast.ts b/src/sortBy/sortByContrast.ts deleted file mode 100644 index 9e9b0939..00000000 --- a/src/sortBy/sortByContrast.ts +++ /dev/null @@ -1,37 +0,0 @@ -// @ts-nocheck -import type { Factor, Color } from '../paramTypes.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; -import { wcagContrast } from 'culori/fn'; - -/** - * @function - * @description Sorts colors according to their contrast value as defined by WCAG. The contrast is tested against a comparison color (the 'against' param) - * @param colors The array of colors to sort - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') - * @returns An array of the sorted color values. - * @example - * - * import { sortByContrast } from 'huetiful-js' - -let sample = ['purple', 'green', 'red', 'brown'] -console.log(sortByContrast(sample, 'yellow')) -// [ 'red', 'green', 'brown', 'purple' ] - -console.log(sortByContrast(sample, 'yellow', 'desc')) -// [ 'purple', 'brown', 'green', 'red' ] - - */ - -const sortByContrast = ( - colors: Color[], - against: Color, - order: 'asc' | 'desc' -): Color[] => { - const factor: Factor = 'contrast'; - const cb = (against: Color) => (color: Color) => wcagContrast(color, against); - //Sorting the color array of object by the 'temp' property in the specified order. - - return sortedArr(factor, cb(against), order)(colors); -}; - -export { sortByContrast }; diff --git a/src/sortBy/sortByDistance.ts b/src/sortBy/sortByDistance.ts deleted file mode 100644 index 414caffd..00000000 --- a/src/sortBy/sortByDistance.ts +++ /dev/null @@ -1,52 +0,0 @@ -// @ts-nocheck -import type { Factor, Color, ColorSpaces } from '../paramTypes.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; -import { differenceEuclidean } from 'culori/fn'; - -/** - * @function - * @description Sorts colors according to their Euclidean distance. The distance factor is determined by the color space used (some color spaces are not symmetrical meaning that the distance between colorA and colorB is not equal to the distance between colorB and colorA ). The distance is compared against a color which is used for comparison for all the colors in the array. - * @param colors The array of colors to sort. - * @param against The color to compare the distance with. All the distances are calculated between this color and the ones in the colors array. - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') - * @param weights The weighting values to pass to the Euclidean function. Default is [1,1,1,0]. - * @param mode The color space to calculate the distance in . - * @returns An array of the sorted color values. - * @example - * import { sortByDistance } from 'huetiful-js' - -let sample = ['purple', 'green', 'red', 'brown'] -console.log( - sortByDistance(sample, 'yellow', 'asc', { - mode: 'lch', - }) -) - -// [ 'brown', 'red', 'green', 'purple' ] - -let sample = ['purple', 'green', 'red', 'brown'] -console.log( - sortByDistance(sample, 'yellow', 'asc', { - mode: 'lch', - }) -) - -// [ 'green', 'brown', 'red', 'purple' ] - */ - -const sortByDistance = ( - colors: Color[], - against: Color, - order?: 'asc' | 'desc', - mode?: ColorSpaces, - weights?: [number, number, number, number] -): Color[] => { - const factor: Factor = 'distance'; - const cb = (against: Color, mode: ColorSpaces) => (color: Color) => - differenceEuclidean(mode || 'lch', weights || [1, 1, 1, 0])(against, color); - //Sorting the color array of object by the 'distance' property in the specified order. - - return sortedArr(factor, cb(against, mode), order)(colors); -}; - -export { sortByDistance }; diff --git a/src/sortBy/sortByHue.ts b/src/sortBy/sortByHue.ts deleted file mode 100644 index ffee0b41..00000000 --- a/src/sortBy/sortByHue.ts +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-nocheck -import { getChannel } from '../getters_and_setters/get.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; - -import type { Factor, Color } from '../paramTypes.ts'; - -/** - * @function - * @description Sorts colors according to hue values. It works with any color space with a hue channel. Note that hue values between HSL and Lch do not align. Achromatic colors are not supported - * @param colors The array of colors to sort - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') -* @param mode The color space to compute the color distances in. All colors within the collection will be converted to mode. Also note that because differences in hue mapping certain color spaces such as HSL and LCH hue values do not align. Keep such quirks in mind to avoid weird results. -* @returns An array of the sorted color values. - * @example - * let sample = [ - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#ffff00", - "#310000", - "#3e0000", - "#4e0000", - "#600000", - "#720000", -]; - - -let sorted = sortByHue(sample); -console.log(sorted) -// [ - '#310000', '#3e0000', - '#4e0000', '#600000', - '#720000', '#ffff00', - '#164100', '#00c000', - '#007e00', '#00ff78', - '#00ffdc' -] - -let sortedDescending = sortByHue(sample,'desc'); -console.log(sortedDescending) -// [ - '#00ffdc', '#00ff78', - '#007e00', '#00c000', - '#164100', '#ffff00', - '#720000', '#600000', - '#4e0000', '#3e0000', - '#310000' -] - - */ - -// Todo: Add the mode param so that users can select mode to work with. The default is -const sortByHue = (colors: Color[], order: 'asc' | 'desc'): Color[] => { - const factor: Factor = 'hue'; - const cb = getChannel('lch.h'); - //Sorting the color array of object by the 'temp' property in the specified order. - - return sortedArr(factor, cb, order)(colors); -}; - -export { sortByHue }; diff --git a/src/sortBy/sortByLightness.ts b/src/sortBy/sortByLightness.ts deleted file mode 100644 index 5d009d37..00000000 --- a/src/sortBy/sortByLightness.ts +++ /dev/null @@ -1,62 +0,0 @@ -// @ts-nocheck -import type { Factor, Color } from '../paramTypes.ts'; -import { getChannel } from '../getters_and_setters/get.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; - -/** - * @function - * @description Sorts colors according to their lightness. - * @param colors The array of colors to sort - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') - * @returns An array of the sorted color values. - * @example - * import { sortByLightness } from "huetiful-js"; - -let sample = [ - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#ffff00", - "#310000", - "#3e0000", - "#4e0000", - "#600000", - "#720000", -] - -sortByLightness(sample) - -// [ - '#310000', '#3e0000', - '#4e0000', '#600000', - '#720000', '#164100', - '#007e00', '#00c000', - '#00ff78', '#00ffdc', - '#ffff00' -] - - -sortByLightness(sample,'desc') - -// [ - '#ffff00', '#00ffdc', - '#00ff78', '#00c000', - '#007e00', '#164100', - '#720000', '#600000', - '#4e0000', '#3e0000', - '#310000' -] - - */ - -const sortByLightness = (colors: Color[], order: 'asc' | 'desc'): Color[] => { - const factor: Factor = 'lightness'; - const cb = getChannel('lch.l'); - //Sorting the color array of object by the 'temp' property in the specified order. - - return sortedArr(factor, cb, order)(colors); -}; - -export { sortByLightness }; diff --git a/src/sortBy/sortByLuminance.ts b/src/sortBy/sortByLuminance.ts deleted file mode 100644 index 1cfb4984..00000000 --- a/src/sortBy/sortByLuminance.ts +++ /dev/null @@ -1,63 +0,0 @@ -// @ts-nocheck -import type { Factor, Color } from '../paramTypes.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; -import { getLuminance } from '../getters_and_setters/luminance.ts'; - -/** - * @function - * @description Sorts colors according to the relative brightness as defined by WCAG definition. - * @param colors The array of colors to sort - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') - * @returns An array of the sorted color values. - * @example - * import { sortByLuminance } from "huetiful-js"; -let sample = [ - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#ffff00", - "#310000", - "#3e0000", - "#4e0000", - "#600000", - "#720000", -]; - - - -let sorted = sortByLuminance(sample) -console.log(sorted) -// [ - '#310000', '#3e0000', - '#4e0000', '#600000', - '#720000', '#164100', - '#007e00', '#00c000', - '#00ff78', '#00ffdc', - '#ffff00' -] - -let sortedDescending = sortByLuminance(sample, "desc"); -console.log(sortedDescending) -// [ - '#ffff00', '#00ffdc', - '#00ff78', '#00c000', - '#007e00', '#164100', - '#720000', '#600000', - '#4e0000', '#3e0000', - '#310000' -] - - - */ - -const sortByLuminance = (colors: Color[], order: 'asc' | 'desc'): Color[] => { - const factor: Factor = 'luminance'; - const cb = getLuminance; - //Sorting the color array of object by the 'temp' property in the specified order. - - return sortedArr(factor, cb, order)(colors); -}; - -export { sortByLuminance }; diff --git a/src/sortBy/sortBySaturation.ts b/src/sortBy/sortBySaturation.ts deleted file mode 100644 index 03d07847..00000000 --- a/src/sortBy/sortBySaturation.ts +++ /dev/null @@ -1,61 +0,0 @@ -// @ts-nocheck -import type { Factor, Color } from '../paramTypes.ts'; -import { getChannel } from '../getters_and_setters/get.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; - -/** - * @function - * @description Sorts colors according to their saturation. - * @param colors The array of colors to sort - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') - * @returns An array of the sorted color values. - * @example - * import { sortBySaturation } from "huetiful-js"; -let sample = [ - "#00ffdc", - "#00ff78", - "#00c000", - "#007e00", - "#164100", - "#ffff00", - "#310000", - "#3e0000", - "#4e0000", - "#600000", - "#720000", -]; - -let sorted = sortBySaturation(sample); -console.log(sorted); - -// [ - '#310000', '#3e0000', - '#164100', '#4e0000', - '#600000', '#720000', - '#00ffdc', '#007e00', - '#00ff78', '#00c000', - '#ffff00' -] - -let sortedDescending = sortBySaturation(sample,'desc'); -console.log(sortedDescending) -// [ - '#ffff00', '#00c000', - '#00ff78', '#007e00', - '#00ffdc', '#720000', - '#600000', '#4e0000', - '#164100', '#3e0000', - '#310000' -] - - */ - -const sortBySaturation = (colors: Color[], order: 'asc' | 'desc'): Color[] => { - const factor: Factor = 'saturation'; - const cb = getChannel('lch.c'); - //Sorting the color array of object by the 'temp' property in the specified order. - - return sortedArr(factor, cb, order)(colors); -}; - -export { sortBySaturation }; diff --git a/src/sortBy/sortByTemp.ts b/src/sortBy/sortByTemp.ts deleted file mode 100644 index 61cd7597..00000000 --- a/src/sortBy/sortByTemp.ts +++ /dev/null @@ -1,45 +0,0 @@ -//@ts-nocheck -import { getTemp } from '../converters/getTemp.ts'; -import type { Color, Factor } from '../paramTypes.ts'; -import { sortedArr } from '../fp/array/sortedArr.ts'; - -/** - * @function - * @description Sorts colors according to temperature value in Kelvins according to the temperatu. Achromatic colors may return awkward results. - * @param colors The array of colors to sort - * @param order The expected order of arrangement. Either 'asc' or 'desc'. Default is ascending ('asc') - * @returns An array of the sorted color values. - * @see Based on Neil Bartlett's implementation https://github.com/neilbartlett/color-temperature - * @example - * import { sortByTemp } from 'huetiful-js' -let sample = [ - '#00ffdc', - '#00ff78', - '#00c000', - '#007e00', - '#164100', - '#ffff00', - '#310000', - '#3e0000', - '#4e0000', - '#600000', - '#720000', -] - -let sorted = sortByTemp(sample) -console.log(sorted) - -let sortedDescending = sortByTemp(sample, 'desc') -console.log(sortedDescending) - */ - -const sortByTemp = (colors: Color[], order: 'asc' | 'desc'): Color[] => { - // Purifying the data.All colors are converted to the specified mode to ensure unbiased results. - const factor: Factor = 'temp'; - const cb = getTemp; - //Sorting the color array of object by the 'temp' property in the specified order. - - return sortedArr(factor, cb, order)(colors); -}; - -export { sortByTemp }; diff --git a/src/source.code-workspace b/src/source.code-workspace new file mode 100644 index 00000000..022c3e5c --- /dev/null +++ b/src/source.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 00000000..9c8e51a4 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,293 @@ +/* + * @preserve + * @license + * types.d.ts - Type declarations for huetiful-js. + * Contains colors from TailwindCSS released under the MIT permissive licence. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +// Color token types +export type ColorTuple = [string, number, number, number, number?]; + +export type ColorObject = { mode: Colorspaces; alpha?: number }; +export type ColorOptions = { + alpha?: number; + lightness?: number; + temperature?: number; + colorspace?: HueColorSpaces; + luminance?: number; + saturation?: number; + + lightMode?: ColorToken; + darkMode?: ColorToken; + + contrast?: number; + colorSpace?: HueColorSpaces; +}; +export type ColorDistanceOptions = { + weights?: [number, number, number, number]; + mode?: Colorspaces; +}; + +/** + * @type + * @description This object returns the lightMode and darkMode optimized version of a color with support to add color vision deficiency simulation to the final color result. + */ +export type AdaptivePaletteOptions = { + backgroundColor?: { light?: ColorToken; dark?: ColorToken }; + viewingConditions?: ViewingConditions; + colorBlind?: boolean; +}; + +export type InterpolatorOptions = Pick< + Options, + | 'easingFunc' + | 'hueInterpolator' + | 'chromaInterpolator' + | 'hueFixup' + | 'lightnessInterpolator' +>; + +/** + * @type + * @description The override parameters for palette functions. + */ +type Options = { + /** + * The easing function to use. + * @param t Any value between 0 and 1 + * @returns A number. + */ + easingFunc?: (t: number) => number; + + /** + *@param The interpolation method to use on the hue channel. + */ + hueInterpolator?: Interpolator; + + /** + *@param The interpolation method to use on the chroma channel. + */ + chromaInterpolator?: Interpolator; + + /** + *@param The type of hue fixup to apply to the hue channels during interpolation. + */ + hueFixup?: (arr: number[]) => number[]; + + /** + *@param The interpolation method to use on the lightness channel. + */ + lightnessInterpolator?: Interpolator; + /** + *@param The color to pass through during interpolation. + */ + via?: Tone; + + /** + *@param The amount of hue angles to increment each iteration with. + */ + hueStep?: number; + + /** + * * @param earthtone The earthtone to interpolate with. + */ + earthtones?: + | 'light-gray' + | 'silver' + | 'sand' + | 'tupe' + | 'mahogany' + | 'brick-red' + | 'clay' + | 'cocoa' + | 'dark-brown' + | 'dark'; + + /** + *@param The amount of samples to return in the result collection. + */ + samples?: number; + + /** + * * @param minLightness Minimum lightness value (range 0-100). + */ + minLightness?: number; + /** + * @param maxLightness Maximum lightness value (range 0-100). + */ + maxLightness?: number; +}; + +export type PairedSchemeOptions = Omit< + Options, + 'earthtones' | 'maxLightness' | 'minLightness' +>; +export type EarthtoneOptions = Omit< + Options, + 'hueStep' | 'via' | 'maxLightness' | 'minLightness' +>; + +export type HueShiftOptions = Omit & + InterpolatorOptions; +export type Interpolator = (arr: number[]) => (t: number) => number; +export type Tone = 'light' | 'dark'; +export type DeficiencyType = 'red' | 'blue' | 'green' | 'monochromacy'; +export type HueFamily = + | 'red-purple' + | 'red' + | 'yellow-red' + | 'yellow' + | 'green-yellow' + | 'green' + | 'blue-green' + | 'blue' + | 'purple-blue' + | 'purple'; + +export type DivergingScheme = + | 'Spectral' + | 'RdYlGn' + | 'RdBu' + | 'PiYG' + | 'PRGn' + | 'RdYlBu' + | 'BrBG' + | 'RdGy' + | 'PuOr'; +export type QualitativeScheme = + | 'Set2' + | 'Accent' + | 'Set1' + | 'Set3' + | 'Dark2' + | 'Paired' + | 'Pastel2' + | 'Pastel1'; + +export type SequentialScheme = + | 'OrRd' + | 'PuBu' + | 'BuPu' + | 'Oranges' + | 'BuGn' + | 'YlOrBr' + | 'YlGn' + | 'Reds' + | 'RdPu' + | 'Greens' + | 'YlGnBu' + | 'Purples' + | 'GnBu' + | 'Greys' + | 'YlOrRd' + | 'PuRd' + | 'Blues' + | 'PuBuGn' + | 'Viridis'; + +/** + * @type + * @description Any recognizable color token. + */ +export type ColorToken = number | string | object | ColorTuple; + +/** + * @param + * An array of baseColor tokens (in short, just an array of valid colors) + */ + +/** + * @type + * The property being queried. Used in utilities that perform operations on collections. + */ +export type Factor = + | 'luminance' + | 'temp' + | 'saturation' + | 'contrast' + | 'distance' + | 'lightness' + | 'hue'; + +type Order = 'asc' | 'desc'; + +type callback = unknown; + +type FactorMapper = ( + factor: Factor, + callback: callback, + order?: Order, + colorObj?: boolean +) => (colors: ColorToken[]) => ColorToken[]; + +export type UniformColorSpaces = + | 'lch' + | 'jch' + | 'dlch' + | 'lch' + | 'lch65' + | 'lchuv' + | 'oklch'; +export type Colorspaces = + | 'a98' + | 'cubehelix' + | 'dlab' + | 'jab' + | 'lab' + | 'lab65' + | 'lrgb' + | 'luv' + | 'oklab' + | 'rgb' + | HueColorSpaces; + +export type HueColorSpaces = + | UniformColorSpaces + | 'hsl' + | 'hsv' + | 'hsi' + | 'hwb' + | 'okhsl' + | 'okhsv'; + +export type ScaleValues = + | '50' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900'; + +export type TailwindColorFamilies = + | 'indigo' + | 'gray' + | 'zinc' + | 'neutral' + | 'stone' + | 'red' + | 'orange' + | 'amber' + | 'yellow' + | 'lime' + | 'green' + | 'emerald' + | 'teal' + | 'sky' + | 'blue' + | 'violet' + | 'purple' + | 'fuchsia' + | 'pink' + | 'rose'; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..fff09386 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,965 @@ +import hueTempMap from './color-maps/samples/hueTemperature.js'; +import { + adjustHue, + customConcat, + expressionParser, + floorCeil, + inRange, + lte, + matchLightnessChannel, + max, + min, + random +} from './helpers.js'; +import { toHex } from './converters.js'; +import { + interpolate, + wcagLuminance, + modeRgb, + useMode, + modeLch, + converter, + wcagContrast, + easingSmootherstep, + modeLab, + nearest, + differenceHyab +} from 'culori/fn'; +import 'culori/css'; +import { + ColorToken, + HueColorSpaces, + Factor, + Order, + callback, + HueFamily, + DeficiencyType +} from './types.js'; + +import { matchChromaChannel, sortedArr, checkArg } from './helpers.js'; + +/** + * + Gets the hue family which a a color belongs to with the overtone included (if it has one.). For achromatic colors it returns the string "gray". + * @param color The color to query its shade or hue family. + * @returns The name of the hue family for example red or green. + * @example + * + * import { getHue } from 'huetiful-js' + + +console.log(getHue("#310000")) +// red + */ +function getHueFamily(color: ColorToken, mode?: HueColorSpaces): HueFamily { + //Capture the hue value + + return Object.keys(hueTempMap) + .map((hue) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + + // @ts-ignore + var [hueVals, minVal, maxVal] = [ + customConcat(hueTempMap[hue]), + min(hueVals), + max(hueVals) + ]; + const bool = customConcat(hueTempMap[hue]).some(() => + inRange(getChannel(`${mode}.h`)(color), minVal, maxVal) + ); + + return bool && hue; + }) + .filter((val) => typeof val === 'string')[0] as HueFamily; +} + +function lightnessPredicate(colorspace) { + return getChannel(`${matchLightnessChannel(colorspace)}`); +} + +function temperaturePredicate(factor: number, temp: 'warm' | 'cool'): boolean { + return Object.keys(hueTempMap).some((val) => + inRange( + floorCeil(factor), + hueTempMap[val][temp][0], + hueTempMap[val][temp][1] + ) + ); +} + +/** + * + * Checks if a color can be roughly classified as a cool color. Returns true if color is a cool color else false. + * @param color The color to check the temperature. + * @returns True or false. + * @example + * + * import { isCool } from 'huetiful-js' + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000" +]; + + +console.log(isCool(sample[2])); +// false + +console.log(map(sample, isCool)); + +// [ true, false, true] + + + + */ +function isCool(color: ColorToken): boolean { + // First we need to get the hue value which we'll pass to the predicate + + return temperaturePredicate(getChannel('lch.h')(color), 'cool'); +} + +/** + * + * Checks if a color can be roughly classified as a warm color. Returns true if color is a warm color else false. + * @param color The color to check the temperature. + * @returns True or false. + * @example import { isWarm } from 'huetiful-js' + +let sample = [ + "#00ffdc", + "#00ff78", + "#00c000" +]; + + + +console.log(isWarm(sample[2])); +//true + +console.log(map(sample, isWarm)); + + +// [ false, true, false] + + */ +function isWarm(color: ColorToken): boolean { + return temperaturePredicate(getChannel('lch.h')(color), 'cool'); +} + +//// Global predicates used by utils such as getNearestHue +function contrastPredicate(color) { + return (against) => getContrast(color, against); +} + +function huePredicate(colorSpace: string) { + return (color: ColorToken) => + getChannel(`${checkArg(colorSpace, 'jch')}.h`)(color); +} +function chromaPredicate(colorspace) { + return (color: ColorToken) => + getChannel(matchChromaChannel(colorspace))(color); +} + +// The baseFunc for getting specifified factor extremums +function baseFunc( + factor: Factor, + collection: ColorToken[] | object, + cb: callback, + order?: Order, + colorObj?: boolean +) { + const result: Array<{ factor: number; name: ColorToken }> | number = + sortedArr( + factor, + cb, + order as Order, + colorObj + )(collection).filter((el) => el[factor] !== undefined); + + return (colorObj && result[0]) || result[0][factor]; +} + +/** + * + * Gets the smallest contrast value from the passed in colors compared against a sample color. + * @param colors The array of colors to query the color with the smallest contrast value. + * @param colorObj Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false. + * @param mode THe mode colorspace to retrieve the contrast value from. + * @returns The smallest contrast value in the colors passed in or a custom object. + * @example + * + * import { getNearestContrast } from 'huetiful-js' + * +console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green")); +// 2.4061390502133424 + +console.log(getNearestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true)); +// { contrast: 2.4061390502133424, name: '#a1bd2f' } + */ +function getNearestContrast( + collection: ColorToken[] | object, + against: ColorToken, + colorObj?: boolean +) { + return baseFunc( + 'contrast', + colors, + contrastPredicate(against), + 'desc', + colorObj + ); +} + +/** + * + * Gets the largest contrast value from the passed in colors compared against a sample color. + * @param colors The array of colors to query the color with the largest contrast value. + * @param colorObj Optional boolean that makes the function return a custom object with factor (contrast) and name of the color as keys. Default is false. + * @param mode THe mode colorspace to retrieve the contrast value from. + * @returns The largest contrast value in the colors passed in or a custom object. + * @example + * +import { getFarthestContrast } from 'huetiful-js' + +console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green")); +// 3.08355493246362 + +console.log(getFarthestContrast(["b2c3f1", "#a1bd2f", "#f3bac1"], "green", true)); +// { contrast: 3.08355493246362, name: '#f3bac1' } + + */ + +function getFarthestContrast( + collection: ColorToken[] | object, + against: ColorToken, + colorObj?: boolean +): number | { factor: number; name: ColorToken } { + return baseFunc( + 'contrast', + colors, + contrastPredicate(against), + 'desc', + colorObj + ); +} + +/** + * + * Gets the smallest chroma/saturation value from the passed in colors. + * @param colors The array of colors to query the color with the smallest chroma/saturation value. + * @param colorspace The mode color space to perform the computation in. + * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. + * @returns The smallest chroma/saturation value in the colors passed in or a custom object. + * @example + * + * import { getNearestChroma } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getNearestChroma(sample, 'lch')) +// 22.45669293295522 + */ +function getNearestChroma( + collection: ColorToken[] | object, + colorspace?: HueColorSpaces, + colorObj = false +): number | { factor: number; color: ColorToken } { + return baseFunc( + 'saturation', + colors, + chromaPredicate(colorspace), + 'asc', + colorObj + ); +} + +/** + * + * Gets the largest saturation value from the passed in colors. + * @param colors The array of colors to query the color with the largest saturation value. + * @param colorSpace The mode color space to perform the computation in. + * @param colorObj Optional boolean that makes the function return a custom object with factor (saturation) and name of the color as keys. Default is false. + * @returns The largest saturation value in the colors passed in or a custom object. + * @example + * + * import { getFarthestChroma } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getFarthestChroma(sample, 'lch')) +// 67.22120855010492 + */ +function getFarthestChroma( + collection: ColorToken[] | object, + colorObj = false +): number | { factor: number; color: ColorToken } { + return baseFunc('saturation', colors, chromaPredicate, 'desc', colorObj); +} + +/** + * + * Gets the smallest hue value from the passed in colors. + * @param colors The array of colors to query the color with the smallest hue value. + * @param colorspace The mode color space to perform the computation in. + * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. + * @returns The smallest hue value in the colors passed in or a custom object. + * @example + * + * import { getNearestHue } from 'huetiful-js' + +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getNearestHue(sample, 'lch')) +// 12.462831644544274 + */ +function getNearestHue( + collection: ColorToken[] | object, + colorspace?: HueColorSpaces | string, + colorObj = false +): number | { factor: number; color: ColorToken } { + return baseFunc('hue', colors, huePredicate(colorspace), 'asc', colorObj); +} + +/** + * + * Gets the largest hue value from the passed in colors. + * @param colors The array of colors to query the color with the largest hue value. + * @param colorspace The mode color space to perform the computation in. + * @param colorObj Optional boolean that makes the function return a custom object with factor (hue) and name of the color as keys. Default is false. + * @returns The largest hue value in the colors passed in or a custom object. + * @example + * + * import { getFarthestHue } from 'huetiful-js' +let sample = ['b2c3f1', '#a1bd2f', '#f3bac1'] + +console.log(getFarthestHue(sample, 'lch')) +// 273.54920266436477 + */ +function getFarthestHue( + collection: ColorToken[] | object, + colorspace?: HueColorSpaces, + colorObj = false +): number | { factor: number; color: ColorToken } { + return baseFunc('hue', colors, huePredicate(colorspace), 'desc', colorObj); +} + +/** + * + * Gets the complementary hue of the passed in color. The function is internally guarded against achromatic colors. + * @param color The color to retrieve its complimentary hue. + * @param colorObj Optional boolean whether to return an object with the result color hue family or just the result color. Default is false. + * @returns An object with the hue family and complimentary color as keys. + * @example + *import { getComplimentaryHue } from "huetiful-js"; + * + * +console.log(getComplimentaryHue("pink", true)) +//// { hue: 'blue-green', color: '#97dfd7ff' } + +console.log(getComplimentaryHue("purple")) +// #005700ff + */ +function getComplimentaryHue( + color: ColorToken, + colorspace?: HueColorSpaces, + colorObj = false +): { hue: string; color: ColorToken } | ColorToken { + const modeChannel = `${colorspace}.h`; + + const complementaryHue: number = adjustHue( + getChannel(modeChannel)(color) + 180 * random(0.965, 1) + ); + + const result: ColorToken | { hue: string; color: ColorToken } = + (complementaryHue && { + hue: getHueFamily(complementaryHue), + color: toHex(setChannel(modeChannel)(color, complementaryHue)) + }) || { hue: 'gray', color: color }; + + return (colorObj && result) || result['color']; +} + +/** + * + * Sets the value for the specified channel in a color. + * @param color Any recognizable color token. + * @param mc The mode and channel to work with. For example 'rgb.b'. + * @param value The value to set on the queried channel. Also supports expressions as strings e.g set('lch.c)("#fc23a1","*0.5") + * @returns color The mutated color. + * + * @example + * + * import { setChannel } from 'huetiful-js' + +let myColor = setChannel('lch.h')('green',10) + +console.log(getChannel('lch.h')(myColor)) +// 10 + */ + +function setChannel(mc: string) { + return (color: ColorToken, value: number | string): ColorToken => { + const [mode, channel] = mc.split('.'); + // @ts-ignore + const src: ColorToken = converter(mode)(toHex(color)); + + if (channel) { + if (typeof value === 'number') { + src[channel] = value; + } else if (typeof value === 'string') { + expressionParser(src, channel, value); + } else { + throw new Error(`unsupported value for setChannel`); + } + + return src; + } else { + throw new Error(`unknown channel ${channel} in mode ${mode}`); + } + }; +} + +/** + * + * Gets the value specifified channel on the color. + * @param mc The mode and channel to be retrieved. For example "rgb.b" will return the value of the blue channel in the RGB color space of that color. + * @param color The color being queried. + * @returns value The value of the queried channel. + * @example + * + * import { getChannel } from 'huetiful-js' + +console.log(getChannel('rgb.g')('#a1bd2f')) +// 0.7411764705882353 + * */ +function getChannel(mc: string) { + return (color: ColorToken): number => { + const [mode, channel] = mc.split('.'); + // @ts-ignore + const src = converter(mode)(toHex(color)); + + if (channel) { + return src[channel]; + } else { + throw Error(`unknown channel ${channel} in mode ${mode}`); + } + }; +} + +/** @alias + * Gets the luminance value of that color as defined by WCAG. + * @param color The color to query. + * @returns value The color's luminance value. + * @example + * + * import { getLuminance } from 'huetiful-js' + +console.log(getLuminance('#a1bd2f')) +// 0.4417749513730954 + */ +function getLuminance(color: ColorToken): number { + return wcagLuminance(toHex(color)); +} + +const { pow, abs } = Math; +const toRgb = useMode(modeRgb); +/** + * + * Sets the luminance by interpolating the color with black (to decrease luminance) or white (to increase the luminance). + * @param color The color to set luminance + * @param lum The amount of luminance to set. The value range is normalised between [0,1] + * @returns The mutated color with the modified properties. + * @example + * + * import { setLuminance, getLuminance } from 'huetiful-js' + +let myColor = setLuminance('#a1bd2f', 0.5) + +console.log(getLuminance(myColor)) +// 0.4999999136285792 + */ +function setLuminance(color: ColorToken, lum: number): ColorToken { + const white = '#ffffff', + black = '#000000'; + + const EPS = 1e-7; + let MAX_ITER = 20; + + if (lum !== undefined && typeof lum == 'number') { + (lum == 0 && lum) || black || (lum == 1 && !lum) || white; + + // compute new color using... + // @ts-ignore + const cur_lum = wcagLuminance(color); + + color = toRgb(toHex(color)); + + const test = (low: ColorToken, high: ColorToken) => { + //Must add the overrides object to change parameters like easings, fixups, and the mode to perform the computations in. + // @ts-ignore + const mid = interpolate([low, high])(0.5); + const lm = getLuminance(color); + // @ts-ignore + if (abs(lum - lm > EPS) || !MAX_ITER--) { + // close enough + return mid; + } + + if (lm > lum) { + return test(low, mid); + } else { + return test(mid, high); + } + }; + + let rgb: ColorToken; + if (cur_lum > lum) { + rgb = test(black, color); + } else { + rgb = test(color, white); + } + color = rgb; + return color; + } + // spreading the array values (r,g,b) + return rgb2luminance(color); +} + +function rgb2luminance(color: ColorToken): number { + color = toRgb(toHex(color)); + + // relative luminance + // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + return ( + 0.7152 * luminance_x(color['g']) + + 0.2126 * luminance_x(color['r']) + + 0.0722 * luminance_x(color['b']) + ); +} + +function luminance_x(x: number) { + x /= 255; + return (lte(x, 0.03928) && x / 12.92) || pow((x + 0.055) / 1.055, 2.4); +} + +/** + * + * Sets the opacity of a color. Also gets the alpha value of the color if the value param is omitted + * @param color The color with the targeted opacity/alpha channel. + * @param value The value to apply to the opacity channel. The value is between [0,1] + * @returns color The resulting color. Returns an 8 character hex code. + * @example + * + * // Getting the alpha +console.log(alpha('#a1bd2f0d')) +// 0.050980392156862744 + +// Setting the alpha + +let myColor = alpha('b2c3f1', 0.5) + +console.log(myColor) + +// #b2c3f180 + */ + +function alpha(color: ColorToken, value?: number | string): number { + // We never perfom an operation on an undefined color. Defaults to pure black + color = color || 'black'; + + const channel = 'alpha'; + const lch = useMode(modeLch); + var src: ColorToken = lch(toHex(color)); + if (typeof value === 'undefined' || null) { + return src[channel]; + } else if (typeof value === 'number') { + if (inRange(value, 0, 1)) { + src[channel] = value; + } else { + src[channel] = value / 100; + } + } else if (typeof value === 'string') { + expressionParser(src, channel, value); + } + // @ts-ignore + return toHex(src); +} + +/** + * + * Gets the contrast between the passed in colors. + * @param color + * @param against + * @returns The relative luminance of the lightest color. + * @example + * + * import { getContrast } from 'huetiful-js' + * + * console.log(getContrast("black", "white")); + * // 21 + */ +function getContrast(color: ColorToken, against: ColorToken): number { + return wcagContrast(toHex(color), toHex(against)); +} + +/** + * + * Returns the hue which is biasing the passed in color + * @param color The color to query its overtone. + * @returns The name of the overtone hue. If an achromatic color is passed in it return the string gray otherwise if the color has no bias it returns false. + * @example + * + * import { overtone } from "huetiful-js"; + * +console.log(overtone("fefefe")) +// 'gray' + +console.log(overtone("cyan")) +// 'green' + +console.log(overtone("blue")) +// false + */ +function overtone(color: ColorToken): string | boolean { + var hue = getHueFamily(color); + + // We check if the color can be found in the defined ranges + return ( + (isAchromatic(color) && 'gray') || + (/-/.test(hue) && hue.split('-')[1]) || + false + ); +} + +/** + * + * Gets the smallest lightness value from the passed in colors. + * @param colors The array of colors to query the color with the smallest lightness value. + * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. + * @param mode THe mode colorspace to retrieve the lightness value from. + * @returns The smallest lightness value in the colors passed in or a custom object. + * @example + * + * import { getNearestLightness } from 'huetiful-js' + +let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] + +console.log(getNearestLightness(sample, true)) + +// { lightness: 72.61647882089876, name: '#a1bd2f' } + + */ +function getNearestLightness( + collection: ColorToken[] | object, + colorspace?: HueColorSpaces, + colorObj = false +): number | { factor: number; color: ColorToken } { + // @ts-ignore + + return baseFunc( + 'lightness', + colors, + lightnessPredicate(colorspace), + 'asc', + colorObj + ); +} + +/** + * + * Gets the largest lightness value from the passed in colors. + * @param colors The array of colors to query the color with the largest lightness value. + * @param colorObj Optional boolean that makes the function return a custom object with factor (lightness) and name of the color as keys. Default is false. + * @param colorspace THe mode colorspace to retrieve the lightness value from. + * @returns The largest lightness value in the colors passed in or a custom object. + * @example + * + * import { getFarthestLightness } from 'huetiful-js' + +let sample = ["b2c3f1", "#a1bd2f", "#f3bac1"] + +console.log(getFarthestLightness(sample, true)) + +// { lightness: 80.94668903360088, name: '#f3bac1' } + + */ +function getFarthestLightness( + collection: ColorToken[] | object, + colorspace?: HueColorSpaces, + colorObj = false +): number | { factor: number; color: ColorToken } { + // @ts-ignore + return baseFunc( + 'lightness', + colors, + lightnessPredicate(colorspace), + 'asc', + colorObj + ); +} + +const toLab = useMode(modeLab); +/** + * + * Darkens the color by reducing the lightness channel. . + * @param color The color to darken. + * @param value The amount to darken with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") + * @returns color The darkened color. + * @example + * + * + + */ +function darken(color: ColorToken, value: number | string): ColorToken { + const Kn = 18; + const channel = 'l'; + + const src = toLab(toHex(color)); + + if (typeof value === 'number') { + src['l'] -= Kn * easingSmootherstep(value / 100); + } else if (typeof value === 'string') { + // @ts-ignore + expressionParser(src, channel, value || 1); + } + + return toHex(src); +} + +/** + * + * @param color The color to brighten. + * @param value The amount to brighten with. Also supports expressions as strings e.g darken("#fc23a1","*0.5") + * @param mode The color space to compute the color in. Any color space with a lightness channel will do (including HWB) + * @returns + */ +function brighten( + color: ColorToken, + value: number | string, + colorspace +): ColorToken { + const src = toLab(toHex(color)); + const ch = matchLightnessChannel(colorspace).split('.')[1]; + let result = src; + if (typeof value == 'number') { + result[ch] -= 18 * easingSmootherstep(Math.abs(value) / 100); + } else if (typeof value == 'string') { + //@ts-ignore + result = expressionParser(src, ch, value); + } + + return toHex(result); +} + +/** + * + * Checks if a color is achromatic(without hue or simply grayscale). + * @param color The color to test if it is achromatic or not. + * @returns boolean Returns true if the color is achromatic else false + * @example + * + * import { isAchromatic } from "huetiful-js"; +import { formatHex8, interpolate, samples } from "culori" + + +isAchromatic('pink') +// false + +let sample = [ + "#164100", + "#ffff00", + "#310000", + 'pink' +]; + +console.log(map(sample, isAchromatic)); + +// [false, false, false,false] + +isAchromatic('gray') +// Returns true + + +console.log(map(sample, isAchromatic)); + + +// we create an interpolation using black and white +let f = interpolate(["black", "white"]); + +//We then create 12 evenly spaced samples and pass them to f as the `t` param required by an interpolating function. +// Lastly we convert the color to hex for brevity for this example (otherwise color objects work fine too.) +let grays = map(samples(12), (c) => formatHex8(f(c))); +console.log(map(grays, isAchromatic)); + +// + [ false, true, true, + true, true, true, + true, true, true, + true, true, false +] + + */ +function isAchromatic(color: ColorToken, mode?: HueColorSpaces): boolean { + // If a color has no lightness then it has no hue so its technically achromatic + const props = { + lightness: getChannel(`${matchLightnessChannel(mode as string)}`), + chroma: getChannel(`${matchChromaChannel(mode as string)}`)(color) + }; + + // Check if the saturation channel is zero or falsy for color spaces with saturation/chroma channel + // @ts-ignore + return (props['chroma'] && props['lightness'] !== false) || 0 || NaN; +} + +import { + filterDeficiencyDeuter, + filterDeficiencyProt, + filterDeficiencyTrit, + filterGrayscale +} from 'culori/fn'; +import { colors } from './colors.js'; + +// This module is focused on creating color blind safe palettes that adhere to the minimum contrast requirements + +// How can I achieve this ? +// 1. First I pass the color(s) to a color vision deficiency simulation function +// 2. Check if the color has the minimum required contrast as compared to a dark/light mode surface which can optionally be overriden +// 3. Check the min luminance contrast ratio between the color and background. +// 4. Find out which channels do I need to tweak in order to fix up the colors. +// 5. Maybe provide an optional adaptive boolean which returns dark/light mode variant colors of the color blind safe palettes. + +// Add reference to articles +// Read more about the minimum accepted values for palette accessibility + +/** + * + * Returns the color as a simulation of the passed in type of color vision deficiency with the deficiency filter's intensity determined by the severity value. + * @param deficiencyType The type of color vision deficiency. To avoid writing the long types, the expected parameters are simply the colors that are hard to perceive for the type of color blindness. For example those with 'tritanopia' are unable to perceive 'blue' light. Default is 'red' when the defeciency parameter is undefined or any falsy value. + * @see For a deep dive on color vision deficiency go to + * @param color The color to return its deficiency simulated variant. + * @param severity The intensity of the filter. The exepected value is between [0,1]. For example 0.5 + * @returns The color as its simulated variant as a hexadecimal string. + * @example + * + * import { colorDeficiency, toHex } from 'huetiful-js' + +// Here we are simulating color blindness of tritanomaly or we can't see 'blue'. +// We are passing in our color as an array of channel values in the mode "rgb". The severity is set to 0.1 +let tritanomaly = colorDeficiency('blue') +console.log(tritanomaly(['rgb', 230, 100, 50, 0.5], 0.1)) +// #dd663680 + +// Here we are simulating color blindness of tritanomaly or we can't see 'red'. The severity is not explicitly set so it defaults to 1 +let protanopia = colorDeficiency('red') +console.log(protanopia({ h: 20, w: 50, b: 30, mode: 'hwb' })) +// #9f9f9f + */ +function colorDeficiency(deficiencyType?: DeficiencyType) { + const baseColorDeficiency = ( + def: 'red' | 'blue' | 'green' | 'monochromacy', + col: ColorToken, + sev: number + ) => { + let result: ColorToken; + col = toHex(col); + switch (def) { + case 'blue': // @ts-ignore + result = filterDeficiencyTrit(sev)(col); + break; + case 'red': // @ts-ignore + result = filterDeficiencyProt(sev)(col); + break; + case 'green': // @ts-ignore + result = filterDeficiencyDeuter(sev)(col); + break; + case 'monochromacy': + result = filterGrayscale(sev, 'lch')(col); + break; + } + + return toHex(result); + }; + + return (color: ColorToken, severity = 1) => { + // Store the keys of deficiency types + const deficiencies: string[] = ['red', 'blue', 'green', 'monochromacy']; + // Cast 'red' as the default parameter + deficiencyType = checkArg(deficiencyType, 'red') as DeficiencyType; + + if ( + typeof deficiencyType === 'string' && + deficiencies.some((el) => el === deficiencyType) + ) { + return baseColorDeficiency(deficiencyType, color, severity); + } else { + throw Error( + `Unknown color vision deficiency ${deficiencyType}. The options are the strings 'red' | 'blue' | 'green' | 'monochromacy'` + ); + } + }; +} + +/** + * + * @param collection The collection of colors to search for nearest colors + * @param color The color to use for distance comparison + * @param num The number of colors to return, if the value is above the colors in the available sample, the entire collection is returned with colors ordered in ascending order using the differenceHyab metric. + * @returns An array of colors. + * @example + * + * let cols = colors('all', '500') + * +console.log(getNearestColor(cols, 'blue', 3)); + // [ '#a855f7', '#8b5cf6', '#d946ef' ] + */ +function getNearestColor( + collection: ColorToken[] | 'tailwind', + color: ColorToken, + num = 1 +): ColorToken | ColorToken[] { + const cb = (collection, color) => { + // + return nearest( + collection as ColorToken[], + differenceHyab(), + (color) => color as string + )(color as string, num); + }; + let result: ColorToken; + + if (collection === 'tailwind') { + result = cb(colors('all'), color); + } else { + result = cb(collection, color); + } + + return result; +} + +export { + getNearestColor, + colorDeficiency, + brighten, + isAchromatic, + alpha, + darken, + getFarthestLightness, + getNearestLightness, + overtone, + getFarthestChroma, + getNearestChroma, + setChannel, + setLuminance, + getChannel, + getLuminance, + getContrast, + getFarthestContrast, + getNearestContrast, + isCool, + isWarm, + getHueFamily, + getComplimentaryHue, + getFarthestHue, + getNearestHue +}; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..332d51e7 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,1074 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./templates/*.html'], + presets: [], + darkMode: 'media', // or 'class' + theme: { + accentColor: ({ theme }) => ({ + ...theme('colors'), + auto: 'auto' + }), + animation: { + none: 'none', + spin: 'spin 1s linear infinite', + ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite', + pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite', + bounce: 'bounce 1s infinite' + }, + aria: { + busy: 'busy="true"', + checked: 'checked="true"', + disabled: 'disabled="true"', + expanded: 'expanded="true"', + hidden: 'hidden="true"', + pressed: 'pressed="true"', + readonly: 'readonly="true"', + required: 'required="true"', + selected: 'selected="true"' + }, + aspectRatio: { + auto: 'auto', + square: '1 / 1', + video: '16 / 9' + }, + backdropBlur: ({ theme }) => theme('blur'), + backdropBrightness: ({ theme }) => theme('brightness'), + backdropContrast: ({ theme }) => theme('contrast'), + backdropGrayscale: ({ theme }) => theme('grayscale'), + backdropHueRotate: ({ theme }) => theme('hueRotate'), + backdropInvert: ({ theme }) => theme('invert'), + backdropOpacity: ({ theme }) => theme('opacity'), + backdropSaturate: ({ theme }) => theme('saturate'), + backdropSepia: ({ theme }) => theme('sepia'), + backgroundColor: ({ theme }) => theme('colors'), + backgroundImage: { + none: 'none', + 'gradient-to-t': 'linear-gradient(to top, var(--tw-gradient-stops))', + 'gradient-to-tr': + 'linear-gradient(to top right, var(--tw-gradient-stops))', + 'gradient-to-r': 'linear-gradient(to right, var(--tw-gradient-stops))', + 'gradient-to-br': + 'linear-gradient(to bottom right, var(--tw-gradient-stops))', + 'gradient-to-b': 'linear-gradient(to bottom, var(--tw-gradient-stops))', + 'gradient-to-bl': + 'linear-gradient(to bottom left, var(--tw-gradient-stops))', + 'gradient-to-l': 'linear-gradient(to left, var(--tw-gradient-stops))', + 'gradient-to-tl': 'linear-gradient(to top left, var(--tw-gradient-stops))' + }, + backgroundOpacity: ({ theme }) => theme('opacity'), + backgroundPosition: { + bottom: 'bottom', + center: 'center', + left: 'left', + 'left-bottom': 'left bottom', + 'left-top': 'left top', + right: 'right', + 'right-bottom': 'right bottom', + 'right-top': 'right top', + top: 'top' + }, + backgroundSize: { + auto: 'auto', + cover: 'cover', + contain: 'contain' + }, + blur: { + 0: '0', + none: '0', + sm: '4px', + DEFAULT: '8px', + md: '12px', + lg: '16px', + xl: '24px', + '2xl': '40px', + '3xl': '64px' + }, + borderColor: ({ theme }) => ({ + ...theme('colors'), + DEFAULT: theme('colors.gray.200', 'currentColor') + }), + borderOpacity: ({ theme }) => theme('opacity'), + borderRadius: { + none: '0px', + sm: '0.125rem', + DEFAULT: '0.25rem', + md: '0.375rem', + lg: '0.5rem', + xl: '0.75rem', + '2xl': '1rem', + '3xl': '1.5rem', + full: '9999px' + }, + borderSpacing: ({ theme }) => ({ + ...theme('spacing') + }), + borderWidth: { + DEFAULT: '1px', + 0: '0px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + boxShadow: { + sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)', + DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)', + md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)', + lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)', + xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)', + '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)', + inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)', + none: 'none' + }, + boxShadowColor: ({ theme }) => theme('colors'), + brightness: { + 0: '0', + 50: '.5', + 75: '.75', + 90: '.9', + 95: '.95', + 100: '1', + 105: '1.05', + 110: '1.1', + 125: '1.25', + 150: '1.5', + 200: '2' + }, + caretColor: ({ theme }) => theme('colors'), + colors: ({ colors }) => ({ + inherit: colors.inherit, + current: colors.current, + transparent: colors.transparent, + black: colors.black, + white: colors.white, + slate: colors.slate, + gray: colors.gray, + zinc: colors.zinc, + neutral: colors.neutral, + stone: colors.stone, + red: colors.red, + orange: colors.orange, + amber: colors.amber, + yellow: colors.yellow, + lime: colors.lime, + green: colors.green, + emerald: colors.emerald, + teal: colors.teal, + cyan: colors.cyan, + sky: colors.sky, + blue: colors.blue, + indigo: colors.indigo, + violet: colors.violet, + purple: colors.purple, + fuchsia: colors.fuchsia, + pink: colors.pink, + rose: colors.rose + }), + columns: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + '3xs': '16rem', + '2xs': '18rem', + xs: '20rem', + sm: '24rem', + md: '28rem', + lg: '32rem', + xl: '36rem', + '2xl': '42rem', + '3xl': '48rem', + '4xl': '56rem', + '5xl': '64rem', + '6xl': '72rem', + '7xl': '80rem' + }, + container: {}, + content: { + none: 'none' + }, + contrast: { + 0: '0', + 50: '.5', + 75: '.75', + 100: '1', + 125: '1.25', + 150: '1.5', + 200: '2' + }, + cursor: { + auto: 'auto', + default: 'default', + pointer: 'pointer', + wait: 'wait', + text: 'text', + move: 'move', + help: 'help', + 'not-allowed': 'not-allowed', + none: 'none', + 'context-menu': 'context-menu', + progress: 'progress', + cell: 'cell', + crosshair: 'crosshair', + 'vertical-text': 'vertical-text', + alias: 'alias', + copy: 'copy', + 'no-drop': 'no-drop', + grab: 'grab', + grabbing: 'grabbing', + 'all-scroll': 'all-scroll', + 'col-resize': 'col-resize', + 'row-resize': 'row-resize', + 'n-resize': 'n-resize', + 'e-resize': 'e-resize', + 's-resize': 's-resize', + 'w-resize': 'w-resize', + 'ne-resize': 'ne-resize', + 'nw-resize': 'nw-resize', + 'se-resize': 'se-resize', + 'sw-resize': 'sw-resize', + 'ew-resize': 'ew-resize', + 'ns-resize': 'ns-resize', + 'nesw-resize': 'nesw-resize', + 'nwse-resize': 'nwse-resize', + 'zoom-in': 'zoom-in', + 'zoom-out': 'zoom-out' + }, + divideColor: ({ theme }) => theme('borderColor'), + divideOpacity: ({ theme }) => theme('borderOpacity'), + divideWidth: ({ theme }) => theme('borderWidth'), + dropShadow: { + sm: '0 1px 1px rgb(0 0 0 / 0.05)', + DEFAULT: ['0 1px 2px rgb(0 0 0 / 0.1)', '0 1px 1px rgb(0 0 0 / 0.06)'], + md: ['0 4px 3px rgb(0 0 0 / 0.07)', '0 2px 2px rgb(0 0 0 / 0.06)'], + lg: ['0 10px 8px rgb(0 0 0 / 0.04)', '0 4px 3px rgb(0 0 0 / 0.1)'], + xl: ['0 20px 13px rgb(0 0 0 / 0.03)', '0 8px 5px rgb(0 0 0 / 0.08)'], + '2xl': '0 25px 25px rgb(0 0 0 / 0.15)', + none: '0 0 #0000' + }, + fill: ({ theme }) => ({ + none: 'none', + ...theme('colors') + }), + flex: { + 1: '1 1 0%', + auto: '1 1 auto', + initial: '0 1 auto', + none: 'none' + }, + flexBasis: ({ theme }) => ({ + auto: 'auto', + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + '1/5': '20%', + '2/5': '40%', + '3/5': '60%', + '4/5': '80%', + '1/6': '16.666667%', + '2/6': '33.333333%', + '3/6': '50%', + '4/6': '66.666667%', + '5/6': '83.333333%', + '1/12': '8.333333%', + '2/12': '16.666667%', + '3/12': '25%', + '4/12': '33.333333%', + '5/12': '41.666667%', + '6/12': '50%', + '7/12': '58.333333%', + '8/12': '66.666667%', + '9/12': '75%', + '10/12': '83.333333%', + '11/12': '91.666667%', + full: '100%' + }), + flexGrow: { + 0: '0', + DEFAULT: '1' + }, + flexShrink: { + 0: '0', + DEFAULT: '1' + }, + fontFamily: { + sans: [ + 'ui-sans-serif', + 'system-ui', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + '"Noto Color Emoji"' + ], + serif: [ + 'ui-serif', + 'Georgia', + 'Cambria', + '"Times New Roman"', + 'Times', + 'serif' + ], + mono: [ + 'ui-monospace', + 'SFMono-Regular', + 'Menlo', + 'Monaco', + 'Consolas', + '"Liberation Mono"', + '"Courier New"', + 'monospace' + ] + }, + fontSize: { + xs: ['0.75rem', { lineHeight: '1rem' }], + sm: ['0.875rem', { lineHeight: '1.25rem' }], + base: ['1rem', { lineHeight: '1.5rem' }], + lg: ['1.125rem', { lineHeight: '1.75rem' }], + xl: ['1.25rem', { lineHeight: '1.75rem' }], + '2xl': ['1.5rem', { lineHeight: '2rem' }], + '3xl': ['1.875rem', { lineHeight: '2.25rem' }], + '4xl': ['2.25rem', { lineHeight: '2.5rem' }], + '5xl': ['3rem', { lineHeight: '1' }], + '6xl': ['3.75rem', { lineHeight: '1' }], + '7xl': ['4.5rem', { lineHeight: '1' }], + '8xl': ['6rem', { lineHeight: '1' }], + '9xl': ['8rem', { lineHeight: '1' }] + }, + fontWeight: { + thin: '100', + extralight: '200', + light: '300', + normal: '400', + medium: '500', + semibold: '600', + bold: '700', + extrabold: '800', + black: '900' + }, + gap: ({ theme }) => theme('spacing'), + gradientColorStops: ({ theme }) => theme('colors'), + gradientColorStopPositions: { + '0%': '0%', + '5%': '5%', + '10%': '10%', + '15%': '15%', + '20%': '20%', + '25%': '25%', + '30%': '30%', + '35%': '35%', + '40%': '40%', + '45%': '45%', + '50%': '50%', + '55%': '55%', + '60%': '60%', + '65%': '65%', + '70%': '70%', + '75%': '75%', + '80%': '80%', + '85%': '85%', + '90%': '90%', + '95%': '95%', + '100%': '100%' + }, + grayscale: { + 0: '0', + DEFAULT: '100%' + }, + gridAutoColumns: { + auto: 'auto', + min: 'min-content', + max: 'max-content', + fr: 'minmax(0, 1fr)' + }, + gridAutoRows: { + auto: 'auto', + min: 'min-content', + max: 'max-content', + fr: 'minmax(0, 1fr)' + }, + gridColumn: { + auto: 'auto', + 'span-1': 'span 1 / span 1', + 'span-2': 'span 2 / span 2', + 'span-3': 'span 3 / span 3', + 'span-4': 'span 4 / span 4', + 'span-5': 'span 5 / span 5', + 'span-6': 'span 6 / span 6', + 'span-7': 'span 7 / span 7', + 'span-8': 'span 8 / span 8', + 'span-9': 'span 9 / span 9', + 'span-10': 'span 10 / span 10', + 'span-11': 'span 11 / span 11', + 'span-12': 'span 12 / span 12', + 'span-full': '1 / -1' + }, + gridColumnEnd: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13' + }, + gridColumnStart: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13' + }, + gridRow: { + auto: 'auto', + 'span-1': 'span 1 / span 1', + 'span-2': 'span 2 / span 2', + 'span-3': 'span 3 / span 3', + 'span-4': 'span 4 / span 4', + 'span-5': 'span 5 / span 5', + 'span-6': 'span 6 / span 6', + 'span-7': 'span 7 / span 7', + 'span-8': 'span 8 / span 8', + 'span-9': 'span 9 / span 9', + 'span-10': 'span 10 / span 10', + 'span-11': 'span 11 / span 11', + 'span-12': 'span 12 / span 12', + 'span-full': '1 / -1' + }, + gridRowEnd: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13' + }, + gridRowStart: { + auto: 'auto', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12', + 13: '13' + }, + gridTemplateColumns: { + none: 'none', + subgrid: 'subgrid', + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))', + 7: 'repeat(7, minmax(0, 1fr))', + 8: 'repeat(8, minmax(0, 1fr))', + 9: 'repeat(9, minmax(0, 1fr))', + 10: 'repeat(10, minmax(0, 1fr))', + 11: 'repeat(11, minmax(0, 1fr))', + 12: 'repeat(12, minmax(0, 1fr))' + }, + gridTemplateRows: { + none: 'none', + subgrid: 'subgrid', + 1: 'repeat(1, minmax(0, 1fr))', + 2: 'repeat(2, minmax(0, 1fr))', + 3: 'repeat(3, minmax(0, 1fr))', + 4: 'repeat(4, minmax(0, 1fr))', + 5: 'repeat(5, minmax(0, 1fr))', + 6: 'repeat(6, minmax(0, 1fr))', + 7: 'repeat(7, minmax(0, 1fr))', + 8: 'repeat(8, minmax(0, 1fr))', + 9: 'repeat(9, minmax(0, 1fr))', + 10: 'repeat(10, minmax(0, 1fr))', + 11: 'repeat(11, minmax(0, 1fr))', + 12: 'repeat(12, minmax(0, 1fr))' + }, + height: ({ theme }) => ({ + auto: 'auto', + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + '1/5': '20%', + '2/5': '40%', + '3/5': '60%', + '4/5': '80%', + '1/6': '16.666667%', + '2/6': '33.333333%', + '3/6': '50%', + '4/6': '66.666667%', + '5/6': '83.333333%', + full: '100%', + screen: '100vh', + svh: '100svh', + lvh: '100lvh', + dvh: '100dvh', + min: 'min-content', + max: 'max-content', + fit: 'fit-content' + }), + hueRotate: { + 0: '0deg', + 15: '15deg', + 30: '30deg', + 60: '60deg', + 90: '90deg', + 180: '180deg' + }, + inset: ({ theme }) => ({ + auto: 'auto', + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + full: '100%' + }), + invert: { + 0: '0', + DEFAULT: '100%' + }, + keyframes: { + spin: { + to: { + transform: 'rotate(360deg)' + } + }, + ping: { + '75%, 100%': { + transform: 'scale(2)', + opacity: '0' + } + }, + pulse: { + '50%': { + opacity: '.5' + } + }, + bounce: { + '0%, 100%': { + transform: 'translateY(-25%)', + animationTimingFunction: 'cubic-bezier(0.8,0,1,1)' + }, + '50%': { + transform: 'none', + animationTimingFunction: 'cubic-bezier(0,0,0.2,1)' + } + } + }, + letterSpacing: { + tighter: '-0.05em', + tight: '-0.025em', + normal: '0em', + wide: '0.025em', + wider: '0.05em', + widest: '0.1em' + }, + lineHeight: { + none: '1', + tight: '1.25', + snug: '1.375', + normal: '1.5', + relaxed: '1.625', + loose: '2', + 3: '.75rem', + 4: '1rem', + 5: '1.25rem', + 6: '1.5rem', + 7: '1.75rem', + 8: '2rem', + 9: '2.25rem', + 10: '2.5rem' + }, + listStyleType: { + none: 'none', + disc: 'disc', + decimal: 'decimal' + }, + listStyleImage: { + none: 'none' + }, + margin: ({ theme }) => ({ + auto: 'auto', + ...theme('spacing') + }), + lineClamp: { + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6' + }, + maxHeight: ({ theme }) => ({ + ...theme('spacing'), + none: 'none', + full: '100%', + screen: '100vh', + svh: '100svh', + lvh: '100lvh', + dvh: '100dvh', + min: 'min-content', + max: 'max-content', + fit: 'fit-content' + }), + maxWidth: ({ theme, breakpoints }) => ({ + ...theme('spacing'), + none: 'none', + xs: '20rem', + sm: '24rem', + md: '28rem', + lg: '32rem', + xl: '36rem', + '2xl': '42rem', + '3xl': '48rem', + '4xl': '56rem', + '5xl': '64rem', + '6xl': '72rem', + '7xl': '80rem', + full: '100%', + min: 'min-content', + max: 'max-content', + fit: 'fit-content', + prose: '65ch', + ...breakpoints(theme('screens')) + }), + minHeight: ({ theme }) => ({ + ...theme('spacing'), + full: '100%', + screen: '100vh', + svh: '100svh', + lvh: '100lvh', + dvh: '100dvh', + min: 'min-content', + max: 'max-content', + fit: 'fit-content' + }), + minWidth: ({ theme }) => ({ + ...theme('spacing'), + full: '100%', + min: 'min-content', + max: 'max-content', + fit: 'fit-content' + }), + objectPosition: { + bottom: 'bottom', + center: 'center', + left: 'left', + 'left-bottom': 'left bottom', + 'left-top': 'left top', + right: 'right', + 'right-bottom': 'right bottom', + 'right-top': 'right top', + top: 'top' + }, + opacity: { + 0: '0', + 5: '0.05', + 10: '0.1', + 15: '0.15', + 20: '0.2', + 25: '0.25', + 30: '0.3', + 35: '0.35', + 40: '0.4', + 45: '0.45', + 50: '0.5', + 55: '0.55', + 60: '0.6', + 65: '0.65', + 70: '0.7', + 75: '0.75', + 80: '0.8', + 85: '0.85', + 90: '0.9', + 95: '0.95', + 100: '1' + }, + order: { + first: '-9999', + last: '9999', + none: '0', + 1: '1', + 2: '2', + 3: '3', + 4: '4', + 5: '5', + 6: '6', + 7: '7', + 8: '8', + 9: '9', + 10: '10', + 11: '11', + 12: '12' + }, + outlineColor: ({ theme }) => theme('colors'), + outlineOffset: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + outlineWidth: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + padding: ({ theme }) => theme('spacing'), + placeholderColor: ({ theme }) => theme('colors'), + placeholderOpacity: ({ theme }) => theme('opacity'), + ringColor: ({ theme }) => ({ + DEFAULT: theme('colors.blue.500', '#3b82f6'), + ...theme('colors') + }), + ringOffsetColor: ({ theme }) => theme('colors'), + ringOffsetWidth: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + ringOpacity: ({ theme }) => ({ + DEFAULT: '0.5', + ...theme('opacity') + }), + ringWidth: { + DEFAULT: '3px', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + rotate: { + 0: '0deg', + 1: '1deg', + 2: '2deg', + 3: '3deg', + 6: '6deg', + 12: '12deg', + 45: '45deg', + 90: '90deg', + 180: '180deg' + }, + saturate: { + 0: '0', + 50: '.5', + 100: '1', + 150: '1.5', + 200: '2' + }, + scale: { + 0: '0', + 50: '.5', + 75: '.75', + 90: '.9', + 95: '.95', + 100: '1', + 105: '1.05', + 110: '1.1', + 125: '1.25', + 150: '1.5' + }, + screens: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px' + }, + scrollMargin: ({ theme }) => ({ + ...theme('spacing') + }), + scrollPadding: ({ theme }) => theme('spacing'), + sepia: { + 0: '0', + DEFAULT: '100%' + }, + skew: { + 0: '0deg', + 1: '1deg', + 2: '2deg', + 3: '3deg', + 6: '6deg', + 12: '12deg' + }, + space: ({ theme }) => ({ + ...theme('spacing') + }), + spacing: { + px: '1px', + 0: '0px', + 0.5: '0.125rem', + 1: '0.25rem', + 1.5: '0.375rem', + 2: '0.5rem', + 2.5: '0.625rem', + 3: '0.75rem', + 3.5: '0.875rem', + 4: '1rem', + 5: '1.25rem', + 6: '1.5rem', + 7: '1.75rem', + 8: '2rem', + 9: '2.25rem', + 10: '2.5rem', + 11: '2.75rem', + 12: '3rem', + 14: '3.5rem', + 16: '4rem', + 20: '5rem', + 24: '6rem', + 28: '7rem', + 32: '8rem', + 36: '9rem', + 40: '10rem', + 44: '11rem', + 48: '12rem', + 52: '13rem', + 56: '14rem', + 60: '15rem', + 64: '16rem', + 72: '18rem', + 80: '20rem', + 96: '24rem' + }, + stroke: ({ theme }) => ({ + none: 'none', + ...theme('colors') + }), + strokeWidth: { + 0: '0', + 1: '1', + 2: '2' + }, + supports: {}, + data: {}, + textColor: ({ theme }) => theme('colors'), + textDecorationColor: ({ theme }) => theme('colors'), + textDecorationThickness: { + auto: 'auto', + 'from-font': 'from-font', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + textIndent: ({ theme }) => ({ + ...theme('spacing') + }), + textOpacity: ({ theme }) => theme('opacity'), + textUnderlineOffset: { + auto: 'auto', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px' + }, + transformOrigin: { + center: 'center', + top: 'top', + 'top-right': 'top right', + right: 'right', + 'bottom-right': 'bottom right', + bottom: 'bottom', + 'bottom-left': 'bottom left', + left: 'left', + 'top-left': 'top left' + }, + transitionDelay: { + 0: '0s', + 75: '75ms', + 100: '100ms', + 150: '150ms', + 200: '200ms', + 300: '300ms', + 500: '500ms', + 700: '700ms', + 1000: '1000ms' + }, + transitionDuration: { + DEFAULT: '150ms', + 0: '0s', + 75: '75ms', + 100: '100ms', + 150: '150ms', + 200: '200ms', + 300: '300ms', + 500: '500ms', + 700: '700ms', + 1000: '1000ms' + }, + transitionProperty: { + none: 'none', + all: 'all', + DEFAULT: + 'color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter', + colors: + 'color, background-color, border-color, text-decoration-color, fill, stroke', + opacity: 'opacity', + shadow: 'box-shadow', + transform: 'transform' + }, + transitionTimingFunction: { + DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)', + linear: 'linear', + in: 'cubic-bezier(0.4, 0, 1, 1)', + out: 'cubic-bezier(0, 0, 0.2, 1)', + 'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)' + }, + translate: ({ theme }) => ({ + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + full: '100%' + }), + size: ({ theme }) => ({ + auto: 'auto', + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + '1/5': '20%', + '2/5': '40%', + '3/5': '60%', + '4/5': '80%', + '1/6': '16.666667%', + '2/6': '33.333333%', + '3/6': '50%', + '4/6': '66.666667%', + '5/6': '83.333333%', + '1/12': '8.333333%', + '2/12': '16.666667%', + '3/12': '25%', + '4/12': '33.333333%', + '5/12': '41.666667%', + '6/12': '50%', + '7/12': '58.333333%', + '8/12': '66.666667%', + '9/12': '75%', + '10/12': '83.333333%', + '11/12': '91.666667%', + full: '100%', + min: 'min-content', + max: 'max-content', + fit: 'fit-content' + }), + width: ({ theme }) => ({ + auto: 'auto', + ...theme('spacing'), + '1/2': '50%', + '1/3': '33.333333%', + '2/3': '66.666667%', + '1/4': '25%', + '2/4': '50%', + '3/4': '75%', + '1/5': '20%', + '2/5': '40%', + '3/5': '60%', + '4/5': '80%', + '1/6': '16.666667%', + '2/6': '33.333333%', + '3/6': '50%', + '4/6': '66.666667%', + '5/6': '83.333333%', + '1/12': '8.333333%', + '2/12': '16.666667%', + '3/12': '25%', + '4/12': '33.333333%', + '5/12': '41.666667%', + '6/12': '50%', + '7/12': '58.333333%', + '8/12': '66.666667%', + '9/12': '75%', + '10/12': '83.333333%', + '11/12': '91.666667%', + full: '100%', + screen: '100vw', + svw: '100svw', + lvw: '100lvw', + dvw: '100dvw', + min: 'min-content', + max: 'max-content', + fit: 'fit-content' + }), + willChange: { + auto: 'auto', + scroll: 'scroll-position', + contents: 'contents', + transform: 'transform' + }, + zIndex: { + auto: 'auto', + 0: '0', + 10: '10', + 20: '20', + 30: '30', + 40: '40', + 50: '50' + } + }, + plugins: [] +}; diff --git a/test/colors.spec.js b/test/colors.spec.js new file mode 100644 index 00000000..72397866 --- /dev/null +++ b/test/colors.spec.js @@ -0,0 +1,14 @@ +import * as sortBy from "../lib/huetiful.esm.mjs"; + +/** + * @license + * colors.ts - Test suite for huetiful-js colors module. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ diff --git a/test/converters.spec.js b/test/converters.spec.js new file mode 100644 index 00000000..3ed1bf36 --- /dev/null +++ b/test/converters.spec.js @@ -0,0 +1,14 @@ +import * as sortBy from "../lib/huetiful.esm.mjs"; + +/** + * @license + * converters.ts - Test suite for huetiful-js converters module. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ diff --git a/test/filterBy.spec.js b/test/filterBy.spec.js new file mode 100644 index 00000000..ef0bbb06 --- /dev/null +++ b/test/filterBy.spec.js @@ -0,0 +1,14 @@ +import * as sortBy from "../lib/huetiful.esm.mjs"; + +/** + * @license + * filterBy.ts - Test suite for huetiful-js color filtering module. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ diff --git a/test/generators.spec.js b/test/generators.spec.js new file mode 100644 index 00000000..383a85c9 --- /dev/null +++ b/test/generators.spec.js @@ -0,0 +1,14 @@ +import * as sortBy from "../lib/huetiful.esm.mjs"; + +/** + * @license + * sortBy.ts - Test suite for huetiful-js palette generator module. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ diff --git a/test/sortBy.spec.js b/test/sortBy.spec.js new file mode 100644 index 00000000..24e76a48 --- /dev/null +++ b/test/sortBy.spec.js @@ -0,0 +1,14 @@ +import * as sortBy from "../lib/huetiful.esm.mjs"; + +/** + * @license + * sortBy.ts - Test suite for huetiful-js color sorting module. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ diff --git a/test/utils.spec.js b/test/utils.spec.js new file mode 100644 index 00000000..7bc950ca --- /dev/null +++ b/test/utils.spec.js @@ -0,0 +1,25 @@ +import * as utils from "../source/lib/huetiful.esm.mjs"; + +/** + * @license + * utils.ts - Test suite for huetiful-js utils module. +Copyright 2023 Dean Tarisai. +This file is licensed to you under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +it(`should get the passed in color's hue family`, function () { + const colors = [ + ["rgb", 60, 79, 165], + "ffce33", + { j: 0.1105, c: 0.18, h: 163, mode: "jch" }, + 679000, + ]; + const hues = colors.map((el) => utils.getHueFamily(el)); + expect(hues).toBe(colors); +}); diff --git a/tsconfig.json b/tsconfig.json index 5d5fb6f5..0b9f75db 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,20 @@ { "include": [ - "src/index.ts" - ], + "./src/index.ts" + ],"exclude":["**/*/node_modules"], "compilerOptions": { + "lib":["es2015"], + "target":"es2015", "allowImportingTsExtensions": true, "skipLibCheck": true, // "allowJs": true, - "checkJs": true, + // "checkJs": true, "declaration": true, // "declarationMap": true, "emitDeclarationOnly": true, "isolatedModules": true, "forceConsistentCasingInFileNames": true, "declarationDir": "./types", + // "rootDir":"./src" } } \ No newline at end of file