|
1 | | -'use strict'; |
2 | | - |
| 1 | +const _color = require('color').default; |
3 | 2 | const isNumeric = require('fast-isnumeric'); |
4 | | -const isTypedArray = require('../../lib/array').isTypedArray; |
5 | | -const color = require('color').default; |
6 | | - |
| 3 | +const { isTypedArray } = require('../../lib/array'); |
7 | 4 | const { background, defaultLine, defaults, lightLine } = require('./attributes'); |
8 | 5 |
|
| 6 | +// Safe wrapper: falls back to black instead of throwing on invalid input. |
| 7 | +// This matches the tinycolor2 behavior. |
| 8 | +const color = (cstr) => { |
| 9 | + try { |
| 10 | + return _color(cstr); |
| 11 | + } catch (e) { |
| 12 | + return _color('#000'); |
| 13 | + } |
| 14 | +}; |
| 15 | + |
9 | 16 | const rgb = (cstr) => { |
10 | 17 | const { r, g, b } = color(cstr).rgb().object(); |
11 | 18 | return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`; |
@@ -79,15 +86,12 @@ const contrast = (cstr, lightAmount, darkAmount) => { |
79 | 86 | if (c.alpha() !== 1) c = color(combine(cstr, background)); |
80 | 87 |
|
81 | 88 | // TODO: Should the API change such that lightAmount/darkAmount are passed in as decimal instead of percent number? |
82 | | - const newColor = color( |
83 | | - c.isDark() |
84 | | - ? lightAmount |
85 | | - ? c.lighten(lightAmount / 100) |
86 | | - : background |
87 | | - : darkAmount |
88 | | - ? c.darken(darkAmount / 100) |
89 | | - : defaultLine |
90 | | - ); |
| 89 | + let newColor; |
| 90 | + if (c.isDark()) { |
| 91 | + newColor = color(lightAmount ? c.lighten(lightAmount / 100) : background); |
| 92 | + } else { |
| 93 | + newColor = color(darkAmount ? c.darken(darkAmount / 100) : defaultLine); |
| 94 | + } |
91 | 95 |
|
92 | 96 | return newColor.rgb().string(); |
93 | 97 | }; |
@@ -175,39 +179,52 @@ const equals = (cstr1, cstr2) => cstr1 && cstr2 && color(cstr1).rgb().string() = |
175 | 179 |
|
176 | 180 | const isValid = (cstr) => { |
177 | 181 | try { |
178 | | - return cstr && !!color(cstr); |
| 182 | + return cstr && !!_color(cstr); |
179 | 183 | } catch { |
180 | 184 | return false; |
181 | 185 | } |
182 | 186 | }; |
183 | 187 |
|
| 188 | +// RGB-space brightening equivalent to tinycolor's brighten(). |
| 189 | +// Adds a fixed amount to each RGB channel (unlike lighten which works in HSL space). |
| 190 | +const brighten = (cstr, amount) => { |
| 191 | + amount = amount === 0 ? 0 : amount || 10; |
| 192 | + const c = color(cstr).rgb().object(); |
| 193 | + const adj = Math.round(255 * (amount / 100)); |
| 194 | + return color({ |
| 195 | + r: Math.max(0, Math.min(255, c.r + adj)), |
| 196 | + g: Math.max(0, Math.min(255, c.g + adj)), |
| 197 | + b: Math.max(0, Math.min(255, c.b + adj)) |
| 198 | + }) |
| 199 | + .rgb() |
| 200 | + .string(); |
| 201 | +}; |
| 202 | + |
184 | 203 | const mix = (cstr1, cstr2, weight) => |
185 | 204 | color(cstr1) |
186 | 205 | .mix(color(cstr2), weight / 100) |
187 | 206 | .rgb() |
188 | 207 | .string(); |
189 | 208 |
|
190 | | -const mostReadable = (baseColor, colorList = []) => { |
| 209 | +const mostReadable = (baseColor, colorList = ['#000', '#fff']) => { |
191 | 210 | let bestColor; |
192 | 211 | let bestContrast = -Infinity; |
193 | 212 |
|
194 | 213 | for (const cstr of colorList) { |
195 | | - const contrast = color(baseColor).contrast(color(cstr)); |
196 | | - if (contrast > bestContrast) { |
197 | | - bestContrast = contrast; |
| 214 | + const contrastRatio = color(baseColor).contrast(color(cstr)); |
| 215 | + if (contrastRatio > bestContrast) { |
| 216 | + bestContrast = contrastRatio; |
198 | 217 | bestColor = color(cstr).rgb().string(); |
199 | 218 | } |
200 | 219 | } |
201 | 220 |
|
202 | | - // Fall back to black/white if provided colors don't have proper contrast level |
203 | | - return bestColor && color(baseColor).level(color(bestColor)) |
204 | | - ? bestColor |
205 | | - : mostReadable(baseColor, ['#000', '#fff']); |
| 221 | + return bestColor; |
206 | 222 | }; |
207 | 223 |
|
208 | 224 | module.exports = { |
209 | 225 | addOpacity, |
210 | 226 | background, |
| 227 | + brighten, |
211 | 228 | clean, |
212 | 229 | color, |
213 | 230 | combine, |
|
0 commit comments