├── .browserslistrc
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── README
├── consistent-shading-v1.0.10.png
└── consistent-shading.png
├── build
└── rollup.config.js
├── package-lock.json
├── package.json
├── src
├── colors.ts
├── generator.ts
├── index.ts
├── types.d.ts
└── util.ts
└── tsconfig.json
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | strategy:
11 | matrix:
12 | node-version: [12.x]
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Use Node.js ${{ matrix.node-version }}
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | - run: npm install
21 | - run: npm run build
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | src/index.test.ts
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | #### 1.1.0 - Latest release
2 |
3 | - Added alpha shades. Now, instead of only being able to generate 100% opacity shades, transparent black/white shades can also be generated via `generateAlpha` from within `ConsistentShading` instances.
4 |
5 | - Added `rgba` as a result type for `generateAlpha`. It is a tuple of 4 numbers, and is not meant to work with `convert`.
6 |
7 | - Fixed some precision bugs. Shades should be more accurate now.
8 |
9 | - Fixed a bug which caused crashes. If the input type was `lch` the converter would try to convert a type to itself, which is not defined in `color-convert`.
10 |
11 | #### 1.0.0 - 1.0.10
12 |
13 | Initial release. Available utilities:
14 |
15 | - `Color` class, useful for conversion.
16 | - `ConsistentShading` class, the main generator.
17 | - Instances of this will contain a `generate` function, which will return the list of solid shades that match the given model.
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Gârleanu Alexandru-Ștefan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # consistent-shading
2 |
3 | 
4 | 
5 | 
6 | 
7 |
8 | Shade consistency for various hues, based on luminance. **Supports both solid and transparent shades.**
9 |
10 | 
11 |
12 | ## Installation
13 |
14 | For the npm package, use\:
15 |
16 | ```bash
17 | npm install --save consistent-shading
18 | ```
19 |
20 | For the ECMAScript module in browsers, use\:
21 |
22 | ```html
23 |
24 | ```
25 |
26 | For the minfied module _(only ~750 bytes!)_ in browsers, use\:
27 |
28 | ```html
29 |
30 | ```
31 |
32 | ## Problem
33 |
34 | The colour spectrum differs in luminance, at maximum saturation. As such, applying the same shadows throughout your design will create uneven colouring, due to the difference in _contrast_.
35 |
36 | Finding aesthetically pleasing combinations of shadows and highlights is difficult enough for one colour, but getting consistent shading with a varied pallete can be really time consuming.
37 |
38 | ## Solution
39 |
40 | The easiest solution lies in the new HCL (or LCH) colour format, which manages to separate luminance into an independent parameter.
41 |
42 | To use this library you have to provide an _ideal base colour_, along with an array of _ideal shades_, that you have found through experimentation and determined to be fitting for your design.
43 |
44 | After providing these in your format of choice\* pick a new base colour and feed it into the generator. This will cause it to return an array of relevant, _contrast consistent_ shades, that _look pleasing_, just like the shaded base colour.
45 |
46 | ## Usage
47 |
48 | > Try to pick base colours that are not on either extremes of the luminance spectrum, since the generator caps at the maximum and minimum levels. (You can't obtain a color darker than black)
49 |
50 | ```typescript
51 | import { Color, ConsistentShading } from "consistent-shading";
52 |
53 | const IdealBaseColor = new Color("hex", ["#3454D1"]);
54 |
55 | const IdealShades = [
56 | new Color("rgb", [122, 143, 225]),
57 | new Color("lab", [31, 28, -58]),
58 | new Color("cmyk", [27, 22, 0, 7]),
59 | ];
60 |
61 | const Generator = new ConsistentShading(IdealBaseColor, IdealShades);
62 |
63 | const AnyColor = new Color("hsl", [345, 63, 51]);
64 |
65 | Generator.generate(AnyColor);
66 | /* => Array of Color objects, representing shades of AnyColor. */
67 |
68 | Generator.generateAlpha(AnyColor, /* optional */ 1.5);
69 | /* => Array of Color objects, rgba format, representing
70 | semi-transparent black or white shades of AnyColor. */
71 | ```
72 |
73 | ## API Description
74 |
75 | ### Color formats\*
76 |
77 | The color formats are taken directly from the [color-convert](https://www.npmjs.com/package/color-convert) package. These are as follows:
78 |
79 | ```typescript
80 | "rgb" | "hsl" | "hsv" | "hwb" | "cmyk" | "xyz" | "lab" | "lch" | "hex" | "keyword" | "ansi16" | "ansi256" | "hcg" | "apple" | "gray";
81 | ```
82 |
83 | ### Color class
84 |
85 | Color is provided with two fields, one of which is the `value`, that is mostly a tuple of varying sizes (check [this](../blob/master/src/colors.ts) for more details), and the other is the `format`, which enables the library to correctly interpret your colours and convert them accordingly.
86 |
87 | ```typescript
88 | const myColor = new Color("rgb", [255, 0, 255]);
89 | ```
90 |
91 | ### Generator class
92 |
93 | The `ConsistentShading` class, which is in basic terms the shade generator and main functionality of this library (thus taking it's name), contains an important construrctor, which takes as a first paramenter the base `Color` and an array of shades, that are represented through an array `Color[]`.
94 |
95 | ***
96 |
97 | #### `generate(baseColor: Color)`
98 | After it has calculated the relative luminances of the provided shades, the `generate` method can be used to obtain **solid shades**. This method has a first mandatory parameter, which is the new base color, and an optional second parameter, which specifies a preferred format for the resulting shades.
99 |
100 | ***
101 |
102 | #### `generateAlpha(baseColor: Color, threshold?: number)`
103 | To obtain **alpha shadows**, a binary search method was used.
104 |
105 | The solid shades are calculated first, and then it is decided based on luminance if the base color of the `rgba` value should be black or white.
106 |
107 | After the decision is made, the opacity is guessed via binary search, with a configurable threshold. If the guessed opacity of the mask is, within the threshold values, equal in luminance to the generated solid shade, it is accepted.
108 |
109 | _Higher threshold will make the generation faster, but the results will be less accurate. It is defaulted to 1, which translates to 1% opacity threshold._
110 |
111 | **Setting the threshold to a value close to 0 may result in an infinite loop.**
112 |
113 | ***
114 |
115 | > These methods can be called as many times as it's needed, since they do not mutate any internal state of the generator object.
116 |
--------------------------------------------------------------------------------
/README/consistent-shading-v1.0.10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugudango/consistent-shading/076a1b89320ad5533e7a05f60116802eea1719d6/README/consistent-shading-v1.0.10.png
--------------------------------------------------------------------------------
/README/consistent-shading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ugudango/consistent-shading/076a1b89320ad5533e7a05f60116802eea1719d6/README/consistent-shading.png
--------------------------------------------------------------------------------
/build/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { nodeResolve } from '@rollup/plugin-node-resolve';
2 | import replace from '@rollup/plugin-replace';
3 | import { terser } from 'rollup-plugin-terser';
4 | import minimist from 'minimist';
5 | import typescript from '@rollup/plugin-typescript';
6 | import pkg from '../package.json';
7 |
8 | const argv = minimist(process.argv.slice(2));
9 |
10 | const external = [
11 | 'color-convert',
12 | ];
13 |
14 | const baseConfig = {
15 | input: 'src/index.ts',
16 | external,
17 | plugins: {
18 | customResolver: nodeResolve({
19 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
20 | }),
21 | replace: {
22 | 'process.env.NODE_ENV': JSON.stringify('production'),
23 | },
24 | }
25 | }
26 |
27 | const buildFormats = [];
28 | if(!argv.format || argv.format === 'es') {
29 | const esConfig = {
30 | ...baseConfig,
31 | output: {
32 | dir: 'dist/esm',
33 | format: 'esm',
34 | exports: 'named',
35 | },
36 | plugins: [
37 | replace(baseConfig.plugins.replace),
38 | typescript({
39 | declaration: true,
40 | declarationDir: 'dist/esm/types/',
41 | rootDir: 'src/',
42 | }),
43 | ],
44 | };
45 |
46 | buildFormats.push(esConfig);
47 | }
48 |
49 | if (!argv.format || argv.format === 'cjs') {
50 | const umdConfig = {
51 | ...baseConfig,
52 | output: {
53 | compact: true,
54 | file: pkg.main,
55 | format: 'cjs',
56 | name: 'ConsistentShading',
57 | exports: 'auto',
58 | },
59 | plugins: [
60 | replace(baseConfig.plugins.replace),
61 | typescript(),
62 | ],
63 | };
64 |
65 | buildFormats.push(umdConfig);
66 | }
67 |
68 | if (!argv.format || argv.format === 'iife') {
69 | const unpkgConfig = {
70 | ...baseConfig,
71 | output: {
72 | compact: true,
73 | file: pkg.unpkg,
74 | format: 'iife',
75 | name: 'ConsistentShading',
76 | exports: 'auto',
77 | },
78 | plugins: [
79 | replace(baseConfig.plugins.replace),
80 | typescript(),
81 | terser({
82 | output: {
83 | ecma: 5,
84 | },
85 | }),
86 | ],
87 | };
88 |
89 | buildFormats.push(unpkgConfig);
90 | }
91 |
92 | export default buildFormats;
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "consistent-shading",
3 | "version": "1.0.10",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/code-frame": {
8 | "version": "7.10.4",
9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
11 | "dev": true,
12 | "requires": {
13 | "@babel/highlight": "^7.10.4"
14 | }
15 | },
16 | "@babel/helper-module-imports": {
17 | "version": "7.12.5",
18 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz",
19 | "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==",
20 | "dev": true,
21 | "requires": {
22 | "@babel/types": "^7.12.5"
23 | }
24 | },
25 | "@babel/helper-validator-identifier": {
26 | "version": "7.10.4",
27 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
28 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
29 | "dev": true
30 | },
31 | "@babel/highlight": {
32 | "version": "7.10.4",
33 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
34 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
35 | "dev": true,
36 | "requires": {
37 | "@babel/helper-validator-identifier": "^7.10.4",
38 | "chalk": "^2.0.0",
39 | "js-tokens": "^4.0.0"
40 | }
41 | },
42 | "@babel/types": {
43 | "version": "7.12.12",
44 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz",
45 | "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==",
46 | "dev": true,
47 | "requires": {
48 | "@babel/helper-validator-identifier": "^7.12.11",
49 | "lodash": "^4.17.19",
50 | "to-fast-properties": "^2.0.0"
51 | },
52 | "dependencies": {
53 | "@babel/helper-validator-identifier": {
54 | "version": "7.12.11",
55 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
56 | "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
57 | "dev": true
58 | }
59 | }
60 | },
61 | "@rollup/plugin-babel": {
62 | "version": "5.2.3",
63 | "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.3.tgz",
64 | "integrity": "sha512-DOMc7nx6y5xFi86AotrFssQqCen6CxYn+zts5KSI879d4n1hggSb4TH3mjVgG17Vc3lZziWWfcXzrEmVdzPMdw==",
65 | "dev": true,
66 | "requires": {
67 | "@babel/helper-module-imports": "^7.10.4",
68 | "@rollup/pluginutils": "^3.1.0"
69 | }
70 | },
71 | "@rollup/plugin-commonjs": {
72 | "version": "17.1.0",
73 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz",
74 | "integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==",
75 | "dev": true,
76 | "requires": {
77 | "@rollup/pluginutils": "^3.1.0",
78 | "commondir": "^1.0.1",
79 | "estree-walker": "^2.0.1",
80 | "glob": "^7.1.6",
81 | "is-reference": "^1.2.1",
82 | "magic-string": "^0.25.7",
83 | "resolve": "^1.17.0"
84 | },
85 | "dependencies": {
86 | "estree-walker": {
87 | "version": "2.0.2",
88 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
89 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
90 | "dev": true
91 | }
92 | }
93 | },
94 | "@rollup/plugin-node-resolve": {
95 | "version": "11.1.1",
96 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.1.tgz",
97 | "integrity": "sha512-zlBXR4eRS+2m79TsUZWhsd0slrHUYdRx4JF+aVQm+MI0wsKdlpC2vlDVjmlGvtZY1vsefOT9w3JxvmWSBei+Lg==",
98 | "dev": true,
99 | "requires": {
100 | "@rollup/pluginutils": "^3.1.0",
101 | "@types/resolve": "1.17.1",
102 | "builtin-modules": "^3.1.0",
103 | "deepmerge": "^4.2.2",
104 | "is-module": "^1.0.0",
105 | "resolve": "^1.19.0"
106 | }
107 | },
108 | "@rollup/plugin-replace": {
109 | "version": "2.3.4",
110 | "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz",
111 | "integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==",
112 | "dev": true,
113 | "requires": {
114 | "@rollup/pluginutils": "^3.1.0",
115 | "magic-string": "^0.25.7"
116 | }
117 | },
118 | "@rollup/plugin-typescript": {
119 | "version": "8.1.1",
120 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.1.1.tgz",
121 | "integrity": "sha512-DPFy0SV8/GgHFL31yPFVo0G1T3yzwdw6R9KisBfO2zCYbDHUqDChSWr1KmtpGz/TmutpoGJjIvu80p9HzCEF0A==",
122 | "dev": true,
123 | "requires": {
124 | "@rollup/pluginutils": "^3.1.0",
125 | "resolve": "^1.17.0"
126 | }
127 | },
128 | "@rollup/pluginutils": {
129 | "version": "3.1.0",
130 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
131 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
132 | "dev": true,
133 | "requires": {
134 | "@types/estree": "0.0.39",
135 | "estree-walker": "^1.0.1",
136 | "picomatch": "^2.2.2"
137 | }
138 | },
139 | "@types/estree": {
140 | "version": "0.0.39",
141 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
142 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
143 | "dev": true
144 | },
145 | "@types/node": {
146 | "version": "14.11.2",
147 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz",
148 | "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==",
149 | "dev": true
150 | },
151 | "@types/resolve": {
152 | "version": "1.17.1",
153 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
154 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
155 | "dev": true,
156 | "requires": {
157 | "@types/node": "*"
158 | }
159 | },
160 | "ansi-styles": {
161 | "version": "3.2.1",
162 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
163 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
164 | "dev": true,
165 | "requires": {
166 | "color-convert": "^1.9.0"
167 | },
168 | "dependencies": {
169 | "color-convert": {
170 | "version": "1.9.3",
171 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
172 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
173 | "dev": true,
174 | "requires": {
175 | "color-name": "1.1.3"
176 | }
177 | },
178 | "color-name": {
179 | "version": "1.1.3",
180 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
181 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
182 | "dev": true
183 | }
184 | }
185 | },
186 | "balanced-match": {
187 | "version": "1.0.0",
188 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
189 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
190 | "dev": true
191 | },
192 | "brace-expansion": {
193 | "version": "1.1.11",
194 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
195 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
196 | "dev": true,
197 | "requires": {
198 | "balanced-match": "^1.0.0",
199 | "concat-map": "0.0.1"
200 | }
201 | },
202 | "buffer-from": {
203 | "version": "1.1.1",
204 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
205 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
206 | "dev": true
207 | },
208 | "builtin-modules": {
209 | "version": "3.2.0",
210 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
211 | "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
212 | "dev": true
213 | },
214 | "chalk": {
215 | "version": "2.4.2",
216 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
217 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
218 | "dev": true,
219 | "requires": {
220 | "ansi-styles": "^3.2.1",
221 | "escape-string-regexp": "^1.0.5",
222 | "supports-color": "^5.3.0"
223 | }
224 | },
225 | "color-convert": {
226 | "version": "2.0.1",
227 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
228 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
229 | "requires": {
230 | "color-name": "~1.1.4"
231 | }
232 | },
233 | "color-name": {
234 | "version": "1.1.4",
235 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
236 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
237 | },
238 | "commander": {
239 | "version": "2.20.3",
240 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
241 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
242 | "dev": true
243 | },
244 | "commondir": {
245 | "version": "1.0.1",
246 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
247 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
248 | "dev": true
249 | },
250 | "concat-map": {
251 | "version": "0.0.1",
252 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
253 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
254 | "dev": true
255 | },
256 | "cross-env": {
257 | "version": "7.0.3",
258 | "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
259 | "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
260 | "dev": true,
261 | "requires": {
262 | "cross-spawn": "^7.0.1"
263 | }
264 | },
265 | "cross-spawn": {
266 | "version": "7.0.3",
267 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
268 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
269 | "dev": true,
270 | "requires": {
271 | "path-key": "^3.1.0",
272 | "shebang-command": "^2.0.0",
273 | "which": "^2.0.1"
274 | }
275 | },
276 | "deepmerge": {
277 | "version": "4.2.2",
278 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
279 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
280 | "dev": true
281 | },
282 | "escape-string-regexp": {
283 | "version": "1.0.5",
284 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
285 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
286 | "dev": true
287 | },
288 | "estree-walker": {
289 | "version": "1.0.1",
290 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
291 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
292 | "dev": true
293 | },
294 | "fs.realpath": {
295 | "version": "1.0.0",
296 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
297 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
298 | "dev": true
299 | },
300 | "fsevents": {
301 | "version": "2.1.3",
302 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
303 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
304 | "dev": true,
305 | "optional": true
306 | },
307 | "function-bind": {
308 | "version": "1.1.1",
309 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
310 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
311 | "dev": true
312 | },
313 | "glob": {
314 | "version": "7.1.6",
315 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
316 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
317 | "dev": true,
318 | "requires": {
319 | "fs.realpath": "^1.0.0",
320 | "inflight": "^1.0.4",
321 | "inherits": "2",
322 | "minimatch": "^3.0.4",
323 | "once": "^1.3.0",
324 | "path-is-absolute": "^1.0.0"
325 | }
326 | },
327 | "has": {
328 | "version": "1.0.3",
329 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
330 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
331 | "dev": true,
332 | "requires": {
333 | "function-bind": "^1.1.1"
334 | }
335 | },
336 | "has-flag": {
337 | "version": "3.0.0",
338 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
339 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
340 | "dev": true
341 | },
342 | "inflight": {
343 | "version": "1.0.6",
344 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
345 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
346 | "dev": true,
347 | "requires": {
348 | "once": "^1.3.0",
349 | "wrappy": "1"
350 | }
351 | },
352 | "inherits": {
353 | "version": "2.0.4",
354 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
355 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
356 | "dev": true
357 | },
358 | "is-core-module": {
359 | "version": "2.2.0",
360 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
361 | "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
362 | "dev": true,
363 | "requires": {
364 | "has": "^1.0.3"
365 | }
366 | },
367 | "is-module": {
368 | "version": "1.0.0",
369 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
370 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
371 | "dev": true
372 | },
373 | "is-reference": {
374 | "version": "1.2.1",
375 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
376 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
377 | "dev": true,
378 | "requires": {
379 | "@types/estree": "*"
380 | }
381 | },
382 | "isexe": {
383 | "version": "2.0.0",
384 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
385 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
386 | "dev": true
387 | },
388 | "jest-worker": {
389 | "version": "26.3.0",
390 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz",
391 | "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==",
392 | "dev": true,
393 | "requires": {
394 | "@types/node": "*",
395 | "merge-stream": "^2.0.0",
396 | "supports-color": "^7.0.0"
397 | },
398 | "dependencies": {
399 | "has-flag": {
400 | "version": "4.0.0",
401 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
402 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
403 | "dev": true
404 | },
405 | "supports-color": {
406 | "version": "7.2.0",
407 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
408 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
409 | "dev": true,
410 | "requires": {
411 | "has-flag": "^4.0.0"
412 | }
413 | }
414 | }
415 | },
416 | "js-tokens": {
417 | "version": "4.0.0",
418 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
419 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
420 | "dev": true
421 | },
422 | "lodash": {
423 | "version": "4.17.20",
424 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
425 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
426 | "dev": true
427 | },
428 | "magic-string": {
429 | "version": "0.25.7",
430 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
431 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
432 | "dev": true,
433 | "requires": {
434 | "sourcemap-codec": "^1.4.4"
435 | }
436 | },
437 | "merge-stream": {
438 | "version": "2.0.0",
439 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
440 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
441 | "dev": true
442 | },
443 | "minimatch": {
444 | "version": "3.0.4",
445 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
446 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
447 | "dev": true,
448 | "requires": {
449 | "brace-expansion": "^1.1.7"
450 | }
451 | },
452 | "minimist": {
453 | "version": "1.2.5",
454 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
455 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
456 | "dev": true
457 | },
458 | "once": {
459 | "version": "1.4.0",
460 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
461 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
462 | "dev": true,
463 | "requires": {
464 | "wrappy": "1"
465 | }
466 | },
467 | "path-is-absolute": {
468 | "version": "1.0.1",
469 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
470 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
471 | "dev": true
472 | },
473 | "path-key": {
474 | "version": "3.1.1",
475 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
476 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
477 | "dev": true
478 | },
479 | "path-parse": {
480 | "version": "1.0.6",
481 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
482 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
483 | "dev": true
484 | },
485 | "picomatch": {
486 | "version": "2.2.2",
487 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
488 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
489 | "dev": true
490 | },
491 | "randombytes": {
492 | "version": "2.1.0",
493 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
494 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
495 | "dev": true,
496 | "requires": {
497 | "safe-buffer": "^5.1.0"
498 | }
499 | },
500 | "resolve": {
501 | "version": "1.19.0",
502 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
503 | "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
504 | "dev": true,
505 | "requires": {
506 | "is-core-module": "^2.1.0",
507 | "path-parse": "^1.0.6"
508 | }
509 | },
510 | "rollup": {
511 | "version": "2.38.2",
512 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.2.tgz",
513 | "integrity": "sha512-3Sg65zfgqsnI2LUFsOmhJDvTWXwio+taySq/dsyvel8+GW+AxeW9V6YZG8BpVGQk/TS4uvGLARRH5T3ygDyyNQ==",
514 | "dev": true,
515 | "requires": {
516 | "fsevents": "~2.1.2"
517 | }
518 | },
519 | "rollup-plugin-terser": {
520 | "version": "7.0.2",
521 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
522 | "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
523 | "dev": true,
524 | "requires": {
525 | "@babel/code-frame": "^7.10.4",
526 | "jest-worker": "^26.2.1",
527 | "serialize-javascript": "^4.0.0",
528 | "terser": "^5.0.0"
529 | }
530 | },
531 | "safe-buffer": {
532 | "version": "5.2.1",
533 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
534 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
535 | "dev": true
536 | },
537 | "serialize-javascript": {
538 | "version": "4.0.0",
539 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
540 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
541 | "dev": true,
542 | "requires": {
543 | "randombytes": "^2.1.0"
544 | }
545 | },
546 | "shebang-command": {
547 | "version": "2.0.0",
548 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
549 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
550 | "dev": true,
551 | "requires": {
552 | "shebang-regex": "^3.0.0"
553 | }
554 | },
555 | "shebang-regex": {
556 | "version": "3.0.0",
557 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
558 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
559 | "dev": true
560 | },
561 | "source-map": {
562 | "version": "0.7.3",
563 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
564 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
565 | "dev": true
566 | },
567 | "source-map-support": {
568 | "version": "0.5.19",
569 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
570 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
571 | "dev": true,
572 | "requires": {
573 | "buffer-from": "^1.0.0",
574 | "source-map": "^0.6.0"
575 | },
576 | "dependencies": {
577 | "source-map": {
578 | "version": "0.6.1",
579 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
580 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
581 | "dev": true
582 | }
583 | }
584 | },
585 | "sourcemap-codec": {
586 | "version": "1.4.8",
587 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
588 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
589 | "dev": true
590 | },
591 | "supports-color": {
592 | "version": "5.5.0",
593 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
594 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
595 | "dev": true,
596 | "requires": {
597 | "has-flag": "^3.0.0"
598 | }
599 | },
600 | "terser": {
601 | "version": "5.3.3",
602 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.3.tgz",
603 | "integrity": "sha512-vRQDIlD+2Pg8YMwVK9kMM3yGylG95EIwzBai1Bw7Ot4OBfn3VP1TZn3EWx4ep2jERN/AmnVaTiGuelZSN7ds/A==",
604 | "dev": true,
605 | "requires": {
606 | "commander": "^2.20.0",
607 | "source-map": "~0.7.2",
608 | "source-map-support": "~0.5.19"
609 | }
610 | },
611 | "to-fast-properties": {
612 | "version": "2.0.0",
613 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
614 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
615 | "dev": true
616 | },
617 | "tslib": {
618 | "version": "2.1.0",
619 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
620 | "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
621 | "dev": true
622 | },
623 | "typescript": {
624 | "version": "4.1.3",
625 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
626 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
627 | "dev": true
628 | },
629 | "which": {
630 | "version": "2.0.2",
631 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
632 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
633 | "dev": true,
634 | "requires": {
635 | "isexe": "^2.0.0"
636 | }
637 | },
638 | "wrappy": {
639 | "version": "1.0.2",
640 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
641 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
642 | "dev": true
643 | }
644 | }
645 | }
646 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "consistent-shading",
3 | "private": false,
4 | "description": "Shade consitency for various hues, based on luminance.",
5 | "keywords": [
6 | "color",
7 | "consistent",
8 | "shade",
9 | "shading",
10 | "shadow",
11 | "light",
12 | "highlight",
13 | "hcl",
14 | "lch",
15 | "chroma",
16 | "alpha",
17 | "correct"
18 | ],
19 | "version": "1.1.0",
20 | "license": "MIT",
21 | "main": "dist/index.ssr.js",
22 | "browser": "dist/esm/index.js",
23 | "module": "dist/esm/index.js",
24 | "unpkg": "dist/index.min.js",
25 | "files": [
26 | "dist/*"
27 | ],
28 | "types": "dist/esm/types/index.d.ts",
29 | "scripts": {
30 | "build": "cross-env NODE_ENV=production rollup -c build/rollup.config.js -m",
31 | "build:ssr": "cross-env NODE_ENV=production rollup -c build/rollup.config.js -m --format cjs",
32 | "build:es": "cross-env NODE_ENV=production rollup -c build/rollup.config.js -m --format es",
33 | "build:unpkg": "cross-env NODE_ENV=production rollup -c build/rollup.config.js -m --format iife",
34 | "watch": "rollup -c build/rollup.config.js -w"
35 | },
36 | "author": {
37 | "name": "Gârleanu Alexandru-Ștefan"
38 | },
39 | "repository": {
40 | "type": "git",
41 | "url": "https://github.com/ugudango/consistent-shading.git"
42 | },
43 | "dependencies": {
44 | "color-convert": "^2.0.1"
45 | },
46 | "devDependencies": {
47 | "@rollup/plugin-babel": "^5.2.3",
48 | "@rollup/plugin-commonjs": "^17.1.0",
49 | "@rollup/plugin-node-resolve": "^11.1.1",
50 | "@rollup/plugin-replace": "^2.3.4",
51 | "@rollup/plugin-typescript": "^8.1.1",
52 | "cross-env": "^7.0.3",
53 | "minimist": "^1.2.5",
54 | "rollup": "^2.38.2",
55 | "rollup-plugin-terser": "^7.0.2",
56 | "tslib": "^2.1.0",
57 | "typescript": "^4.1.3"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/colors.ts:
--------------------------------------------------------------------------------
1 | export type rgb = [number, number, number];
2 | export type rgba = [number, number, number, number];
3 | type hsl = [number, number, number];
4 | type hsv = [number, number, number];
5 | type hwb = [number, number, number];
6 | type cmyk = [number, number, number, number];
7 | type xyz = [number, number, number];
8 | type lab = [number, number, number];
9 | export type lch = [number, number, number];
10 | type hex = [string];
11 | type keyword = [string];
12 | type ansi16 = [string];
13 | type ansi256 = [string];
14 | type hcg = [number, number, number];
15 | type apple = [number, number, number];
16 | type gray = [number];
17 |
18 | export type ColorFormat =
19 | rgb
20 | | rgba
21 | | hsl
22 | | hsv
23 | | hwb
24 | | cmyk
25 | | xyz
26 | | lab
27 | | lch
28 | | hex
29 | | keyword
30 | | ansi16
31 | | ansi256
32 | | hcg
33 | | apple
34 | | gray;
35 |
36 | export type ColorFormatLabel =
37 | 'rgb'
38 | | 'rgba'
39 | | 'hsl'
40 | | 'hsv'
41 | | 'hwb'
42 | | 'cmyk'
43 | | 'xyz'
44 | | 'lab'
45 | | 'lch'
46 | | 'hex'
47 | | 'keyword'
48 | | 'ansi16'
49 | | 'ansi256'
50 | | 'hcg'
51 | | 'apple'
52 | | 'gray';
53 |
54 | export class Color {
55 | public value: ColorFormat;
56 |
57 | public format: ColorFormatLabel;
58 |
59 | public constructor(format: ColorFormatLabel, value: ColorFormat) {
60 | this.value = value;
61 | this.format = format;
62 | }
63 | }
--------------------------------------------------------------------------------
/src/generator.ts:
--------------------------------------------------------------------------------
1 | import Util from './util';
2 | import { Color, ColorFormatLabel, lch, rgb, rgba } from './colors';
3 | import convert from 'color-convert';
4 |
5 | export class ConsistentShading {
6 | private _base: lch;
7 |
8 | private _shades!: lch[];
9 |
10 | private _deltas!: number[];
11 |
12 | public constructor(base: Color, shades: Color[]) {
13 | this._base = convert[base.format]['lch'].raw(base.value);
14 | this._shades = new Array();
15 | this._deltas = new Array();
16 | shades.forEach((shade, index) => {
17 | const converted = shade.format === 'lch' ? [...shade.value] : convert[shade.format]['lch'].raw(shade.value);
18 | this._shades.push(converted);
19 | this._deltas.push(converted[0] - this._base[0]);
20 | });
21 | }
22 |
23 | public generate(base: Color, exportFormat: ColorFormatLabel = base.format): Color[] {
24 | let exportedColors: Color[] = [];
25 | const lchBase: lch = base.format === 'lch' ? [...base.value] : convert[base.format]['lch'].raw(base.value);
26 | this._deltas.forEach((delta: number) => {
27 | let lchGenerated: lch = [...lchBase];
28 | lchGenerated[0] += delta;
29 | lchGenerated[0] = Math.min(100, Math.max(0, lchGenerated[0]));
30 | let convertedLchGenerated = exportFormat === 'lch' ? [...lchGenerated] : convert['lch'][exportFormat](lchGenerated);
31 | const clgRounded = convertedLchGenerated.map?.((component: number) => Math.round(component));
32 | if (typeof clgRounded !== 'undefined') {
33 | convertedLchGenerated = clgRounded;
34 | }
35 | const generatedColor = new Color(exportFormat, convertedLchGenerated);
36 | exportedColors.push(generatedColor);
37 | });
38 | return exportedColors;
39 | }
40 |
41 | public generateAlpha(base: Color, threshold: number = 1): Color[] {
42 | const lchBase: lch = base.format === 'lch' ? [...base.value] : convert[base.format]['lch'].raw(base.value);
43 |
44 | const rgbBase: rgb = base.format === 'rgb' ? [...base.value] : convert[base.format]['rgb'].raw(base.value);
45 |
46 | const lchResults: lch[] = this.generate(base, 'lch').map((color) => color.value as lch);
47 |
48 | const rgbResults: rgb[] = lchResults.map((color) => convert['lch']['rgb'].raw(color) as rgb);
49 |
50 | const exportedColors: Color[] = [];
51 |
52 | rgbResults.forEach((result: rgb, index) => {
53 | const desiredLuminance = lchResults[index][0];
54 |
55 | let currentColor: rgb;
56 |
57 | let bottom = 0;
58 | let top = 1;
59 |
60 | let currentOverlay: rgba = [255, 255, 255, 0];
61 | let overlayIsBlack = 0;
62 |
63 | if (lchResults[index][0] < lchBase[0]) {
64 | currentOverlay = [0, 0, 0, 0];
65 | overlayIsBlack = 1;
66 | }
67 |
68 | do {
69 | let middle = (bottom + top) / 2;
70 | currentOverlay[3] = middle;
71 | currentColor = Util.applyAlpha(rgbBase, currentOverlay);
72 | let currentLuminance = convert.rgb.lch.raw(currentColor)[0];
73 |
74 | if (currentLuminance > desiredLuminance ? !overlayIsBlack : overlayIsBlack)
75 | top = middle - 0.01;
76 | else
77 | bottom = middle + 0.01;
78 |
79 | } while(!Util.checkLuminance(currentColor, desiredLuminance, threshold));
80 |
81 | currentOverlay[3] = Math.round(currentOverlay[3] * 100) / 100;
82 |
83 | exportedColors.push(new Color('rgba', [...currentOverlay]));
84 | });
85 |
86 | return exportedColors;
87 | }
88 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ConsistentShading } from './generator';
2 | export { Color } from './colors';
--------------------------------------------------------------------------------
/src/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'color-convert';
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | import { Color, ColorFormatLabel, lch, rgb, rgba } from './colors';
2 | import convert from 'color-convert';
3 |
4 | export default {
5 | applyAlpha(base: rgb, overlay: rgba): rgb {
6 | const result: rgb = [...base];
7 | result.forEach((channel, index) => {
8 | result[index] = overlay[index] * overlay[3] + channel * (1 - overlay[3]);
9 | });
10 | return result;
11 | },
12 | checkLuminance(color: rgb, desiredLuminance: number, threshold: number = 1): boolean {
13 | const lchColor: lch = convert.rgb.lch.raw(color);
14 | const upperBound = desiredLuminance + threshold;
15 | const lowerBound = desiredLuminance - threshold;
16 | return lowerBound < lchColor[0] && lchColor[0] < upperBound;
17 | }
18 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "strict": true,
6 | "importHelpers": true,
7 | "moduleResolution": "node",
8 | "experimentalDecorators": true,
9 | "skipLibCheck": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "sourceMap": true,
13 | "baseUrl": ".",
14 | "noImplicitAny": true,
15 | "lib": [
16 | "ESNext",
17 | ]
18 | },
19 | "include": [
20 | "src/**/*"
21 | ],
22 | "exclude": ["node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------