gdp-color-picker 1.0.0

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.
@@ -0,0 +1,216 @@
1
+
2
+ /**
3
+ * Clamps a value between min and max
4
+ * @param {number} value
5
+ * @param {number} min
6
+ * @param {number} max
7
+ */
8
+ export function clamp(value, min, max) {
9
+ return Math.min(Math.max(value, min), max);
10
+ }
11
+
12
+ /**
13
+ * Converts HSV to RGB
14
+ * @param {number} h Hue 0-360
15
+ * @param {number} s Saturation 0-1
16
+ * @param {number} v Value 0-1
17
+ * @returns {{r: number, g: number, b: number}}
18
+ */
19
+ export function hsvToRgb(h, s, v) {
20
+ h = ((h % 360) + 360) % 360; // Normalize hue
21
+ let r, g, b;
22
+ let i = Math.floor(h / 60);
23
+ let f = h / 60 - i;
24
+ let p = v * (1 - s);
25
+ let q = v * (1 - f * s);
26
+ let t = v * (1 - (1 - f) * s);
27
+
28
+ switch (i % 6) {
29
+ case 0: r = v; g = t; b = p; break;
30
+ case 1: r = q; g = v; b = p; break;
31
+ case 2: r = p; g = v; b = t; break;
32
+ case 3: r = p; g = q; b = v; break;
33
+ case 4: r = t; g = p; b = v; break;
34
+ case 5: r = v; g = p; b = q; break;
35
+ default: r = 0; g = 0; b = 0;
36
+ }
37
+
38
+ return {
39
+ r: Math.round(r * 255),
40
+ g: Math.round(g * 255),
41
+ b: Math.round(b * 255)
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Converts RGB to HSV
47
+ * @param {number} r Red 0-255
48
+ * @param {number} g Green 0-255
49
+ * @param {number} b Blue 0-255
50
+ * @returns {{h: number, s: number, v: number}}
51
+ */
52
+ export function rgbToHsv(r, g, b) {
53
+ r /= 255;
54
+ g /= 255;
55
+ b /= 255;
56
+
57
+ let max = Math.max(r, g, b);
58
+ let min = Math.min(r, g, b);
59
+ let h, s, v = max;
60
+
61
+ let d = max - min;
62
+ s = max === 0 ? 0 : d / max;
63
+
64
+ if (max === min) {
65
+ h = 0; // achromatic
66
+ } else {
67
+ switch (max) {
68
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
69
+ case g: h = (b - r) / d + 2; break;
70
+ case b: h = (r - g) / d + 4; break;
71
+ default: h = 0;
72
+ }
73
+ h /= 6;
74
+ }
75
+
76
+ return {
77
+ h: h * 360,
78
+ s: s,
79
+ v: v
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Converts RGB to Hex string
85
+ * @param {number} r
86
+ * @param {number} g
87
+ * @param {number} b
88
+ * @returns {string} Hex string (e.g., "#ff0000")
89
+ */
90
+ export function rgbToHex(r, g, b) {
91
+ const toHex = (c) => {
92
+ const hex = Math.round(c).toString(16);
93
+ return hex.length === 1 ? "0" + hex : hex;
94
+ };
95
+ return "#" + toHex(r) + toHex(g) + toHex(b);
96
+ }
97
+
98
+ /**
99
+ * Converts Hex string to RGB
100
+ * @param {string} hex
101
+ * @returns {{r: number, g: number, b: number} | null}
102
+ */
103
+ export function hexToRgb(hex) {
104
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
105
+ const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
106
+ hex = hex.replace(shorthandRegex, function(m, r, g, b) {
107
+ return r + r + g + g + b + b;
108
+ });
109
+
110
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
111
+ return result ? {
112
+ r: parseInt(result[1], 16),
113
+ g: parseInt(result[2], 16),
114
+ b: parseInt(result[3], 16)
115
+ } : null;
116
+ }
117
+
118
+ /**
119
+ * Parses a color string to HSV
120
+ * @param {string} color
121
+ * @returns {{h: number, s: number, v: number, a: number}}
122
+ */
123
+ export function parseColor(color) {
124
+ if (!color) return { h: 0, s: 1, v: 1, a: 1 };
125
+
126
+ // Hex
127
+ if (color.startsWith('#')) {
128
+ const rgb = hexToRgb(color);
129
+ if (rgb) {
130
+ const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
131
+ return { ...hsv, a: 1 };
132
+ }
133
+ }
134
+
135
+ // RGB/RGBA
136
+ const rgbaMatch = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)$/);
137
+ if (rgbaMatch) {
138
+ const r = parseInt(rgbaMatch[1], 10);
139
+ const g = parseInt(rgbaMatch[2], 10);
140
+ const b = parseInt(rgbaMatch[3], 10);
141
+ const a = rgbaMatch[4] !== undefined ? parseFloat(rgbaMatch[4]) : 1;
142
+ const hsv = rgbToHsv(r, g, b);
143
+ return { ...hsv, a };
144
+ }
145
+
146
+ // Default fallback
147
+ return { h: 0, s: 1, v: 1, a: 1 };
148
+ }
149
+
150
+ /**
151
+ * Converts RGB to HSL
152
+ * @param {number} r Red 0-255
153
+ * @param {number} g Green 0-255
154
+ * @param {number} b Blue 0-255
155
+ * @returns {{h: number, s: number, l: number}}
156
+ */
157
+ export function rgbToHsl(r, g, b) {
158
+ r /= 255;
159
+ g /= 255;
160
+ b /= 255;
161
+
162
+ let max = Math.max(r, g, b), min = Math.min(r, g, b);
163
+ let h, s, l = (max + min) / 2;
164
+
165
+ if (max === min) {
166
+ h = s = 0; // achromatic
167
+ } else {
168
+ let d = max - min;
169
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
170
+ switch (max) {
171
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
172
+ case g: h = (b - r) / d + 2; break;
173
+ case b: h = (r - g) / d + 4; break;
174
+ }
175
+ h /= 6;
176
+ }
177
+
178
+ return { h: h * 360, s: s, l: l };
179
+ }
180
+
181
+ /**
182
+ * Converts HSL to RGB
183
+ * @param {number} h Hue 0-360
184
+ * @param {number} s Saturation 0-1
185
+ * @param {number} l Lightness 0-1
186
+ * @returns {{r: number, g: number, b: number}}
187
+ */
188
+ export function hslToRgb(h, s, l) {
189
+ let r, g, b;
190
+
191
+ if (s === 0) {
192
+ r = g = b = l; // achromatic
193
+ } else {
194
+ const hue2rgb = (p, q, t) => {
195
+ if (t < 0) t += 1;
196
+ if (t > 1) t -= 1;
197
+ if (t < 1/6) return p + (q - p) * 6 * t;
198
+ if (t < 1/2) return q;
199
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
200
+ return p;
201
+ };
202
+
203
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
204
+ const p = 2 * l - q;
205
+ h /= 360;
206
+ r = hue2rgb(p, q, h + 1/3);
207
+ g = hue2rgb(p, q, h);
208
+ b = hue2rgb(p, q, h - 1/3);
209
+ }
210
+
211
+ return {
212
+ r: Math.round(r * 255),
213
+ g: Math.round(g * 255),
214
+ b: Math.round(b * 255)
215
+ };
216
+ }