@fluixi/utils 1.0.0-alpha.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/lib/array/advanced.cjs +1 -0
- package/dist/lib/array/advanced.d.ts +1116 -0
- package/dist/lib/array/advanced.d.ts.map +1 -0
- package/dist/lib/array/advanced.js +2429 -0
- package/dist/lib/array/advanced.mjs +1 -0
- package/dist/lib/array/array.cjs +1 -0
- package/dist/lib/array/array.d.ts +6 -0
- package/dist/lib/array/array.d.ts.map +1 -0
- package/dist/lib/array/array.js +31 -0
- package/dist/lib/array/array.mjs +1 -0
- package/dist/lib/array/index.cjs +1 -0
- package/dist/lib/array/index.d.ts +4 -0
- package/dist/lib/array/index.d.ts.map +1 -0
- package/dist/lib/array/index.js +3 -0
- package/dist/lib/array/index.mjs +1 -0
- package/dist/lib/array/utils.cjs +1 -0
- package/dist/lib/array/utils.d.ts +100 -0
- package/dist/lib/array/utils.d.ts.map +1 -0
- package/dist/lib/array/utils.js +165 -0
- package/dist/lib/array/utils.mjs +1 -0
- package/dist/lib/color/color.cjs +1 -0
- package/dist/lib/color/color.d.ts +366 -0
- package/dist/lib/color/color.d.ts.map +1 -0
- package/dist/lib/color/color.js +722 -0
- package/dist/lib/color/color.mjs +1 -0
- package/dist/lib/color/constants.cjs +1 -0
- package/dist/lib/color/constants.d.ts +361 -0
- package/dist/lib/color/constants.d.ts.map +1 -0
- package/dist/lib/color/constants.js +358 -0
- package/dist/lib/color/constants.mjs +1 -0
- package/dist/lib/color/conversions.cjs +1 -0
- package/dist/lib/color/conversions.d.ts +166 -0
- package/dist/lib/color/conversions.d.ts.map +1 -0
- package/dist/lib/color/conversions.js +766 -0
- package/dist/lib/color/conversions.mjs +1 -0
- package/dist/lib/color/helpers.cjs +1 -0
- package/dist/lib/color/helpers.d.ts +75 -0
- package/dist/lib/color/helpers.d.ts.map +1 -0
- package/dist/lib/color/helpers.js +419 -0
- package/dist/lib/color/helpers.mjs +1 -0
- package/dist/lib/color/index.cjs +1 -0
- package/dist/lib/color/index.d.ts +12 -0
- package/dist/lib/color/index.d.ts.map +1 -0
- package/dist/lib/color/index.js +15 -0
- package/dist/lib/color/index.mjs +1 -0
- package/dist/lib/color/types.cjs +1 -0
- package/dist/lib/color/types.d.ts +179 -0
- package/dist/lib/color/types.d.ts.map +1 -0
- package/dist/lib/color/types.js +1 -0
- package/dist/lib/color/types.mjs +0 -0
- package/dist/lib/color/utils.cjs +1 -0
- package/dist/lib/color/utils.d.ts +199 -0
- package/dist/lib/color/utils.d.ts.map +1 -0
- package/dist/lib/color/utils.js +858 -0
- package/dist/lib/color/utils.mjs +1 -0
- package/dist/lib/compare/comparators.cjs +1 -0
- package/dist/lib/compare/comparators.d.ts +65 -0
- package/dist/lib/compare/comparators.d.ts.map +1 -0
- package/dist/lib/compare/comparators.js +210 -0
- package/dist/lib/compare/comparators.mjs +1 -0
- package/dist/lib/compare/index.cjs +1 -0
- package/dist/lib/compare/index.d.ts +2 -0
- package/dist/lib/compare/index.d.ts.map +1 -0
- package/dist/lib/compare/index.js +1 -0
- package/dist/lib/compare/index.mjs +1 -0
- package/dist/lib/crypto/hash.cjs +1 -0
- package/dist/lib/crypto/hash.d.ts +6 -0
- package/dist/lib/crypto/hash.d.ts.map +1 -0
- package/dist/lib/crypto/hash.js +53 -0
- package/dist/lib/crypto/hash.mjs +1 -0
- package/dist/lib/crypto/index.cjs +1 -0
- package/dist/lib/crypto/index.d.ts +2 -0
- package/dist/lib/crypto/index.d.ts.map +1 -0
- package/dist/lib/crypto/index.js +1 -0
- package/dist/lib/crypto/index.mjs +1 -0
- package/dist/lib/dom/events.cjs +1 -0
- package/dist/lib/dom/events.d.ts +159 -0
- package/dist/lib/dom/events.d.ts.map +1 -0
- package/dist/lib/dom/events.js +50 -0
- package/dist/lib/dom/events.mjs +1 -0
- package/dist/lib/dom/index.cjs +1 -0
- package/dist/lib/dom/index.d.ts +3 -0
- package/dist/lib/dom/index.d.ts.map +1 -0
- package/dist/lib/dom/index.js +2 -0
- package/dist/lib/dom/index.mjs +1 -0
- package/dist/lib/dom/utils.cjs +1 -0
- package/dist/lib/dom/utils.d.ts +6 -0
- package/dist/lib/dom/utils.d.ts.map +1 -0
- package/dist/lib/dom/utils.js +37 -0
- package/dist/lib/dom/utils.mjs +1 -0
- package/dist/lib/functions/functions.cjs +1 -0
- package/dist/lib/functions/functions.d.ts +329 -0
- package/dist/lib/functions/functions.d.ts.map +1 -0
- package/dist/lib/functions/functions.js +585 -0
- package/dist/lib/functions/functions.mjs +1 -0
- package/dist/lib/functions/index.cjs +1 -0
- package/dist/lib/functions/index.d.ts +3 -0
- package/dist/lib/functions/index.d.ts.map +1 -0
- package/dist/lib/functions/index.js +1 -0
- package/dist/lib/functions/index.mjs +1 -0
- package/dist/lib/functions/types.cjs +1 -0
- package/dist/lib/functions/types.d.ts +2 -0
- package/dist/lib/functions/types.d.ts.map +1 -0
- package/dist/lib/functions/types.js +1 -0
- package/dist/lib/functions/types.mjs +0 -0
- package/dist/lib/helpers.cjs +1 -0
- package/dist/lib/helpers.d.ts +13 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +66 -0
- package/dist/lib/helpers.mjs +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/maths/constants.cjs +1 -0
- package/dist/lib/maths/constants.d.ts +84 -0
- package/dist/lib/maths/constants.d.ts.map +1 -0
- package/dist/lib/maths/constants.js +79 -0
- package/dist/lib/maths/constants.mjs +1 -0
- package/dist/lib/maths/fn/advanced.cjs +1 -0
- package/dist/lib/maths/fn/advanced.d.ts +149 -0
- package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
- package/dist/lib/maths/fn/advanced.js +264 -0
- package/dist/lib/maths/fn/advanced.mjs +1 -0
- package/dist/lib/maths/fn/basic.cjs +1 -0
- package/dist/lib/maths/fn/basic.d.ts +182 -0
- package/dist/lib/maths/fn/basic.d.ts.map +1 -0
- package/dist/lib/maths/fn/basic.js +343 -0
- package/dist/lib/maths/fn/basic.mjs +1 -0
- package/dist/lib/maths/fn/complex.cjs +1 -0
- package/dist/lib/maths/fn/complex.d.ts +153 -0
- package/dist/lib/maths/fn/complex.d.ts.map +1 -0
- package/dist/lib/maths/fn/complex.js +383 -0
- package/dist/lib/maths/fn/complex.mjs +1 -0
- package/dist/lib/maths/fn/index.cjs +1 -0
- package/dist/lib/maths/fn/index.d.ts +10 -0
- package/dist/lib/maths/fn/index.d.ts.map +1 -0
- package/dist/lib/maths/fn/index.js +9 -0
- package/dist/lib/maths/fn/index.mjs +1 -0
- package/dist/lib/maths/fn/trigo.cjs +1 -0
- package/dist/lib/maths/fn/trigo.d.ts +147 -0
- package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
- package/dist/lib/maths/fn/trigo.js +227 -0
- package/dist/lib/maths/fn/trigo.mjs +1 -0
- package/dist/lib/maths/fn/vector.cjs +1 -0
- package/dist/lib/maths/fn/vector.d.ts +512 -0
- package/dist/lib/maths/fn/vector.d.ts.map +1 -0
- package/dist/lib/maths/fn/vector.js +1136 -0
- package/dist/lib/maths/fn/vector.mjs +1 -0
- package/dist/lib/maths/formatter.cjs +1 -0
- package/dist/lib/maths/formatter.d.ts +35 -0
- package/dist/lib/maths/formatter.d.ts.map +1 -0
- package/dist/lib/maths/formatter.js +56 -0
- package/dist/lib/maths/formatter.mjs +1 -0
- package/dist/lib/maths/guards.cjs +1 -0
- package/dist/lib/maths/guards.d.ts +166 -0
- package/dist/lib/maths/guards.d.ts.map +1 -0
- package/dist/lib/maths/guards.js +315 -0
- package/dist/lib/maths/guards.mjs +1 -0
- package/dist/lib/maths/index.cjs +1 -0
- package/dist/lib/maths/index.d.ts +24 -0
- package/dist/lib/maths/index.d.ts.map +1 -0
- package/dist/lib/maths/index.js +29 -0
- package/dist/lib/maths/index.mjs +1 -0
- package/dist/lib/maths/parser.cjs +1 -0
- package/dist/lib/maths/parser.d.ts +20 -0
- package/dist/lib/maths/parser.d.ts.map +1 -0
- package/dist/lib/maths/parser.js +78 -0
- package/dist/lib/maths/parser.mjs +1 -0
- package/dist/lib/maths/random.cjs +1 -0
- package/dist/lib/maths/random.d.ts +27 -0
- package/dist/lib/maths/random.d.ts.map +1 -0
- package/dist/lib/maths/random.js +49 -0
- package/dist/lib/maths/random.mjs +1 -0
- package/dist/lib/maths/types/advanced.cjs +1 -0
- package/dist/lib/maths/types/advanced.d.ts +357 -0
- package/dist/lib/maths/types/advanced.d.ts.map +1 -0
- package/dist/lib/maths/types/advanced.js +5 -0
- package/dist/lib/maths/types/advanced.mjs +0 -0
- package/dist/lib/maths/types/basic.cjs +1 -0
- package/dist/lib/maths/types/basic.d.ts +95 -0
- package/dist/lib/maths/types/basic.d.ts.map +1 -0
- package/dist/lib/maths/types/basic.js +5 -0
- package/dist/lib/maths/types/basic.mjs +0 -0
- package/dist/lib/maths/types/index.cjs +1 -0
- package/dist/lib/maths/types/index.d.ts +4 -0
- package/dist/lib/maths/types/index.d.ts.map +1 -0
- package/dist/lib/maths/types/index.js +3 -0
- package/dist/lib/maths/types/index.mjs +0 -0
- package/dist/lib/maths/types/negative.cjs +1 -0
- package/dist/lib/maths/types/negative.d.ts +51 -0
- package/dist/lib/maths/types/negative.d.ts.map +1 -0
- package/dist/lib/maths/types/negative.js +5 -0
- package/dist/lib/maths/types/negative.mjs +0 -0
- package/dist/lib/maths/types/operations.cjs +1 -0
- package/dist/lib/maths/types/operations.d.ts +188 -0
- package/dist/lib/maths/types/operations.d.ts.map +1 -0
- package/dist/lib/maths/types/operations.js +6 -0
- package/dist/lib/maths/types/operations.mjs +0 -0
- package/dist/lib/object/clone.cjs +1 -0
- package/dist/lib/object/clone.d.ts +46 -0
- package/dist/lib/object/clone.d.ts.map +1 -0
- package/dist/lib/object/clone.js +104 -0
- package/dist/lib/object/clone.mjs +1 -0
- package/dist/lib/object/diff.cjs +1 -0
- package/dist/lib/object/diff.d.ts +203 -0
- package/dist/lib/object/diff.d.ts.map +1 -0
- package/dist/lib/object/diff.js +805 -0
- package/dist/lib/object/diff.mjs +1 -0
- package/dist/lib/object/getter-setter.cjs +1 -0
- package/dist/lib/object/getter-setter.d.ts +361 -0
- package/dist/lib/object/getter-setter.d.ts.map +1 -0
- package/dist/lib/object/getter-setter.js +1771 -0
- package/dist/lib/object/getter-setter.mjs +1 -0
- package/dist/lib/object/index.cjs +6 -0
- package/dist/lib/object/index.d.ts +13 -0
- package/dist/lib/object/index.d.ts.map +1 -0
- package/dist/lib/object/index.js +12 -0
- package/dist/lib/object/index.mjs +6 -0
- package/dist/lib/object/merge.cjs +1 -0
- package/dist/lib/object/merge.d.ts +93 -0
- package/dist/lib/object/merge.d.ts.map +1 -0
- package/dist/lib/object/merge.js +474 -0
- package/dist/lib/object/merge.mjs +1 -0
- package/dist/lib/object/object.cjs +1 -0
- package/dist/lib/object/object.d.ts +128 -0
- package/dist/lib/object/object.d.ts.map +1 -0
- package/dist/lib/object/object.js +432 -0
- package/dist/lib/object/object.mjs +1 -0
- package/dist/lib/object/omit.cjs +1 -0
- package/dist/lib/object/omit.d.ts +351 -0
- package/dist/lib/object/omit.d.ts.map +1 -0
- package/dist/lib/object/omit.js +656 -0
- package/dist/lib/object/omit.mjs +1 -0
- package/dist/lib/object/reconcile.cjs +1 -0
- package/dist/lib/object/reconcile.d.ts +7 -0
- package/dist/lib/object/reconcile.d.ts.map +1 -0
- package/dist/lib/object/reconcile.js +122 -0
- package/dist/lib/object/reconcile.mjs +1 -0
- package/dist/lib/object/selector.cjs +1 -0
- package/dist/lib/object/selector.d.ts +50 -0
- package/dist/lib/object/selector.d.ts.map +1 -0
- package/dist/lib/object/selector.js +371 -0
- package/dist/lib/object/selector.mjs +1 -0
- package/dist/lib/object/sort.cjs +1 -0
- package/dist/lib/object/sort.d.ts +149 -0
- package/dist/lib/object/sort.d.ts.map +1 -0
- package/dist/lib/object/sort.js +298 -0
- package/dist/lib/object/sort.mjs +1 -0
- package/dist/lib/object/transform.cjs +1 -0
- package/dist/lib/object/transform.d.ts +620 -0
- package/dist/lib/object/transform.d.ts.map +1 -0
- package/dist/lib/object/transform.js +1058 -0
- package/dist/lib/object/transform.mjs +1 -0
- package/dist/lib/object/types.cjs +1 -0
- package/dist/lib/object/types.d.ts +250 -0
- package/dist/lib/object/types.d.ts.map +1 -0
- package/dist/lib/object/types.js +1 -0
- package/dist/lib/object/types.mjs +0 -0
- package/dist/lib/object/utils.cjs +6 -0
- package/dist/lib/object/utils.d.ts +165 -0
- package/dist/lib/object/utils.d.ts.map +1 -0
- package/dist/lib/object/utils.js +444 -0
- package/dist/lib/object/utils.mjs +6 -0
- package/dist/lib/primitive/boolean/boolean.cjs +1 -0
- package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
- package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/boolean.js +615 -0
- package/dist/lib/primitive/boolean/boolean.mjs +1 -0
- package/dist/lib/primitive/boolean/index.cjs +1 -0
- package/dist/lib/primitive/boolean/index.d.ts +3 -0
- package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/index.js +2 -0
- package/dist/lib/primitive/boolean/index.mjs +1 -0
- package/dist/lib/primitive/boolean/types.cjs +1 -0
- package/dist/lib/primitive/boolean/types.d.ts +14 -0
- package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/types.js +1 -0
- package/dist/lib/primitive/boolean/types.mjs +0 -0
- package/dist/lib/primitive/date/date.cjs +1 -0
- package/dist/lib/primitive/date/date.d.ts +641 -0
- package/dist/lib/primitive/date/date.d.ts.map +1 -0
- package/dist/lib/primitive/date/date.js +1998 -0
- package/dist/lib/primitive/date/date.mjs +1 -0
- package/dist/lib/primitive/date/index.cjs +1 -0
- package/dist/lib/primitive/date/index.d.ts +3 -0
- package/dist/lib/primitive/date/index.d.ts.map +1 -0
- package/dist/lib/primitive/date/index.js +2 -0
- package/dist/lib/primitive/date/index.mjs +1 -0
- package/dist/lib/primitive/date/types.cjs +1 -0
- package/dist/lib/primitive/date/types.d.ts +205 -0
- package/dist/lib/primitive/date/types.d.ts.map +1 -0
- package/dist/lib/primitive/date/types.js +7 -0
- package/dist/lib/primitive/date/types.mjs +0 -0
- package/dist/lib/primitive/index.cjs +5 -0
- package/dist/lib/primitive/index.d.ts +5 -0
- package/dist/lib/primitive/index.d.ts.map +1 -0
- package/dist/lib/primitive/index.js +4 -0
- package/dist/lib/primitive/index.mjs +5 -0
- package/dist/lib/primitive/string/index.cjs +5 -0
- package/dist/lib/primitive/string/index.d.ts +3 -0
- package/dist/lib/primitive/string/index.d.ts.map +1 -0
- package/dist/lib/primitive/string/index.js +2 -0
- package/dist/lib/primitive/string/index.mjs +5 -0
- package/dist/lib/primitive/string/parser-2.cjs +8 -0
- package/dist/lib/primitive/string/parser-2.d.ts +17 -0
- package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser-2.js +656 -0
- package/dist/lib/primitive/string/parser-2.mjs +8 -0
- package/dist/lib/primitive/string/parser.cjs +3 -0
- package/dist/lib/primitive/string/parser.d.ts +9 -0
- package/dist/lib/primitive/string/parser.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser.js +775 -0
- package/dist/lib/primitive/string/parser.mjs +3 -0
- package/dist/lib/primitive/string/string.cjs +5 -0
- package/dist/lib/primitive/string/string.d.ts +535 -0
- package/dist/lib/primitive/string/string.d.ts.map +1 -0
- package/dist/lib/primitive/string/string.js +1301 -0
- package/dist/lib/primitive/string/string.mjs +5 -0
- package/dist/lib/primitive/string/types.cjs +1 -0
- package/dist/lib/primitive/string/types.d.ts +318 -0
- package/dist/lib/primitive/string/types.d.ts.map +1 -0
- package/dist/lib/primitive/string/types.js +5 -0
- package/dist/lib/primitive/string/types.mjs +0 -0
- package/dist/lib/primitive/types.cjs +1 -0
- package/dist/lib/primitive/types.d.ts +27 -0
- package/dist/lib/primitive/types.d.ts.map +1 -0
- package/dist/lib/primitive/types.js +1 -0
- package/dist/lib/primitive/types.mjs +0 -0
- package/dist/lib/url/index.cjs +1 -0
- package/dist/lib/url/index.d.ts +3 -0
- package/dist/lib/url/index.d.ts.map +1 -0
- package/dist/lib/url/index.js +2 -0
- package/dist/lib/url/index.mjs +1 -0
- package/dist/lib/url/parser.cjs +1 -0
- package/dist/lib/url/parser.d.ts +216 -0
- package/dist/lib/url/parser.d.ts.map +1 -0
- package/dist/lib/url/parser.js +488 -0
- package/dist/lib/url/parser.mjs +1 -0
- package/dist/lib/url/types.cjs +1 -0
- package/dist/lib/url/types.d.ts +18 -0
- package/dist/lib/url/types.d.ts.map +1 -0
- package/dist/lib/url/types.js +1 -0
- package/dist/lib/url/types.mjs +0 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +150 -0
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Comprehensive color management utility with support for multiple color formats,
|
|
3
|
+
* conversions, and color manipulation operations.
|
|
4
|
+
*/
|
|
5
|
+
import { NAMED_COLORS } from './constants.js';
|
|
6
|
+
/**
|
|
7
|
+
* Comprehensive RGB/RGBA regex patterns
|
|
8
|
+
*/
|
|
9
|
+
export const RGB_PATTERNS = {
|
|
10
|
+
/**
|
|
11
|
+
* Matches legacy comma-separated RGB
|
|
12
|
+
* @example rgb(255, 0, 0)
|
|
13
|
+
*/
|
|
14
|
+
LEGACY_RGB: /^rgb\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*\)$/i,
|
|
15
|
+
/**
|
|
16
|
+
* Matches modern space-separated RGB
|
|
17
|
+
* @example rgb(255 0 0)
|
|
18
|
+
*/
|
|
19
|
+
MODERN_RGB: /^rgb\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s*\)$/i,
|
|
20
|
+
/**
|
|
21
|
+
* Matches legacy comma-separated RGBA
|
|
22
|
+
* @example rgba(255, 0, 0, 0.5)
|
|
23
|
+
* @example rgba(255, 0, 0, 50%)
|
|
24
|
+
*/
|
|
25
|
+
LEGACY_RGBA: /^rgba\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(0|1|0?\.\d+|\d{1,3}%?|\.\d+%?)\s*\)$/i,
|
|
26
|
+
/**
|
|
27
|
+
* Matches modern space-separated RGBA with slash
|
|
28
|
+
* @example rgb(255 0 0 / 0.5)
|
|
29
|
+
* @example rgb(255 0 0 / 50%)
|
|
30
|
+
* @example rgba(255 0 0 / 0.5)
|
|
31
|
+
*/
|
|
32
|
+
MODERN_RGBA: /^rgba?\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s*\/\s*(0|1|0?\.\d+|\d{1,3}%?|\.\d+%?)\s*\)$/i,
|
|
33
|
+
/**
|
|
34
|
+
* Combined pattern that matches all RGB/RGBA variations
|
|
35
|
+
* Supports:
|
|
36
|
+
* - Legacy comma-separated: rgb(255, 0, 0), rgba(255, 0, 0, 0.5)
|
|
37
|
+
* - Modern space-separated: rgb(255 0 0), rgb(255 0 0 / 0.5)
|
|
38
|
+
* - Mixed separators: rgb(255,0,0), rgb(255 0 0)
|
|
39
|
+
* - Percentages: rgb(100% 0% 0%), rgb(100% 0% 0% / 50%)
|
|
40
|
+
* - Decimal numbers: rgb(255.5 0 0), rgba(255, 0, 0, 0.5)
|
|
41
|
+
* - CSS variables: rgb(var(--red) var(--green) var(--blue))
|
|
42
|
+
*/
|
|
43
|
+
COMPREHENSIVE: /^rgba?\(\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s*(?:[,\s]\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\))))?\s*(?:[,\s]\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\))))?\s*(?:(?:\s*[,\s]?\s*\/?\s*|\s*\/\s*)((?:0|1|0?\.\d+|\.\d+|\d{1,3}%?|var\([^)]+\))))?\s*\)$/i,
|
|
44
|
+
/**
|
|
45
|
+
* Strict RGB pattern with validation
|
|
46
|
+
* Matches integers (0-255) or percentages (0-100%)
|
|
47
|
+
*/
|
|
48
|
+
STRICT_RGB: /^rgb\(\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*\)$/i,
|
|
49
|
+
/**
|
|
50
|
+
* Strict RGBA pattern with validation
|
|
51
|
+
*/
|
|
52
|
+
STRICT_RGBA: /^rgba\(\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:0|1|0?\.\d+|\.\d+|\d{1,2}(?:\.\d+)?%|100%|var\([^)]+\)))\s*\)$/i,
|
|
53
|
+
/**
|
|
54
|
+
* Modern RGB with optional alpha
|
|
55
|
+
*/
|
|
56
|
+
MODERN_RGB_ALPHA: /^rgba?\(\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s+((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s+((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s*(?:\/\s*((?:0|1|0?\.\d+|\.\d+|\d{1,3}%?|var\([^)]+\))))?\s*\)$/i,
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Enhanced RGB/RGBA parsing function
|
|
60
|
+
* @param colorString - RGB/RGBA color string
|
|
61
|
+
* @returns Parsed RGBA values or null if invalid
|
|
62
|
+
*/
|
|
63
|
+
export const parseRgbaColor = (colorString) => {
|
|
64
|
+
const str = colorString.trim().toLowerCase();
|
|
65
|
+
// Return null for empty strings or CSS variables
|
|
66
|
+
if (!str || str.includes('var(')) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
// Helper to parse a single color component (r, g, or b)
|
|
70
|
+
const parseComponent = (component) => {
|
|
71
|
+
if (!component)
|
|
72
|
+
return null;
|
|
73
|
+
const trimmed = component.trim();
|
|
74
|
+
if (!trimmed || trimmed.includes('var('))
|
|
75
|
+
return null;
|
|
76
|
+
if (trimmed.endsWith('%')) {
|
|
77
|
+
const percent = parseFloat(trimmed);
|
|
78
|
+
if (isNaN(percent))
|
|
79
|
+
return null;
|
|
80
|
+
// Clamp percentage to 0-100 range, then convert to 0-255
|
|
81
|
+
const clamped = Math.max(0, Math.min(100, percent));
|
|
82
|
+
return Math.round((clamped * 255) / 100);
|
|
83
|
+
}
|
|
84
|
+
const num = parseFloat(trimmed);
|
|
85
|
+
if (isNaN(num))
|
|
86
|
+
return null;
|
|
87
|
+
return Math.round(Math.max(0, Math.min(255, num)));
|
|
88
|
+
};
|
|
89
|
+
// Helper to parse alpha component
|
|
90
|
+
const parseAlpha = (alpha) => {
|
|
91
|
+
if (!alpha)
|
|
92
|
+
return 1;
|
|
93
|
+
const trimmed = alpha.trim();
|
|
94
|
+
if (!trimmed || trimmed.includes('var('))
|
|
95
|
+
return 1;
|
|
96
|
+
if (trimmed.endsWith('%')) {
|
|
97
|
+
const percent = parseFloat(trimmed);
|
|
98
|
+
if (isNaN(percent))
|
|
99
|
+
return 1;
|
|
100
|
+
return Math.max(0, Math.min(100, percent)) / 100;
|
|
101
|
+
}
|
|
102
|
+
const num = parseFloat(trimmed);
|
|
103
|
+
if (isNaN(num))
|
|
104
|
+
return 1;
|
|
105
|
+
return Math.max(0, Math.min(1, num));
|
|
106
|
+
};
|
|
107
|
+
// Pattern 1: Legacy comma-separated (must come first to properly handle percentages)
|
|
108
|
+
// rgb(255, 0, 0) or rgba(255, 0, 0, 0.5) or rgba(255, 0, 0, 50%)
|
|
109
|
+
// Also handles rgb(100%, 0%, 0%)
|
|
110
|
+
const legacyCommaPattern = /^rgba?\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*(?:,\s*([^\)]+))?\s*\)$/;
|
|
111
|
+
let match = str.match(legacyCommaPattern);
|
|
112
|
+
if (match) {
|
|
113
|
+
const r = parseComponent(match[1]);
|
|
114
|
+
const g = parseComponent(match[2]);
|
|
115
|
+
const b = parseComponent(match[3]);
|
|
116
|
+
const a = parseAlpha(match[4]);
|
|
117
|
+
if (r !== null && g !== null && b !== null) {
|
|
118
|
+
return [r, g, b, a];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Pattern 2: Modern space-separated with slash alpha
|
|
122
|
+
// rgb(255 0 0 / 0.5) or rgba(255 0 0 / 50%)
|
|
123
|
+
const modernSlashPattern = /^rgba?\(\s*([^\s\/]+)\s+([^\s\/]+)\s+([^\s\/]+)\s*(?:\/\s*([^\)]+))?\s*\)$/;
|
|
124
|
+
match = str.match(modernSlashPattern);
|
|
125
|
+
if (match) {
|
|
126
|
+
const r = parseComponent(match[1]);
|
|
127
|
+
const g = parseComponent(match[2]);
|
|
128
|
+
const b = parseComponent(match[3]);
|
|
129
|
+
const a = parseAlpha(match[4]);
|
|
130
|
+
if (r !== null && g !== null && b !== null) {
|
|
131
|
+
return [r, g, b, a];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Pattern 3: Modern space-separated without slash (rgb only, no alpha)
|
|
135
|
+
// rgb(255 0 0)
|
|
136
|
+
const modernSpacePattern = /^rgb\(\s*([^\s,]+)\s+([^\s,]+)\s+([^\s,]+)\s*\)$/;
|
|
137
|
+
match = str.match(modernSpacePattern);
|
|
138
|
+
if (match) {
|
|
139
|
+
const r = parseComponent(match[1]);
|
|
140
|
+
const g = parseComponent(match[2]);
|
|
141
|
+
const b = parseComponent(match[3]);
|
|
142
|
+
if (r !== null && g !== null && b !== null) {
|
|
143
|
+
return [r, g, b, 1];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Enhanced color pattern collection with improved RGB support
|
|
150
|
+
*/
|
|
151
|
+
export const COLOR_PATTERNS = {
|
|
152
|
+
// Hex patterns (unchanged)
|
|
153
|
+
HEX: /^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/,
|
|
154
|
+
// RGB patterns (enhanced)
|
|
155
|
+
RGB: RGB_PATTERNS.COMPREHENSIVE,
|
|
156
|
+
// HSL patterns (supports both hsl and hsla)
|
|
157
|
+
HSL: /^hsla?\(\s*(\d+(?:\.\d+)?)(?:deg|rad|grad|turn)?\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
158
|
+
// OKLCH pattern
|
|
159
|
+
OKLCH: /^oklch\(\s*(\d+(?:\.\d+)?%?)\s+(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)(?:\s+[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
160
|
+
// OKLAB pattern
|
|
161
|
+
OKLAB: /^oklab\(\s*(\d+(?:\.\d+)?%?)\s+([+-]?\d+(?:\.\d+)?)\s+([+-]?\d+(?:\.\d+)?)(?:\s+[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
162
|
+
// HSV pattern
|
|
163
|
+
HSV: /^hsv\(\s*(\d+(?:\.\d+)?)(?:deg|rad|grad|turn)?\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
164
|
+
// CMYK pattern
|
|
165
|
+
CMYK: /^cmyk\(\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
166
|
+
// CSS color() function
|
|
167
|
+
COLOR_FUNCTION: /^color\(\s*([a-z]+)\s+((?:\d+(?:\.\d+)?%?\s+)*\d+(?:\.\d+)?%?)(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
168
|
+
// Lab color
|
|
169
|
+
LAB: /^lab\(\s*(\d+(?:\.\d+)?%?)\s+([+-]?\d+(?:\.\d+)?)\s+([+-]?\d+(?:\.\d+)?)(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
170
|
+
// LCH color
|
|
171
|
+
LCH: /^lch\(\s*(\d+(?:\.\d+)?%?)\s+(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Test RGB/RGBA strings against all patterns
|
|
175
|
+
* @param colorString - Color string to test
|
|
176
|
+
* @returns Array of matching pattern names
|
|
177
|
+
*/
|
|
178
|
+
export const testRgbaPatterns = (colorString) => {
|
|
179
|
+
const matches = [];
|
|
180
|
+
const str = colorString.trim();
|
|
181
|
+
Object.entries(RGB_PATTERNS).forEach(([name, pattern]) => {
|
|
182
|
+
if (pattern.test(str)) {
|
|
183
|
+
matches.push(name);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return matches;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Enhanced parseColor function with better RGB support
|
|
190
|
+
*/
|
|
191
|
+
export const parseColor = (color) => {
|
|
192
|
+
if (!color || typeof color !== 'string') {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const str = color.trim().toLowerCase();
|
|
196
|
+
if (!str) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
// Handle transparent
|
|
200
|
+
if (str === 'transparent') {
|
|
201
|
+
return [0, 0, 0, 0];
|
|
202
|
+
}
|
|
203
|
+
// Handle named colors
|
|
204
|
+
const namedColor = str;
|
|
205
|
+
if (NAMED_COLORS[namedColor]) {
|
|
206
|
+
return parseColor(NAMED_COLORS[namedColor]);
|
|
207
|
+
}
|
|
208
|
+
// Try RGB/RGBA parsing first (most common for programmatic use)
|
|
209
|
+
const rgba = parseRgbaColor(str);
|
|
210
|
+
if (rgba) {
|
|
211
|
+
return rgba;
|
|
212
|
+
}
|
|
213
|
+
// Try hex colors
|
|
214
|
+
const hexMatch = str.match(COLOR_PATTERNS.HEX);
|
|
215
|
+
if (hexMatch) {
|
|
216
|
+
let hex = hexMatch[1];
|
|
217
|
+
// Expand shorthand hex (#RGB or #RGBA to #RRGGBB or #RRGGBBAA)
|
|
218
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
219
|
+
hex = hex
|
|
220
|
+
.split('')
|
|
221
|
+
.map((c) => c + c)
|
|
222
|
+
.join('');
|
|
223
|
+
}
|
|
224
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
225
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
226
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
227
|
+
const a = hex.length === 8 ? parseInt(hex.substring(6, 8), 16) / 255 : 1;
|
|
228
|
+
// Validate parsed values
|
|
229
|
+
if (!isNaN(r) && !isNaN(g) && !isNaN(b) && !isNaN(a)) {
|
|
230
|
+
return [r, g, b, a];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Try HSL colors
|
|
234
|
+
const hslMatch = str.match(COLOR_PATTERNS.HSL);
|
|
235
|
+
if (hslMatch) {
|
|
236
|
+
const h = parseFloat(hslMatch[1]);
|
|
237
|
+
const s = parseFloat(hslMatch[2]) / 100;
|
|
238
|
+
const l = parseFloat(hslMatch[3]) / 100;
|
|
239
|
+
const a = hslMatch[4] ? parsePercentage(hslMatch[4]) : 1;
|
|
240
|
+
if (!isNaN(h) && !isNaN(s) && !isNaN(l) && !isNaN(a)) {
|
|
241
|
+
// Convert HSL to RGB
|
|
242
|
+
return hslToRgb(h, s, l, a);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Try OKLCH colors
|
|
246
|
+
const oklchMatch = str.match(COLOR_PATTERNS.OKLCH);
|
|
247
|
+
if (oklchMatch) {
|
|
248
|
+
const L = parsePercentage(oklchMatch[1]);
|
|
249
|
+
const C = parseFloat(oklchMatch[2]);
|
|
250
|
+
const H = parseFloat(oklchMatch[3]);
|
|
251
|
+
const a = oklchMatch[4] ? parsePercentage(oklchMatch[4]) : 1;
|
|
252
|
+
if (!isNaN(L) && !isNaN(C) && !isNaN(H) && !isNaN(a)) {
|
|
253
|
+
// Convert OKLCH to RGB
|
|
254
|
+
return oklchToRgb(L, C, H, a);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Convert HSL to RGB
|
|
261
|
+
*/
|
|
262
|
+
export const hslToRgb = (h, s, l, a = 1) => {
|
|
263
|
+
h = h % 360;
|
|
264
|
+
if (h < 0)
|
|
265
|
+
h += 360;
|
|
266
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
267
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
268
|
+
const m = l - c / 2;
|
|
269
|
+
let r = 0, g = 0, b = 0;
|
|
270
|
+
if (h >= 0 && h < 60) {
|
|
271
|
+
[r, g, b] = [c, x, 0];
|
|
272
|
+
}
|
|
273
|
+
else if (h >= 60 && h < 120) {
|
|
274
|
+
[r, g, b] = [x, c, 0];
|
|
275
|
+
}
|
|
276
|
+
else if (h >= 120 && h < 180) {
|
|
277
|
+
[r, g, b] = [0, c, x];
|
|
278
|
+
}
|
|
279
|
+
else if (h >= 180 && h < 240) {
|
|
280
|
+
[r, g, b] = [0, x, c];
|
|
281
|
+
}
|
|
282
|
+
else if (h >= 240 && h < 300) {
|
|
283
|
+
[r, g, b] = [x, 0, c];
|
|
284
|
+
}
|
|
285
|
+
else if (h >= 300 && h < 360) {
|
|
286
|
+
[r, g, b] = [c, 0, x];
|
|
287
|
+
}
|
|
288
|
+
return [
|
|
289
|
+
Math.round((r + m) * 255),
|
|
290
|
+
Math.round((g + m) * 255),
|
|
291
|
+
Math.round((b + m) * 255),
|
|
292
|
+
a,
|
|
293
|
+
];
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Convert OKLCH to RGB (simplified approximation)
|
|
297
|
+
*/
|
|
298
|
+
export const oklchToRgb = (L, C, H, a = 1) => {
|
|
299
|
+
// This is a simplified approximation
|
|
300
|
+
// Real conversion is complex and requires proper color space conversion
|
|
301
|
+
const hue = H % 360;
|
|
302
|
+
const saturation = Math.min(100, C * 100);
|
|
303
|
+
const lightness = L * 100;
|
|
304
|
+
return hslToRgb(hue, saturation / 100, lightness / 100, a);
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Formats RGBA values into a CSS rgba() or rgb() string
|
|
308
|
+
* @param r - Red value (0-255) or CSS variable
|
|
309
|
+
* @param g - Green value (0-255) or CSS variable
|
|
310
|
+
* @param b - Blue value (0-255) or CSS variable
|
|
311
|
+
* @param a - Alpha value (0-1) or CSS variable (default: 1)
|
|
312
|
+
* @returns Formatted rgba string
|
|
313
|
+
* @example
|
|
314
|
+
* formatRgba(255, 0, 0, 0.5) // returns "rgba(255, 0, 0, 0.5)"
|
|
315
|
+
* formatRgba(255, 0, 0, 'var(--opacity)') // returns "rgba(255, 0, 0, var(--opacity))"
|
|
316
|
+
*/
|
|
317
|
+
export const formatRgba = (r, g, b, a = 1) => {
|
|
318
|
+
// Handle non-numeric or CSS variable values
|
|
319
|
+
const isVariable = (value) => typeof value === 'string' && value.includes('var(');
|
|
320
|
+
const isAlphaVariable = isVariable(a);
|
|
321
|
+
const hasAlpha = isAlphaVariable || (typeof a === 'number' && a !== 1);
|
|
322
|
+
// Use modern CSS syntax with spaces and slash for alpha
|
|
323
|
+
if (hasAlpha) {
|
|
324
|
+
return `rgba(${r} ${g} ${b} / ${a})`;
|
|
325
|
+
}
|
|
326
|
+
return `rgb(${r} ${g} ${b})`;
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Converts a numeric value to a two-digit hex string
|
|
330
|
+
* @param value - Number between 0-255
|
|
331
|
+
* @returns Two-digit hex string
|
|
332
|
+
* @private
|
|
333
|
+
*/
|
|
334
|
+
const numToHex = (value) => {
|
|
335
|
+
const hex = Math.round(value).toString(16);
|
|
336
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* Converts a percentage string to a number
|
|
340
|
+
* @param value - Percentage string (e.g., "50%")
|
|
341
|
+
* @returns Number between 0-1
|
|
342
|
+
* @private
|
|
343
|
+
*/
|
|
344
|
+
const parsePercentage = (value) => {
|
|
345
|
+
if (value.endsWith('%')) {
|
|
346
|
+
return parseFloat(value) / 100;
|
|
347
|
+
}
|
|
348
|
+
return parseFloat(value);
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Parses any color string to RGBA values
|
|
352
|
+
* @param color - Color string in any supported format
|
|
353
|
+
* @returns RGBA array [r, g, b, a] or null if invalid
|
|
354
|
+
*/
|
|
355
|
+
// export const parseColor = (
|
|
356
|
+
// color: string
|
|
357
|
+
// ): [number, number, number, number] | null => {
|
|
358
|
+
// // Handle transparent
|
|
359
|
+
// if (color.toLowerCase() === 'transparent') {
|
|
360
|
+
// return [0, 0, 0, 0];
|
|
361
|
+
// }
|
|
362
|
+
// // Handle named colors
|
|
363
|
+
// const namedColor = color.toLowerCase() as NamedColor;
|
|
364
|
+
// if (NAMED_COLORS[namedColor]) {
|
|
365
|
+
// return parseColor(NAMED_COLORS[namedColor]);
|
|
366
|
+
// }
|
|
367
|
+
// // Handle hex colors
|
|
368
|
+
// const hexMatch = color.match(COLOR_PATTERNS.HEX);
|
|
369
|
+
// if (hexMatch) {
|
|
370
|
+
// let hex = hexMatch[1];
|
|
371
|
+
// // Expand shorthand hex
|
|
372
|
+
// if (hex.length === 3 || hex.length === 4) {
|
|
373
|
+
// hex = hex
|
|
374
|
+
// .split('')
|
|
375
|
+
// .map((c) => c + c)
|
|
376
|
+
// .join('');
|
|
377
|
+
// }
|
|
378
|
+
// const r = parseInt(hex.substring(0, 2), 16);
|
|
379
|
+
// const g = parseInt(hex.substring(2, 4), 16);
|
|
380
|
+
// const b = parseInt(hex.substring(4, 6), 16);
|
|
381
|
+
// const a =
|
|
382
|
+
// hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 1) : 1;
|
|
383
|
+
// return [r, g, b, a];
|
|
384
|
+
// }
|
|
385
|
+
// // Handle rgb/rgba colors
|
|
386
|
+
// const rgbMatch = color.match(COLOR_PATTERNS.RGB);
|
|
387
|
+
// if (rgbMatch) {
|
|
388
|
+
// const r = parseInt(rgbMatch[1]);
|
|
389
|
+
// const g = parseInt(rgbMatch[2]);
|
|
390
|
+
// const b = parseInt(rgbMatch[3]);
|
|
391
|
+
// const a = rgbMatch[4] ? parsePercentage(rgbMatch[4]) : 1;
|
|
392
|
+
// return [r, g, b, a];
|
|
393
|
+
// }
|
|
394
|
+
// // Handle hsl/hsla colors
|
|
395
|
+
// const hslMatch = color.match(COLOR_PATTERNS.HSL);
|
|
396
|
+
// if (hslMatch) {
|
|
397
|
+
// let h = parseFloat(hslMatch[1]);
|
|
398
|
+
// const s = parseFloat(hslMatch[2]) / 100;
|
|
399
|
+
// const l = parseFloat(hslMatch[3]) / 100;
|
|
400
|
+
// const a = hslMatch[4] ? parsePercentage(hslMatch[4]) : 1;
|
|
401
|
+
// // Convert HSL to RGB
|
|
402
|
+
// const chroma = (1 - Math.abs(2 * l - 1)) * s;
|
|
403
|
+
// const x = chroma * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
404
|
+
// const m = l - chroma / 2;
|
|
405
|
+
// let r = 0,
|
|
406
|
+
// g = 0,
|
|
407
|
+
// b = 0;
|
|
408
|
+
// if (h >= 0 && h < 60) {
|
|
409
|
+
// [r, g, b] = [chroma, x, 0];
|
|
410
|
+
// } else if (h >= 60 && h < 120) {
|
|
411
|
+
// [r, g, b] = [x, chroma, 0];
|
|
412
|
+
// } else if (h >= 120 && h < 180) {
|
|
413
|
+
// [r, g, b] = [0, chroma, x];
|
|
414
|
+
// } else if (h >= 180 && h < 240) {
|
|
415
|
+
// [r, g, b] = [0, x, chroma];
|
|
416
|
+
// } else if (h >= 240 && h < 300) {
|
|
417
|
+
// [r, g, b] = [x, 0, chroma];
|
|
418
|
+
// } else if (h >= 300 && h < 360) {
|
|
419
|
+
// [r, g, b] = [chroma, 0, x];
|
|
420
|
+
// }
|
|
421
|
+
// return [
|
|
422
|
+
// Math.round((r + m) * 255),
|
|
423
|
+
// Math.round((g + m) * 255),
|
|
424
|
+
// Math.round((b + m) * 255),
|
|
425
|
+
// a,
|
|
426
|
+
// ];
|
|
427
|
+
// }
|
|
428
|
+
// // Handle oklch colors
|
|
429
|
+
// const oklchMatch = color.match(COLOR_PATTERNS.OKLCH);
|
|
430
|
+
// if (oklchMatch) {
|
|
431
|
+
// // For simplicity, we'll convert oklch to rgb via hsl approximation
|
|
432
|
+
// // Note: This is a simplified conversion
|
|
433
|
+
// const L = parsePercentage(oklchMatch[1]);
|
|
434
|
+
// const C = parseFloat(oklchMatch[2]);
|
|
435
|
+
// const H = parseFloat(oklchMatch[3]);
|
|
436
|
+
// const a = oklchMatch[4] ? parsePercentage(oklchMatch[4]) : 1;
|
|
437
|
+
// // Approximate conversion from OKLCH to RGB
|
|
438
|
+
// // This is a simplified version - real conversion is more complex
|
|
439
|
+
// const h = H;
|
|
440
|
+
// const s = C * 100;
|
|
441
|
+
// const l = L * 100;
|
|
442
|
+
// const rgb = parseColor(`hsl(${h}, ${s}%, ${l}%)`);
|
|
443
|
+
// if (rgb) {
|
|
444
|
+
// rgb[3] = a;
|
|
445
|
+
// return rgb;
|
|
446
|
+
// }
|
|
447
|
+
// }
|
|
448
|
+
// return null;
|
|
449
|
+
// };
|
|
450
|
+
/**
|
|
451
|
+
* Validates if a string is a valid color
|
|
452
|
+
* @param color - Color string to validate
|
|
453
|
+
* @returns True if valid color
|
|
454
|
+
*/
|
|
455
|
+
export const isValidColor = (color) => {
|
|
456
|
+
// Check named colors
|
|
457
|
+
if (color.toLowerCase() in NAMED_COLORS) {
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
// For RGB/RGBA, delegate to the actual parser to ensure proper validation
|
|
461
|
+
if (color.toLowerCase().trim().startsWith('rgb')) {
|
|
462
|
+
return parseRgbaColor(color) !== null;
|
|
463
|
+
}
|
|
464
|
+
// Check other patterns (HSL, hex, etc.)
|
|
465
|
+
const { RGB, ...otherPatterns } = COLOR_PATTERNS;
|
|
466
|
+
return Object.values(otherPatterns).some((pattern) => pattern.test(color));
|
|
467
|
+
};
|
|
468
|
+
/**
|
|
469
|
+
* Converts RGBA values to hex string
|
|
470
|
+
* @param r - Red value (0-255)
|
|
471
|
+
* @param g - Green value (0-255)
|
|
472
|
+
* @param b - Blue value (0-255)
|
|
473
|
+
* @param a - Alpha value (0-1)
|
|
474
|
+
* @returns Hex color string
|
|
475
|
+
*/
|
|
476
|
+
export const rgbaToHex = (r, g, b, a = 1) => {
|
|
477
|
+
const hex = `#${numToHex(r)}${numToHex(g)}${numToHex(b)}`;
|
|
478
|
+
return a < 1 ? `${hex}${numToHex(Math.round(a * 255))}` : hex;
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* Converts RGBA values to HSL string
|
|
482
|
+
* @param r - Red value (0-255)
|
|
483
|
+
* @param g - Green value (0-255)
|
|
484
|
+
* @param b - Blue value (0-255)
|
|
485
|
+
* @param a - Alpha value (0-1)
|
|
486
|
+
* @returns HSL/HSLA color string
|
|
487
|
+
*/
|
|
488
|
+
export const rgbaToHsl = (r, g, b, a = 1) => {
|
|
489
|
+
const rNorm = r / 255;
|
|
490
|
+
const gNorm = g / 255;
|
|
491
|
+
const bNorm = b / 255;
|
|
492
|
+
const max = Math.max(rNorm, gNorm, bNorm);
|
|
493
|
+
const min = Math.min(rNorm, gNorm, bNorm);
|
|
494
|
+
let h = 0;
|
|
495
|
+
let s = 0;
|
|
496
|
+
const l = (max + min) / 2;
|
|
497
|
+
if (max !== min) {
|
|
498
|
+
const d = max - min;
|
|
499
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
500
|
+
switch (max) {
|
|
501
|
+
case rNorm:
|
|
502
|
+
h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0);
|
|
503
|
+
break;
|
|
504
|
+
case gNorm:
|
|
505
|
+
h = (bNorm - rNorm) / d + 2;
|
|
506
|
+
break;
|
|
507
|
+
case bNorm:
|
|
508
|
+
h = (rNorm - gNorm) / d + 4;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
h /= 6;
|
|
512
|
+
}
|
|
513
|
+
h = Math.round(h * 360);
|
|
514
|
+
s = Math.round(s * 100);
|
|
515
|
+
const lPercent = Math.round(l * 100);
|
|
516
|
+
if (a === 1) {
|
|
517
|
+
return `hsl(${h}, ${s}%, ${lPercent}%)`;
|
|
518
|
+
}
|
|
519
|
+
return `hsla(${h}, ${s}%, ${lPercent}%, ${a})`;
|
|
520
|
+
};
|
|
521
|
+
/**
|
|
522
|
+
* Converts RGBA values to HSV string
|
|
523
|
+
* @param r - Red value (0-255)
|
|
524
|
+
* @param g - Green value (0-255)
|
|
525
|
+
* @param b - Blue value (0-255)
|
|
526
|
+
* @param a - Alpha value (0-1)
|
|
527
|
+
* @returns HSV/HSVA color string
|
|
528
|
+
*/
|
|
529
|
+
export const rgbaToHsv = (r, g, b, a = 1) => {
|
|
530
|
+
const rNorm = r / 255;
|
|
531
|
+
const gNorm = g / 255;
|
|
532
|
+
const bNorm = b / 255;
|
|
533
|
+
const max = Math.max(rNorm, gNorm, bNorm);
|
|
534
|
+
const min = Math.min(rNorm, gNorm, bNorm);
|
|
535
|
+
const d = max - min;
|
|
536
|
+
let h = 0;
|
|
537
|
+
if (d !== 0) {
|
|
538
|
+
switch (max) {
|
|
539
|
+
case rNorm:
|
|
540
|
+
h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0);
|
|
541
|
+
break;
|
|
542
|
+
case gNorm:
|
|
543
|
+
h = (bNorm - rNorm) / d + 2;
|
|
544
|
+
break;
|
|
545
|
+
case bNorm:
|
|
546
|
+
h = (rNorm - gNorm) / d + 4;
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
h /= 6;
|
|
550
|
+
}
|
|
551
|
+
const s = max === 0 ? 0 : d / max;
|
|
552
|
+
const v = max;
|
|
553
|
+
h = Math.round(h * 360);
|
|
554
|
+
const sPercent = Math.round(s * 100);
|
|
555
|
+
const vPercent = Math.round(v * 100);
|
|
556
|
+
if (a === 1) {
|
|
557
|
+
return `hsv(${h}, ${sPercent}%, ${vPercent}%)`;
|
|
558
|
+
}
|
|
559
|
+
return `hsva(${h}, ${sPercent}%, ${vPercent}%, ${a})`;
|
|
560
|
+
};
|
|
561
|
+
/**
|
|
562
|
+
* Converts RGBA values to CMYK string
|
|
563
|
+
* @param r - Red value (0-255)
|
|
564
|
+
* @param g - Green value (0-255)
|
|
565
|
+
* @param b - Blue value (0-255)
|
|
566
|
+
* @param a - Alpha value (0-1)
|
|
567
|
+
* @returns CMYK color string
|
|
568
|
+
*/
|
|
569
|
+
export const rgbaToCmyk = (r, g, b, a = 1) => {
|
|
570
|
+
if (r === 0 && g === 0 && b === 0) {
|
|
571
|
+
return `cmyk(0%, 0%, 0%, 100%)`;
|
|
572
|
+
}
|
|
573
|
+
const rNorm = r / 255;
|
|
574
|
+
const gNorm = g / 255;
|
|
575
|
+
const bNorm = b / 255;
|
|
576
|
+
const k = 1 - Math.max(rNorm, gNorm, bNorm);
|
|
577
|
+
const c = (1 - rNorm - k) / (1 - k);
|
|
578
|
+
const m = (1 - gNorm - k) / (1 - k);
|
|
579
|
+
const y = (1 - bNorm - k) / (1 - k);
|
|
580
|
+
const cPercent = Math.round(c * 100);
|
|
581
|
+
const mPercent = Math.round(m * 100);
|
|
582
|
+
const yPercent = Math.round(y * 100);
|
|
583
|
+
const kPercent = Math.round(k * 100);
|
|
584
|
+
return `cmyk(${cPercent}%, ${mPercent}%, ${yPercent}%, ${kPercent}%)`;
|
|
585
|
+
};
|
|
586
|
+
/**
|
|
587
|
+
* Calculates relative luminance for contrast ratio
|
|
588
|
+
* @param r - Red value (0-255)
|
|
589
|
+
* @param g - Green value (0-255)
|
|
590
|
+
* @param b - Blue value (0-255)
|
|
591
|
+
* @returns Relative luminance (0-1)
|
|
592
|
+
* @private
|
|
593
|
+
*/
|
|
594
|
+
export const calculateLuminance = (r, g, b) => {
|
|
595
|
+
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
596
|
+
c = c / 255;
|
|
597
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
598
|
+
});
|
|
599
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
600
|
+
};
|
|
601
|
+
/**
|
|
602
|
+
* Calculates contrast ratio between two colors
|
|
603
|
+
* @param color1 - First color string
|
|
604
|
+
* @param color2 - Second color string
|
|
605
|
+
* @returns Contrast ratio (1-21)
|
|
606
|
+
*/
|
|
607
|
+
export const calculateContrastRatio = (color1, color2) => {
|
|
608
|
+
const rgb1 = parseColor(color1);
|
|
609
|
+
const rgb2 = parseColor(color2);
|
|
610
|
+
if (!rgb1 || !rgb2) {
|
|
611
|
+
return 1;
|
|
612
|
+
}
|
|
613
|
+
const l1 = calculateLuminance(rgb1[0], rgb1[1], rgb1[2]);
|
|
614
|
+
const l2 = calculateLuminance(rgb2[0], rgb2[1], rgb2[2]);
|
|
615
|
+
const lighter = Math.max(l1, l2);
|
|
616
|
+
const darker = Math.min(l1, l2);
|
|
617
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
618
|
+
};
|
|
619
|
+
/**
|
|
620
|
+
* Adjusts color based on options
|
|
621
|
+
* @param color - Input color string
|
|
622
|
+
* @param options - Adjustment options
|
|
623
|
+
* @returns Adjusted color string
|
|
624
|
+
*/
|
|
625
|
+
export const adjustColor = (color, options) => {
|
|
626
|
+
const rgb = parseColor(color);
|
|
627
|
+
if (!rgb)
|
|
628
|
+
return color;
|
|
629
|
+
let [r, g, b, a] = rgb;
|
|
630
|
+
// Adjust alpha
|
|
631
|
+
if (options.alpha !== undefined) {
|
|
632
|
+
a = Math.max(0, Math.min(1, options.alpha));
|
|
633
|
+
}
|
|
634
|
+
// Convert to HSL for hue/saturation/lightness adjustments
|
|
635
|
+
let h = 0, s = 0, l = 0;
|
|
636
|
+
if (options.lightness !== undefined ||
|
|
637
|
+
options.saturation !== undefined ||
|
|
638
|
+
options.hue !== undefined) {
|
|
639
|
+
// Convert RGB to HSL
|
|
640
|
+
const rNorm = r / 255;
|
|
641
|
+
const gNorm = g / 255;
|
|
642
|
+
const bNorm = b / 255;
|
|
643
|
+
const max = Math.max(rNorm, gNorm, bNorm);
|
|
644
|
+
const min = Math.min(rNorm, gNorm, bNorm);
|
|
645
|
+
l = (max + min) / 2;
|
|
646
|
+
if (max !== min) {
|
|
647
|
+
const d = max - min;
|
|
648
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
649
|
+
switch (max) {
|
|
650
|
+
case rNorm:
|
|
651
|
+
h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0);
|
|
652
|
+
break;
|
|
653
|
+
case gNorm:
|
|
654
|
+
h = (bNorm - rNorm) / d + 2;
|
|
655
|
+
break;
|
|
656
|
+
case bNorm:
|
|
657
|
+
h = (rNorm - gNorm) / d + 4;
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
h /= 6;
|
|
661
|
+
}
|
|
662
|
+
h *= 360;
|
|
663
|
+
s *= 100;
|
|
664
|
+
l *= 100;
|
|
665
|
+
// Apply adjustments
|
|
666
|
+
if (options.hue !== undefined) {
|
|
667
|
+
h = (h + options.hue) % 360;
|
|
668
|
+
if (h < 0)
|
|
669
|
+
h += 360;
|
|
670
|
+
}
|
|
671
|
+
if (options.saturation !== undefined) {
|
|
672
|
+
s = Math.max(0, Math.min(100, s + options.saturation));
|
|
673
|
+
}
|
|
674
|
+
if (options.lightness !== undefined) {
|
|
675
|
+
l = Math.max(0, Math.min(100, l + options.lightness));
|
|
676
|
+
}
|
|
677
|
+
// Convert back to RGB
|
|
678
|
+
const hNorm = h / 360;
|
|
679
|
+
const sNorm = s / 100;
|
|
680
|
+
const lNorm = l / 100;
|
|
681
|
+
const chroma = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
|
|
682
|
+
const x = chroma * (1 - Math.abs(((hNorm * 6) % 2) - 1));
|
|
683
|
+
const m = lNorm - chroma / 2;
|
|
684
|
+
let rNorm2 = 0, gNorm2 = 0, bNorm2 = 0;
|
|
685
|
+
if (hNorm < 1 / 6) {
|
|
686
|
+
[rNorm2, gNorm2, bNorm2] = [chroma, x, 0];
|
|
687
|
+
}
|
|
688
|
+
else if (hNorm < 2 / 6) {
|
|
689
|
+
[rNorm2, gNorm2, bNorm2] = [x, chroma, 0];
|
|
690
|
+
}
|
|
691
|
+
else if (hNorm < 3 / 6) {
|
|
692
|
+
[rNorm2, gNorm2, bNorm2] = [0, chroma, x];
|
|
693
|
+
}
|
|
694
|
+
else if (hNorm < 4 / 6) {
|
|
695
|
+
[rNorm2, gNorm2, bNorm2] = [0, x, chroma];
|
|
696
|
+
}
|
|
697
|
+
else if (hNorm < 5 / 6) {
|
|
698
|
+
[rNorm2, gNorm2, bNorm2] = [x, 0, chroma];
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
[rNorm2, gNorm2, bNorm2] = [chroma, 0, x];
|
|
702
|
+
}
|
|
703
|
+
r = Math.round((rNorm2 + m) * 255);
|
|
704
|
+
g = Math.round((gNorm2 + m) * 255);
|
|
705
|
+
b = Math.round((bNorm2 + m) * 255);
|
|
706
|
+
}
|
|
707
|
+
// Adjust contrast
|
|
708
|
+
if (options.contrast !== undefined) {
|
|
709
|
+
const factor = (options.contrast / 100) * 2.55; // Convert to 0-255 range factor
|
|
710
|
+
const avg = (r + g + b) / 3;
|
|
711
|
+
r = Math.round(avg + (r - avg) * (1 + factor));
|
|
712
|
+
g = Math.round(avg + (g - avg) * (1 + factor));
|
|
713
|
+
b = Math.round(avg + (b - avg) * (1 + factor));
|
|
714
|
+
r = Math.max(0, Math.min(255, r));
|
|
715
|
+
g = Math.max(0, Math.min(255, g));
|
|
716
|
+
b = Math.max(0, Math.min(255, b));
|
|
717
|
+
}
|
|
718
|
+
return rgbaToHex(r, g, b, a);
|
|
719
|
+
};
|
|
720
|
+
/**
|
|
721
|
+
* Mixes two colors
|
|
722
|
+
* @param options - Mixing options
|
|
723
|
+
* @returns Mixed color string
|
|
724
|
+
*/
|
|
725
|
+
export const mixColors = (options) => {
|
|
726
|
+
const { color, with: mixColor = 'transparent', percentage = 50, colorSpace = 'srgb', } = options;
|
|
727
|
+
const rgb1 = parseColor(color);
|
|
728
|
+
const rgb2 = parseColor(mixColor);
|
|
729
|
+
if (!rgb1 || !rgb2) {
|
|
730
|
+
return color;
|
|
731
|
+
}
|
|
732
|
+
const factor = percentage / 100;
|
|
733
|
+
let [r1, g1, b1, a1] = rgb1;
|
|
734
|
+
let [r2, g2, b2, a2] = rgb2;
|
|
735
|
+
// Apply opacities if provided
|
|
736
|
+
if (options.opacity !== undefined) {
|
|
737
|
+
a1 =
|
|
738
|
+
typeof options.opacity === 'number'
|
|
739
|
+
? options.opacity
|
|
740
|
+
: parsePercentage(options.opacity);
|
|
741
|
+
}
|
|
742
|
+
if (options.withOpacity !== undefined) {
|
|
743
|
+
a2 =
|
|
744
|
+
typeof options.withOpacity === 'number'
|
|
745
|
+
? options.withOpacity
|
|
746
|
+
: parsePercentage(options.withOpacity);
|
|
747
|
+
}
|
|
748
|
+
// Mix based on color space
|
|
749
|
+
switch (colorSpace) {
|
|
750
|
+
case 'srgb':
|
|
751
|
+
default:
|
|
752
|
+
// Simple linear interpolation in RGB
|
|
753
|
+
const r = Math.round(r1 + (r2 - r1) * factor);
|
|
754
|
+
const g = Math.round(g1 + (g2 - g1) * factor);
|
|
755
|
+
const b = Math.round(b1 + (b2 - b1) * factor);
|
|
756
|
+
const a = a1 + (a2 - a1) * factor;
|
|
757
|
+
return rgbaToHex(r, g, b, a);
|
|
758
|
+
case 'hsl':
|
|
759
|
+
// Mix in HSL space
|
|
760
|
+
const hsl1 = parseColor(rgbaToHsl(r1, g1, b1, a1));
|
|
761
|
+
const hsl2 = parseColor(rgbaToHsl(r2, g2, b2, a2));
|
|
762
|
+
if (!hsl1 || !hsl2) {
|
|
763
|
+
return color;
|
|
764
|
+
}
|
|
765
|
+
// Simplified HSL mixing - in practice would need proper HSL interpolation
|
|
766
|
+
const h = (hsl1[0] + (hsl2[0] - hsl1[0]) * factor) % 360;
|
|
767
|
+
const s = hsl1[1] + (hsl2[1] - hsl1[1]) * factor;
|
|
768
|
+
const l = hsl1[2] + (hsl2[2] - hsl1[2]) * factor;
|
|
769
|
+
return `hsl(${h}, ${s}%, ${l}%)`;
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
/**
|
|
773
|
+
* Generates a color palette from a base color
|
|
774
|
+
* @param baseColor - Base color string
|
|
775
|
+
* @param options - Palette generation options
|
|
776
|
+
* @returns Object with color shades
|
|
777
|
+
*/
|
|
778
|
+
export const generatePalette = (baseColor, options = {}) => {
|
|
779
|
+
const rgb = parseColor(baseColor);
|
|
780
|
+
if (!rgb)
|
|
781
|
+
return {};
|
|
782
|
+
const shades = options.shades || 10;
|
|
783
|
+
const lightnessFactor = options.lightnessFactor || 0.1;
|
|
784
|
+
const saturationFactor = options.saturationFactor || 0.05;
|
|
785
|
+
const palette = {};
|
|
786
|
+
// Generate shades
|
|
787
|
+
for (let i = 0; i < shades; i++) {
|
|
788
|
+
const position = i / (shades - 1); // 0 to 1
|
|
789
|
+
const lightnessAdjustment = (position - 0.5) * 2 * lightnessFactor * 100; // -100 to 100
|
|
790
|
+
const shadeColor = adjustColor(baseColor, {
|
|
791
|
+
lightness: lightnessAdjustment,
|
|
792
|
+
saturation: (position - 0.5) * 2 * saturationFactor * 100,
|
|
793
|
+
});
|
|
794
|
+
const key = i === 0 ? '50' : `${i * 100}`;
|
|
795
|
+
palette[key] = shadeColor;
|
|
796
|
+
}
|
|
797
|
+
// Generate complementary colors if requested
|
|
798
|
+
if (options.includeComplementary) {
|
|
799
|
+
const hsl = parseColor(rgbaToHsl(rgb[0], rgb[1], rgb[2]));
|
|
800
|
+
if (hsl) {
|
|
801
|
+
let h = hsl[0];
|
|
802
|
+
h = (h + 180) % 360;
|
|
803
|
+
palette.complementary = `hsl(${h}, ${hsl[1]}%, ${hsl[2]}%)`;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
// Generate analogous colors (30° apart)
|
|
807
|
+
if (options.includeAnalogous) {
|
|
808
|
+
const hsl = parseColor(rgbaToHsl(rgb[0], rgb[1], rgb[2]));
|
|
809
|
+
if (hsl) {
|
|
810
|
+
let h = hsl[0];
|
|
811
|
+
palette.analogous1 = `hsl(${(h - 30 + 360) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
|
|
812
|
+
palette.analogous2 = `hsl(${(h + 30) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// Generate triadic colors (120° apart)
|
|
816
|
+
if (options.includeTriadic) {
|
|
817
|
+
const hsl = parseColor(rgbaToHsl(rgb[0], rgb[1], rgb[2]));
|
|
818
|
+
if (hsl) {
|
|
819
|
+
let h = hsl[0];
|
|
820
|
+
palette.triadic1 = `hsl(${(h + 120) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
|
|
821
|
+
palette.triadic2 = `hsl(${(h + 240) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return palette;
|
|
825
|
+
};
|
|
826
|
+
/**
|
|
827
|
+
* Creates a contrasting color for accessibility
|
|
828
|
+
* @param color - Base color string
|
|
829
|
+
* @param options - Contrast options
|
|
830
|
+
* @returns Contrasting color string
|
|
831
|
+
*/
|
|
832
|
+
export const createContrastColor = (color, options = {}) => {
|
|
833
|
+
const { lightColor = '#ffffff', darkColor = '#000000', targetRatio = 4.5, fallbackColor = '#000000', } = options;
|
|
834
|
+
const rgb = parseColor(color);
|
|
835
|
+
if (!rgb)
|
|
836
|
+
return fallbackColor;
|
|
837
|
+
// Calculate luminance
|
|
838
|
+
const luminance = calculateLuminance(rgb[0], rgb[1], rgb[2]);
|
|
839
|
+
// Calculate contrast with light and dark colors
|
|
840
|
+
const lightLuminance = calculateLuminance(...parseColor(lightColor).slice(0, 3));
|
|
841
|
+
const darkLuminance = calculateLuminance(...parseColor(darkColor).slice(0, 3));
|
|
842
|
+
const lightContrast = (Math.max(luminance, lightLuminance) + 0.05) /
|
|
843
|
+
(Math.min(luminance, lightLuminance) + 0.05);
|
|
844
|
+
const darkContrast = (Math.max(luminance, darkLuminance) + 0.05) /
|
|
845
|
+
(Math.min(luminance, darkLuminance) + 0.05);
|
|
846
|
+
// Return color with best contrast that meets target ratio
|
|
847
|
+
if (lightContrast >= targetRatio && darkContrast >= targetRatio) {
|
|
848
|
+
return lightContrast > darkContrast ? lightColor : darkColor;
|
|
849
|
+
}
|
|
850
|
+
else if (lightContrast >= targetRatio) {
|
|
851
|
+
return lightColor;
|
|
852
|
+
}
|
|
853
|
+
else if (darkContrast >= targetRatio) {
|
|
854
|
+
return darkColor;
|
|
855
|
+
}
|
|
856
|
+
// If neither meets target, return the one with better contrast
|
|
857
|
+
return lightContrast > darkContrast ? lightColor : darkColor;
|
|
858
|
+
};
|