UNPKG

7.01 kBJavaScriptView Raw
1/*
2Copyright 2019 Adobe. All rights reserved.
3This file is licensed to you under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License. You may obtain a copy
5of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7Unless required by applicable law or agreed to in writing, software distributed under
8the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9OF ANY KIND, either express or implied. See the License for the specific language
10governing permissions and limitations under the License.
11*/
12
13const chroma = require('chroma-js');
14
15const {
16 colorSpaces,
17 convertColorValue,
18 multiplyRatios,
19 ratioName,
20 round,
21 searchColors,
22} = require('./utils');
23
24const { BackgroundColor } = require('./backgroundcolor');
25
26class Theme {
27 constructor({ colors, backgroundColor, lightness, contrast = 1, output = 'HEX' }) {
28 this._output = output;
29 this._colors = colors;
30 this._lightness = lightness;
31
32 this._setBackgroundColor(backgroundColor);
33 this._setBackgroundColorValue();
34
35 this._contrast = contrast;
36 if (!this._colors) {
37 throw new Error('No colors are defined');
38 }
39 if (!this._backgroundColor) {
40 throw new Error('Background color is undefined');
41 }
42 colors.forEach((color) => {
43 if (!color.ratios) throw new Error(`Color ${color.name}'s ratios are undefined`);
44 });
45 if (!colorSpaces[this._output]) {
46 throw new Error(`Output “${output}” not supported`);
47 }
48
49 this._modifiedColors = this._colors;
50 // console.log(`${this._colors} \n ----------------- \n ${this._modifiedColors}`)
51 // this._setContrasts(this._contrast);
52
53 this._findContrastColors();
54 this._findContrastColorPairs();
55 this._findContrastColorValues();
56 }
57
58 set contrast(contrast) {
59 this._contrast = contrast;
60 // this._setContrasts(contrast);
61 this._findContrastColors();
62 }
63
64 get contrast() {
65 return this._contrast;
66 }
67
68 set lightness(lightness) {
69 this._lightness = lightness;
70 this._setBackgroundColor(this._backgroundColor);
71 this._findContrastColors();
72 }
73
74 get lightness() {
75 return this._lightness;
76 }
77
78 set backgroundColor(backgroundColor) {
79 this._setBackgroundColor(backgroundColor);
80 this._findContrastColors();
81 }
82
83 get backgroundColorValue() {
84 return this._backgroundColorValue;
85 }
86
87 get backgroundColor() {
88 return this._backgroundColor;
89 }
90
91 // Add a getter and setter for colors
92 set colors(colors) {
93 this._colors = colors;
94 this._findContrastColors();
95 }
96
97 get colors() {
98 return this._colors;
99 }
100
101 set output(output) {
102 this._output = output;
103 this._colors.forEach((element) => {
104 element.output = this._output;
105 });
106 this._backgroundColor.output = this._output;
107
108 this._findContrastColors();
109 }
110
111 get output() {
112 return this._output;
113 }
114
115 get contrastColors() {
116 return this._contrastColors;
117 }
118
119 get contrastColorPairs() {
120 return this._contrastColorPairs;
121 }
122
123 get contrastColorValues() {
124 return this._contrastColorValues;
125 }
126
127 _setBackgroundColor(backgroundColor) {
128 if (typeof backgroundColor === 'string') {
129 // If it's a string, convert to Color object and assign lightness.
130 const newBackgroundColor = new BackgroundColor({ name: 'background', colorKeys: [backgroundColor], output: 'RGB' });
131 const calcLightness = round(chroma(String(backgroundColor)).hsluv()[2]);
132
133 this._backgroundColor = newBackgroundColor;
134 this._lightness = calcLightness;
135 this._backgroundColorValue = newBackgroundColor[this._lightness];
136 // console.log(`String background color of ${backgroundColor} converted to ${newBackgroundColor}`)
137 } else {
138 // console.log(`NOT a string for background, instead it is ${JSON.stringify(backgroundColor)}`)
139 backgroundColor.output = 'RGB';
140 const calcBackgroundColorValue = backgroundColor.backgroundColorScale[this._lightness];
141
142 // console.log(`Object background \nLightness: ${this._lightness} \nBackground scale: ${backgroundColor.backgroundColorScale}\nCalculated background value of ${calcBackgroundColorValue}`)
143 this._backgroundColor = backgroundColor;
144 this._backgroundColorValue = calcBackgroundColorValue;
145 }
146 }
147
148 _setBackgroundColorValue() {
149 this._backgroundColorValue = this._backgroundColor.backgroundColorScale[this._lightness];
150 }
151
152 _findContrastColors() {
153 const bgRgbArray = chroma(String(this._backgroundColorValue)).rgb();
154 const baseV = this._lightness / 100;
155 const convertedBackgroundColorValue = convertColorValue(this._backgroundColorValue, this._output);
156 const baseObj = { background: convertedBackgroundColorValue };
157
158 const returnColors = []; // Array to be populated with JSON objects for each color, including names & contrast values
159 const returnColorValues = []; // Array to be populated with flat list of all color values
160 const returnColorPairs = {...baseObj}; // Objext to be populated with flat list of all color values as named key-value pairs
161 returnColors.push(baseObj);
162
163 this._modifiedColors.map((color) => {
164 if (color.ratios !== undefined) {
165 let swatchNames;
166 const newArr = [];
167 const colorObj = {
168 name: color.name,
169 values: newArr,
170 };
171
172 let ratioValues;
173
174 if (Array.isArray(color.ratios)) {
175 ratioValues = color.ratios;
176 } else if (!Array.isArray(color.ratios)) {
177 swatchNames = Object.keys(color.ratios);
178 ratioValues = Object.values(color.ratios);
179 }
180
181 // modify target ratio based on contrast multiplier
182 ratioValues = ratioValues.map((ratio) => multiplyRatios(+ratio, this._contrast));
183
184 const contrastColors = searchColors(color, bgRgbArray, baseV, ratioValues).map((clr) => convertColorValue(clr, this._output));
185
186 for (let i = 0; i < contrastColors.length; i++) {
187 let n;
188 if (!swatchNames) {
189 const rVal = ratioName(color.ratios)[i];
190 n = color.name.concat(rVal);
191 } else {
192 n = swatchNames[i];
193 }
194
195 const obj = {
196 name: n,
197 contrast: ratioValues[i],
198 value: contrastColors[i],
199 };
200 newArr.push(obj);
201 // Push the same values to the returnColorPairs object
202 returnColorPairs[n] = contrastColors[i];
203 // Push the same value to the returnColorValues array
204 returnColorValues.push(contrastColors[i]);
205 }
206 returnColors.push(colorObj);
207 }
208 return null;
209 });
210 this._contrastColorValues = returnColorValues;
211 this._contrastColorPairs = returnColorPairs;
212 this._contrastColors = returnColors;
213 return this._contrastColors;
214 }
215
216 _findContrastColorPairs() {
217 return this._contrastColorPairs;
218 }
219
220 _findContrastColorValues() {
221 return this._contrastColorValues;
222 }
223}
224
225module.exports = { Theme };