1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | const chroma = require('chroma-js');
|
14 |
|
15 | const {
|
16 | colorSpaces,
|
17 | convertColorValue,
|
18 | multiplyRatios,
|
19 | ratioName,
|
20 | round,
|
21 | searchColors,
|
22 | } = require('./utils');
|
23 |
|
24 | const { BackgroundColor } = require('./backgroundcolor');
|
25 |
|
26 | class Theme {
|
27 | constructor({ colors, backgroundColor, lightness, contrast = 1, saturation = 100, output = 'HEX' }) {
|
28 | this._output = output;
|
29 | this._colors = colors;
|
30 | this._lightness = lightness;
|
31 | this._saturatoin = saturation;
|
32 |
|
33 | this._setBackgroundColor(backgroundColor);
|
34 | this._setBackgroundColorValue();
|
35 |
|
36 | this._contrast = contrast;
|
37 | if (!this._colors) {
|
38 | throw new Error('No colors are defined');
|
39 | }
|
40 | if (!this._backgroundColor) {
|
41 | throw new Error('Background color is undefined');
|
42 | }
|
43 | colors.forEach((color) => {
|
44 | if (!color.ratios) throw new Error(`Color ${color.name}'s ratios are undefined`);
|
45 | });
|
46 | if (!colorSpaces[this._output]) {
|
47 | throw new Error(`Output “${output}” not supported`);
|
48 | }
|
49 |
|
50 | this._findContrastColors();
|
51 | this._findContrastColorPairs();
|
52 | this._findContrastColorValues();
|
53 | }
|
54 |
|
55 | set contrast(contrast) {
|
56 | this._contrast = contrast;
|
57 | this._findContrastColors();
|
58 | }
|
59 |
|
60 | get contrast() {
|
61 | return this._contrast;
|
62 | }
|
63 |
|
64 | set lightness(lightness) {
|
65 | this._lightness = lightness;
|
66 | this._setBackgroundColor(this._backgroundColor);
|
67 | this._findContrastColors();
|
68 | }
|
69 |
|
70 | get lightness() {
|
71 | return this._lightness;
|
72 | }
|
73 |
|
74 | set saturation(saturation) {
|
75 | this._saturation = saturation;
|
76 |
|
77 | this._updateColorSaturation(saturation);
|
78 | this._findContrastColors();
|
79 | }
|
80 |
|
81 | get saturation() {
|
82 | return this._saturation;
|
83 | }
|
84 |
|
85 | set backgroundColor(backgroundColor) {
|
86 | this._setBackgroundColor(backgroundColor);
|
87 | this._findContrastColors();
|
88 | }
|
89 |
|
90 | get backgroundColorValue() {
|
91 | return this._backgroundColorValue;
|
92 | }
|
93 |
|
94 | get backgroundColor() {
|
95 | return this._backgroundColor;
|
96 | }
|
97 |
|
98 |
|
99 | set colors(colors) {
|
100 | this._colors = colors;
|
101 | this._findContrastColors();
|
102 | }
|
103 |
|
104 | get colors() {
|
105 | return this._colors;
|
106 | }
|
107 |
|
108 |
|
109 | set addColor(color) {
|
110 | this._colors.push(color);
|
111 | this._findContrastColors();
|
112 | }
|
113 |
|
114 | set removeColor(color) {
|
115 | const filteredColors = this._colors.filter(entry => {return entry.name !== color.name});
|
116 | this._colors = filteredColors;
|
117 | this._findContrastColors();
|
118 | }
|
119 |
|
120 | set updateColor(param) {
|
121 |
|
122 |
|
123 | let currentColor = this._colors.filter(entry => {return entry.name === param.color});
|
124 | currentColor = currentColor[0];
|
125 | const filteredColors = this._colors.filter(entry => {return entry.name !== param.color});
|
126 | if(param.name) currentColor.name = param.name;
|
127 | if(param.colorKeys) currentColor.colorKeys = param.colorKeys;
|
128 | if(param.ratios) currentColor.ratios = param.ratios;
|
129 | if(param.colorspace) currentColor.colorspace = param.colorspace;
|
130 | if(param.smooth) currentColor.smooth = param.smooth;
|
131 |
|
132 | filteredColors.push(currentColor);
|
133 | this._colors = filteredColors;
|
134 |
|
135 | this._findContrastColors();
|
136 | }
|
137 |
|
138 | set output(output) {
|
139 | this._output = output;
|
140 | this._colors.forEach((element) => {
|
141 | element.output = this._output;
|
142 | });
|
143 | this._backgroundColor.output = this._output;
|
144 |
|
145 | this._findContrastColors();
|
146 | }
|
147 |
|
148 | get output() {
|
149 | return this._output;
|
150 | }
|
151 |
|
152 | get contrastColors() {
|
153 | return this._contrastColors;
|
154 | }
|
155 |
|
156 | get contrastColorPairs() {
|
157 | return this._contrastColorPairs;
|
158 | }
|
159 |
|
160 | get contrastColorValues() {
|
161 | return this._contrastColorValues;
|
162 | }
|
163 |
|
164 | _setBackgroundColor(backgroundColor) {
|
165 | if (typeof backgroundColor === 'string') {
|
166 |
|
167 | const newBackgroundColor = new BackgroundColor({ name: 'background', colorKeys: [backgroundColor], output: 'RGB' });
|
168 | const calcLightness = round(chroma(String(backgroundColor)).hsluv()[2]);
|
169 |
|
170 | this._backgroundColor = newBackgroundColor;
|
171 | this._lightness = calcLightness;
|
172 | this._backgroundColorValue = newBackgroundColor[this._lightness];
|
173 |
|
174 | } else {
|
175 |
|
176 | backgroundColor.output = 'RGB';
|
177 | const calcBackgroundColorValue = backgroundColor.backgroundColorScale[this._lightness];
|
178 |
|
179 |
|
180 | this._backgroundColor = backgroundColor;
|
181 | this._backgroundColorValue = calcBackgroundColorValue;
|
182 | }
|
183 | }
|
184 |
|
185 | _setBackgroundColorValue() {
|
186 | this._backgroundColorValue = this._backgroundColor.backgroundColorScale[this._lightness];
|
187 | }
|
188 |
|
189 | _updateColorSaturation(saturation) {
|
190 | this._colors.map((color) => {
|
191 | const colorKeys = color.colorKeys;
|
192 | let newColorKeys = [];
|
193 | colorKeys.forEach(key => {
|
194 | let currentHsluv = chroma(`${key}`).hsluv();
|
195 | let currentSaturation = currentHsluv[1];
|
196 | let newSaturation = currentSaturation * (saturation / 100);
|
197 | let newHsluv = chroma.hsluv(currentHsluv[0], newSaturation, currentHsluv[2]);
|
198 | let newColor = chroma.rgb(newHsluv).hex();
|
199 | newColorKeys.push(newColor);
|
200 | });
|
201 |
|
202 | color.colorKeys = newColorKeys;
|
203 | })
|
204 | }
|
205 |
|
206 | _findContrastColors() {
|
207 | const bgRgbArray = chroma(String(this._backgroundColorValue)).rgb();
|
208 | const baseV = this._lightness / 100;
|
209 | const convertedBackgroundColorValue = convertColorValue(this._backgroundColorValue, this._output);
|
210 | const baseObj = { background: convertedBackgroundColorValue };
|
211 |
|
212 | const returnColors = [];
|
213 | const returnColorValues = [];
|
214 | const returnColorPairs = {...baseObj};
|
215 | returnColors.push(baseObj);
|
216 |
|
217 | this._colors.map((color) => {
|
218 | if (color.ratios !== undefined) {
|
219 | let swatchNames;
|
220 | const newArr = [];
|
221 | const colorObj = {
|
222 | name: color.name,
|
223 | values: newArr,
|
224 | };
|
225 |
|
226 | let ratioValues;
|
227 |
|
228 | if (Array.isArray(color.ratios)) {
|
229 | ratioValues = color.ratios;
|
230 | } else if (!Array.isArray(color.ratios)) {
|
231 | swatchNames = Object.keys(color.ratios);
|
232 | ratioValues = Object.values(color.ratios);
|
233 | }
|
234 |
|
235 |
|
236 | ratioValues = ratioValues.map((ratio) => multiplyRatios(+ratio, this._contrast));
|
237 |
|
238 | const contrastColors = searchColors(color, bgRgbArray, baseV, ratioValues).map((clr) => convertColorValue(clr, this._output));
|
239 |
|
240 | for (let i = 0; i < contrastColors.length; i++) {
|
241 | let n;
|
242 | if (!swatchNames) {
|
243 | const rVal = ratioName(color.ratios)[i];
|
244 | n = color.name.concat(rVal);
|
245 | } else {
|
246 | n = swatchNames[i];
|
247 | }
|
248 |
|
249 | const obj = {
|
250 | name: n,
|
251 | contrast: ratioValues[i],
|
252 | value: contrastColors[i],
|
253 | };
|
254 | newArr.push(obj);
|
255 |
|
256 | returnColorPairs[n] = contrastColors[i];
|
257 |
|
258 | returnColorValues.push(contrastColors[i]);
|
259 | }
|
260 | returnColors.push(colorObj);
|
261 | }
|
262 | return null;
|
263 | });
|
264 | this._contrastColorValues = returnColorValues;
|
265 | this._contrastColorPairs = returnColorPairs;
|
266 | this._contrastColors = returnColors;
|
267 | return this._contrastColors;
|
268 | }
|
269 |
|
270 | _findContrastColorPairs() {
|
271 | return this._contrastColorPairs;
|
272 | }
|
273 |
|
274 | _findContrastColorValues() {
|
275 | return this._contrastColorValues;
|
276 | }
|
277 | }
|
278 |
|
279 | module.exports = { Theme };
|