@proyecto-viviana/solid-stately 0.1.5 → 0.2.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.
Files changed (217) hide show
  1. package/dist/index.js +226 -504
  2. package/dist/index.jsx +6407 -0
  3. package/package.json +3 -5
  4. package/dist/index.js.map +0 -1
  5. package/src/autocomplete/createAutocompleteState.d.ts +0 -46
  6. package/src/autocomplete/createAutocompleteState.d.ts.map +0 -1
  7. package/src/autocomplete/createAutocompleteState.ts +0 -90
  8. package/src/autocomplete/index.d.ts +0 -2
  9. package/src/autocomplete/index.d.ts.map +0 -1
  10. package/src/autocomplete/index.ts +0 -5
  11. package/src/calendar/createCalendarState.d.ts +0 -130
  12. package/src/calendar/createCalendarState.d.ts.map +0 -1
  13. package/src/calendar/createCalendarState.ts +0 -461
  14. package/src/calendar/createDateFieldState.d.ts +0 -110
  15. package/src/calendar/createDateFieldState.d.ts.map +0 -1
  16. package/src/calendar/createDateFieldState.ts +0 -562
  17. package/src/calendar/createRangeCalendarState.d.ts +0 -146
  18. package/src/calendar/createRangeCalendarState.d.ts.map +0 -1
  19. package/src/calendar/createRangeCalendarState.ts +0 -535
  20. package/src/calendar/createTimeFieldState.d.ts +0 -95
  21. package/src/calendar/createTimeFieldState.d.ts.map +0 -1
  22. package/src/calendar/createTimeFieldState.ts +0 -483
  23. package/src/calendar/index.d.ts +0 -7
  24. package/src/calendar/index.d.ts.map +0 -1
  25. package/src/calendar/index.ts +0 -81
  26. package/src/checkbox/createCheckboxGroupState.d.ts +0 -71
  27. package/src/checkbox/createCheckboxGroupState.d.ts.map +0 -1
  28. package/src/checkbox/createCheckboxGroupState.ts +0 -193
  29. package/src/checkbox/index.d.ts +0 -2
  30. package/src/checkbox/index.d.ts.map +0 -1
  31. package/src/checkbox/index.ts +0 -5
  32. package/src/collections/ListCollection.d.ts +0 -37
  33. package/src/collections/ListCollection.d.ts.map +0 -1
  34. package/src/collections/ListCollection.ts +0 -146
  35. package/src/collections/createListState.d.ts +0 -79
  36. package/src/collections/createListState.d.ts.map +0 -1
  37. package/src/collections/createListState.ts +0 -264
  38. package/src/collections/createMenuState.d.ts +0 -50
  39. package/src/collections/createMenuState.d.ts.map +0 -1
  40. package/src/collections/createMenuState.ts +0 -106
  41. package/src/collections/createSelectionState.d.ts +0 -76
  42. package/src/collections/createSelectionState.d.ts.map +0 -1
  43. package/src/collections/createSelectionState.ts +0 -336
  44. package/src/collections/index.d.ts +0 -6
  45. package/src/collections/index.d.ts.map +0 -1
  46. package/src/collections/index.ts +0 -46
  47. package/src/collections/types.d.ts +0 -147
  48. package/src/collections/types.d.ts.map +0 -1
  49. package/src/collections/types.ts +0 -169
  50. package/src/color/Color.d.ts +0 -28
  51. package/src/color/Color.d.ts.map +0 -1
  52. package/src/color/Color.ts +0 -951
  53. package/src/color/createColorAreaState.d.ts +0 -76
  54. package/src/color/createColorAreaState.d.ts.map +0 -1
  55. package/src/color/createColorAreaState.ts +0 -293
  56. package/src/color/createColorFieldState.d.ts +0 -55
  57. package/src/color/createColorFieldState.d.ts.map +0 -1
  58. package/src/color/createColorFieldState.ts +0 -292
  59. package/src/color/createColorSliderState.d.ts +0 -67
  60. package/src/color/createColorSliderState.d.ts.map +0 -1
  61. package/src/color/createColorSliderState.ts +0 -241
  62. package/src/color/createColorWheelState.d.ts +0 -51
  63. package/src/color/createColorWheelState.d.ts.map +0 -1
  64. package/src/color/createColorWheelState.ts +0 -211
  65. package/src/color/index.d.ts +0 -10
  66. package/src/color/index.d.ts.map +0 -1
  67. package/src/color/index.ts +0 -47
  68. package/src/color/types.d.ts +0 -106
  69. package/src/color/types.d.ts.map +0 -1
  70. package/src/color/types.ts +0 -127
  71. package/src/combobox/createComboBoxState.d.ts +0 -125
  72. package/src/combobox/createComboBoxState.d.ts.map +0 -1
  73. package/src/combobox/createComboBoxState.ts +0 -703
  74. package/src/combobox/index.d.ts +0 -5
  75. package/src/combobox/index.d.ts.map +0 -1
  76. package/src/combobox/index.ts +0 -13
  77. package/src/disclosure/createDisclosureState.d.ts +0 -64
  78. package/src/disclosure/createDisclosureState.d.ts.map +0 -1
  79. package/src/disclosure/createDisclosureState.ts +0 -193
  80. package/src/disclosure/index.d.ts +0 -2
  81. package/src/disclosure/index.d.ts.map +0 -1
  82. package/src/disclosure/index.ts +0 -9
  83. package/src/dnd/createDragState.d.ts +0 -59
  84. package/src/dnd/createDragState.d.ts.map +0 -1
  85. package/src/dnd/createDragState.ts +0 -153
  86. package/src/dnd/createDraggableCollectionState.d.ts +0 -57
  87. package/src/dnd/createDraggableCollectionState.d.ts.map +0 -1
  88. package/src/dnd/createDraggableCollectionState.ts +0 -165
  89. package/src/dnd/createDropState.d.ts +0 -61
  90. package/src/dnd/createDropState.d.ts.map +0 -1
  91. package/src/dnd/createDropState.ts +0 -212
  92. package/src/dnd/createDroppableCollectionState.d.ts +0 -78
  93. package/src/dnd/createDroppableCollectionState.d.ts.map +0 -1
  94. package/src/dnd/createDroppableCollectionState.ts +0 -357
  95. package/src/dnd/index.d.ts +0 -11
  96. package/src/dnd/index.d.ts.map +0 -1
  97. package/src/dnd/index.ts +0 -76
  98. package/src/dnd/types.d.ts +0 -264
  99. package/src/dnd/types.d.ts.map +0 -1
  100. package/src/dnd/types.ts +0 -317
  101. package/src/form/createFormValidationState.d.ts +0 -100
  102. package/src/form/createFormValidationState.d.ts.map +0 -1
  103. package/src/form/createFormValidationState.ts +0 -389
  104. package/src/form/index.d.ts +0 -2
  105. package/src/form/index.d.ts.map +0 -1
  106. package/src/form/index.ts +0 -15
  107. package/src/grid/createGridState.d.ts +0 -12
  108. package/src/grid/createGridState.d.ts.map +0 -1
  109. package/src/grid/createGridState.ts +0 -327
  110. package/src/grid/index.d.ts +0 -7
  111. package/src/grid/index.d.ts.map +0 -1
  112. package/src/grid/index.ts +0 -13
  113. package/src/grid/types.d.ts +0 -156
  114. package/src/grid/types.d.ts.map +0 -1
  115. package/src/grid/types.ts +0 -179
  116. package/src/index.d.ts +0 -26
  117. package/src/index.d.ts.map +0 -1
  118. package/src/index.ts +0 -383
  119. package/src/numberfield/createNumberFieldState.d.ts +0 -65
  120. package/src/numberfield/createNumberFieldState.d.ts.map +0 -1
  121. package/src/numberfield/createNumberFieldState.ts +0 -383
  122. package/src/numberfield/index.d.ts +0 -2
  123. package/src/numberfield/index.d.ts.map +0 -1
  124. package/src/numberfield/index.ts +0 -5
  125. package/src/overlays/createOverlayTriggerState.d.ts +0 -32
  126. package/src/overlays/createOverlayTriggerState.d.ts.map +0 -1
  127. package/src/overlays/createOverlayTriggerState.ts +0 -67
  128. package/src/overlays/index.d.ts +0 -2
  129. package/src/overlays/index.d.ts.map +0 -1
  130. package/src/overlays/index.ts +0 -5
  131. package/src/radio/createRadioGroupState.d.ts +0 -77
  132. package/src/radio/createRadioGroupState.d.ts.map +0 -1
  133. package/src/radio/createRadioGroupState.ts +0 -201
  134. package/src/radio/index.d.ts +0 -2
  135. package/src/radio/index.d.ts.map +0 -1
  136. package/src/radio/index.ts +0 -6
  137. package/src/searchfield/createSearchFieldState.d.ts +0 -25
  138. package/src/searchfield/createSearchFieldState.d.ts.map +0 -1
  139. package/src/searchfield/createSearchFieldState.ts +0 -62
  140. package/src/searchfield/index.d.ts +0 -3
  141. package/src/searchfield/index.d.ts.map +0 -1
  142. package/src/searchfield/index.ts +0 -5
  143. package/src/select/createSelectState.d.ts +0 -73
  144. package/src/select/createSelectState.d.ts.map +0 -1
  145. package/src/select/createSelectState.ts +0 -181
  146. package/src/select/index.d.ts +0 -2
  147. package/src/select/index.d.ts.map +0 -1
  148. package/src/select/index.ts +0 -5
  149. package/src/slider/createSliderState.d.ts +0 -72
  150. package/src/slider/createSliderState.d.ts.map +0 -1
  151. package/src/slider/createSliderState.ts +0 -211
  152. package/src/slider/index.d.ts +0 -3
  153. package/src/slider/index.d.ts.map +0 -1
  154. package/src/slider/index.ts +0 -6
  155. package/src/ssr/index.d.ts +0 -28
  156. package/src/ssr/index.d.ts.map +0 -1
  157. package/src/ssr/index.ts +0 -41
  158. package/src/table/TableCollection.d.ts +0 -52
  159. package/src/table/TableCollection.d.ts.map +0 -1
  160. package/src/table/TableCollection.ts +0 -388
  161. package/src/table/createTableState.d.ts +0 -12
  162. package/src/table/createTableState.d.ts.map +0 -1
  163. package/src/table/createTableState.ts +0 -127
  164. package/src/table/index.d.ts +0 -8
  165. package/src/table/index.d.ts.map +0 -1
  166. package/src/table/index.ts +0 -18
  167. package/src/table/types.d.ts +0 -139
  168. package/src/table/types.d.ts.map +0 -1
  169. package/src/table/types.ts +0 -150
  170. package/src/tabs/createTabListState.d.ts +0 -68
  171. package/src/tabs/createTabListState.d.ts.map +0 -1
  172. package/src/tabs/createTabListState.ts +0 -240
  173. package/src/tabs/index.d.ts +0 -2
  174. package/src/tabs/index.d.ts.map +0 -1
  175. package/src/tabs/index.ts +0 -7
  176. package/src/textfield/createTextFieldState.d.ts +0 -30
  177. package/src/textfield/createTextFieldState.d.ts.map +0 -1
  178. package/src/textfield/createTextFieldState.ts +0 -75
  179. package/src/textfield/index.d.ts +0 -2
  180. package/src/textfield/index.d.ts.map +0 -1
  181. package/src/textfield/index.ts +0 -5
  182. package/src/toast/createToastState.d.ts +0 -118
  183. package/src/toast/createToastState.d.ts.map +0 -1
  184. package/src/toast/createToastState.ts +0 -316
  185. package/src/toast/index.d.ts +0 -2
  186. package/src/toast/index.d.ts.map +0 -1
  187. package/src/toast/index.ts +0 -11
  188. package/src/toggle/createToggleState.d.ts +0 -34
  189. package/src/toggle/createToggleState.d.ts.map +0 -1
  190. package/src/toggle/createToggleState.ts +0 -94
  191. package/src/toggle/index.d.ts +0 -2
  192. package/src/toggle/index.d.ts.map +0 -1
  193. package/src/toggle/index.ts +0 -5
  194. package/src/tooltip/createTooltipTriggerState.d.ts +0 -39
  195. package/src/tooltip/createTooltipTriggerState.d.ts.map +0 -1
  196. package/src/tooltip/createTooltipTriggerState.ts +0 -183
  197. package/src/tooltip/index.d.ts +0 -2
  198. package/src/tooltip/index.d.ts.map +0 -1
  199. package/src/tooltip/index.ts +0 -6
  200. package/src/tree/TreeCollection.d.ts +0 -40
  201. package/src/tree/TreeCollection.d.ts.map +0 -1
  202. package/src/tree/TreeCollection.ts +0 -175
  203. package/src/tree/createTreeState.d.ts +0 -14
  204. package/src/tree/createTreeState.d.ts.map +0 -1
  205. package/src/tree/createTreeState.ts +0 -392
  206. package/src/tree/index.d.ts +0 -7
  207. package/src/tree/index.d.ts.map +0 -1
  208. package/src/tree/index.ts +0 -13
  209. package/src/tree/types.d.ts +0 -157
  210. package/src/tree/types.d.ts.map +0 -1
  211. package/src/tree/types.ts +0 -174
  212. package/src/utils/index.d.ts +0 -2
  213. package/src/utils/index.d.ts.map +0 -1
  214. package/src/utils/index.ts +0 -1
  215. package/src/utils/reactivity.d.ts +0 -28
  216. package/src/utils/reactivity.d.ts.map +0 -1
  217. package/src/utils/reactivity.ts +0 -36
@@ -1,951 +0,0 @@
1
- /**
2
- * Color class implementation.
3
- * Based on @react-stately/color.
4
- *
5
- * Provides color manipulation, conversion, and formatting.
6
- */
7
-
8
- import type {
9
- Color,
10
- ColorFormat,
11
- ColorSpace,
12
- ColorChannel,
13
- ColorChannelRange,
14
- ColorAxes,
15
- RGBColor,
16
- HSLColor,
17
- HSBColor,
18
- } from './types';
19
-
20
- // Channel ranges
21
- const RGB_CHANNEL_RANGE: ColorChannelRange = {
22
- minValue: 0,
23
- maxValue: 255,
24
- step: 1,
25
- pageSize: 17,
26
- };
27
-
28
- const ALPHA_CHANNEL_RANGE: ColorChannelRange = {
29
- minValue: 0,
30
- maxValue: 1,
31
- step: 0.01,
32
- pageSize: 0.1,
33
- };
34
-
35
- const HUE_CHANNEL_RANGE: ColorChannelRange = {
36
- minValue: 0,
37
- maxValue: 360,
38
- step: 1,
39
- pageSize: 15,
40
- };
41
-
42
- const PERCENT_CHANNEL_RANGE: ColorChannelRange = {
43
- minValue: 0,
44
- maxValue: 100,
45
- step: 1,
46
- pageSize: 10,
47
- };
48
-
49
- // Channel names (English only for now)
50
- const CHANNEL_NAMES: Record<ColorChannel, string> = {
51
- hue: 'Hue',
52
- saturation: 'Saturation',
53
- brightness: 'Brightness',
54
- lightness: 'Lightness',
55
- red: 'Red',
56
- green: 'Green',
57
- blue: 'Blue',
58
- alpha: 'Alpha',
59
- };
60
-
61
- // Hue names for color naming
62
- const HUE_NAMES: Array<{ max: number; name: string }> = [
63
- { max: 15, name: 'pink' },
64
- { max: 48, name: 'red' },
65
- { max: 94, name: 'orange' },
66
- { max: 135, name: 'yellow' },
67
- { max: 175, name: 'green' },
68
- { max: 264, name: 'cyan' },
69
- { max: 284, name: 'blue' },
70
- { max: 320, name: 'purple' },
71
- { max: 349, name: 'magenta' },
72
- { max: 360, name: 'pink' },
73
- ];
74
-
75
- /**
76
- * Clamp a value to a range.
77
- */
78
- function clamp(value: number, min: number, max: number): number {
79
- return Math.min(Math.max(value, min), max);
80
- }
81
-
82
- /**
83
- * Round to fixed decimal places.
84
- */
85
- function toFixed(value: number, decimals: number): number {
86
- return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
87
- }
88
-
89
- /**
90
- * Convert RGB to HSL.
91
- */
92
- function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number } {
93
- r /= 255;
94
- g /= 255;
95
- b /= 255;
96
-
97
- const max = Math.max(r, g, b);
98
- const min = Math.min(r, g, b);
99
- let h = 0;
100
- let s = 0;
101
- const l = (max + min) / 2;
102
-
103
- if (max !== min) {
104
- const d = max - min;
105
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
106
-
107
- switch (max) {
108
- case r:
109
- h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
110
- break;
111
- case g:
112
- h = ((b - r) / d + 2) / 6;
113
- break;
114
- case b:
115
- h = ((r - g) / d + 4) / 6;
116
- break;
117
- }
118
- }
119
-
120
- return {
121
- h: Math.round(h * 360),
122
- s: Math.round(s * 100),
123
- l: Math.round(l * 100),
124
- };
125
- }
126
-
127
- /**
128
- * Convert HSL to RGB.
129
- */
130
- function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {
131
- h /= 360;
132
- s /= 100;
133
- l /= 100;
134
-
135
- let r: number, g: number, b: number;
136
-
137
- if (s === 0) {
138
- r = g = b = l;
139
- } else {
140
- const hue2rgb = (p: number, q: number, t: number) => {
141
- if (t < 0) t += 1;
142
- if (t > 1) t -= 1;
143
- if (t < 1 / 6) return p + (q - p) * 6 * t;
144
- if (t < 1 / 2) return q;
145
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
146
- return p;
147
- };
148
-
149
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
150
- const p = 2 * l - q;
151
- r = hue2rgb(p, q, h + 1 / 3);
152
- g = hue2rgb(p, q, h);
153
- b = hue2rgb(p, q, h - 1 / 3);
154
- }
155
-
156
- return {
157
- r: Math.round(r * 255),
158
- g: Math.round(g * 255),
159
- b: Math.round(b * 255),
160
- };
161
- }
162
-
163
- /**
164
- * Convert RGB to HSB.
165
- */
166
- function rgbToHsb(r: number, g: number, b: number): { h: number; s: number; b: number } {
167
- r /= 255;
168
- g /= 255;
169
- b /= 255;
170
-
171
- const max = Math.max(r, g, b);
172
- const min = Math.min(r, g, b);
173
- let h = 0;
174
- const v = max;
175
- const d = max - min;
176
- const s = max === 0 ? 0 : d / max;
177
-
178
- if (max !== min) {
179
- switch (max) {
180
- case r:
181
- h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
182
- break;
183
- case g:
184
- h = ((b - r) / d + 2) / 6;
185
- break;
186
- case b:
187
- h = ((r - g) / d + 4) / 6;
188
- break;
189
- }
190
- }
191
-
192
- return {
193
- h: Math.round(h * 360),
194
- s: Math.round(s * 100),
195
- b: Math.round(v * 100),
196
- };
197
- }
198
-
199
- /**
200
- * Convert HSB to RGB.
201
- */
202
- function hsbToRgb(h: number, s: number, b: number): { r: number; g: number; b: number } {
203
- h /= 360;
204
- s /= 100;
205
- b /= 100;
206
-
207
- let r: number, g: number, bl: number;
208
-
209
- const i = Math.floor(h * 6);
210
- const f = h * 6 - i;
211
- const p = b * (1 - s);
212
- const q = b * (1 - f * s);
213
- const t = b * (1 - (1 - f) * s);
214
-
215
- switch (i % 6) {
216
- case 0:
217
- r = b;
218
- g = t;
219
- bl = p;
220
- break;
221
- case 1:
222
- r = q;
223
- g = b;
224
- bl = p;
225
- break;
226
- case 2:
227
- r = p;
228
- g = b;
229
- bl = t;
230
- break;
231
- case 3:
232
- r = p;
233
- g = q;
234
- bl = b;
235
- break;
236
- case 4:
237
- r = t;
238
- g = p;
239
- bl = b;
240
- break;
241
- default:
242
- r = b;
243
- g = p;
244
- bl = q;
245
- break;
246
- }
247
-
248
- return {
249
- r: Math.round(r * 255),
250
- g: Math.round(g * 255),
251
- b: Math.round(bl * 255),
252
- };
253
- }
254
-
255
- /**
256
- * Get hue name from hue angle.
257
- */
258
- function getHueNameFromAngle(hue: number): string {
259
- for (const { max, name } of HUE_NAMES) {
260
- if (hue <= max) {
261
- return name;
262
- }
263
- }
264
- return 'pink';
265
- }
266
-
267
- /**
268
- * Get a human-readable color name based on RGB values.
269
- */
270
- function getColorNameFromRgb(r: number, g: number, b: number, alpha: number): string {
271
- // Convert to HSL for analysis
272
- const { h, s, l } = rgbToHsl(r, g, b);
273
-
274
- // Handle edge cases
275
- if (l >= 100) {
276
- return alpha < 1 ? `white ${Math.round(alpha * 100)}% transparent` : 'white';
277
- }
278
- if (l <= 0) {
279
- return alpha < 1 ? `black ${Math.round(alpha * 100)}% transparent` : 'black';
280
- }
281
-
282
- // Build color name
283
- const parts: string[] = [];
284
-
285
- // Lightness descriptor
286
- if (l < 30) {
287
- parts.push('very dark');
288
- } else if (l < 55) {
289
- parts.push('dark');
290
- } else if (l > 85) {
291
- parts.push('very light');
292
- } else if (l > 70) {
293
- parts.push('light');
294
- }
295
-
296
- // Saturation/chroma descriptor
297
- if (s < 10) {
298
- parts.push('gray');
299
- } else if (s < 30) {
300
- parts.push('grayish');
301
- } else if (s > 80) {
302
- parts.push('vibrant');
303
- }
304
-
305
- // Hue name (skip if gray)
306
- if (s >= 10) {
307
- let hueName = getHueNameFromAngle(h);
308
-
309
- // Special cases
310
- if (hueName === 'orange' && l < 68) {
311
- hueName = 'brown';
312
- }
313
- if (hueName === 'yellow' && l < 85 && s > 50) {
314
- hueName = 'yellow green';
315
- }
316
-
317
- parts.push(hueName);
318
- }
319
-
320
- let name = parts.join(' ');
321
-
322
- // Add transparency
323
- if (alpha < 1) {
324
- name += ` ${Math.round(alpha * 100)}% transparent`;
325
- }
326
-
327
- return name || 'color';
328
- }
329
-
330
- /**
331
- * RGB Color implementation.
332
- */
333
- class RGBColorImpl implements Color {
334
- private red: number;
335
- private green: number;
336
- private blue: number;
337
- private alpha: number;
338
-
339
- constructor(red: number, green: number, blue: number, alpha: number = 1) {
340
- this.red = clamp(Math.round(red), 0, 255);
341
- this.green = clamp(Math.round(green), 0, 255);
342
- this.blue = clamp(Math.round(blue), 0, 255);
343
- this.alpha = clamp(toFixed(alpha, 2), 0, 1);
344
- }
345
-
346
- toFormat(format: ColorFormat): Color {
347
- switch (format) {
348
- case 'hex':
349
- case 'hexa':
350
- case 'rgb':
351
- case 'rgba':
352
- return this.clone();
353
- case 'hsl':
354
- case 'hsla': {
355
- const { h, s, l } = rgbToHsl(this.red, this.green, this.blue);
356
- return new HSLColorImpl(h, s, l, this.alpha);
357
- }
358
- case 'hsb':
359
- case 'hsba': {
360
- const { h, s, b } = rgbToHsb(this.red, this.green, this.blue);
361
- return new HSBColorImpl(h, s, b, this.alpha);
362
- }
363
- default:
364
- throw new Error(`Unsupported format: ${format}`);
365
- }
366
- }
367
-
368
- toString(format?: ColorFormat | 'css'): string {
369
- const f = format ?? 'css';
370
-
371
- switch (f) {
372
- case 'hex':
373
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue.toString(16).padStart(2, '0')}`;
374
- case 'hexa':
375
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue.toString(16).padStart(2, '0')}${Math.round(this.alpha * 255).toString(16).padStart(2, '0')}`;
376
- case 'rgb':
377
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
378
- case 'rgba':
379
- case 'css':
380
- return this.alpha === 1
381
- ? `rgb(${this.red}, ${this.green}, ${this.blue})`
382
- : `rgba(${this.red}, ${this.green}, ${this.blue}, ${this.alpha})`;
383
- default:
384
- return this.toFormat(f as ColorFormat).toString(f);
385
- }
386
- }
387
-
388
- clone(): Color {
389
- return new RGBColorImpl(this.red, this.green, this.blue, this.alpha);
390
- }
391
-
392
- toHexInt(): number {
393
- return (this.red << 16) | (this.green << 8) | this.blue;
394
- }
395
-
396
- getChannelValue(channel: ColorChannel): number {
397
- switch (channel) {
398
- case 'red':
399
- return this.red;
400
- case 'green':
401
- return this.green;
402
- case 'blue':
403
- return this.blue;
404
- case 'alpha':
405
- return this.alpha;
406
- // Cross-color-space channels - convert to HSB
407
- case 'hue':
408
- case 'saturation':
409
- case 'brightness':
410
- return this.toFormat('hsb').getChannelValue(channel);
411
- case 'lightness':
412
- return this.toFormat('hsl').getChannelValue(channel);
413
- default:
414
- throw new Error(`Invalid channel: ${channel}`);
415
- }
416
- }
417
-
418
- withChannelValue(channel: ColorChannel, value: number): Color {
419
- switch (channel) {
420
- case 'red':
421
- return new RGBColorImpl(value, this.green, this.blue, this.alpha);
422
- case 'green':
423
- return new RGBColorImpl(this.red, value, this.blue, this.alpha);
424
- case 'blue':
425
- return new RGBColorImpl(this.red, this.green, value, this.alpha);
426
- case 'alpha':
427
- return new RGBColorImpl(this.red, this.green, this.blue, value);
428
- // Cross-color-space channels - convert, update, convert back
429
- case 'hue':
430
- case 'saturation':
431
- case 'brightness':
432
- return this.toFormat('hsb').withChannelValue(channel, value).toFormat('rgb');
433
- case 'lightness':
434
- return this.toFormat('hsl').withChannelValue(channel, value).toFormat('rgb');
435
- default:
436
- throw new Error(`Invalid channel: ${channel}`);
437
- }
438
- }
439
-
440
- getChannelRange(channel: ColorChannel): ColorChannelRange {
441
- switch (channel) {
442
- case 'red':
443
- case 'green':
444
- case 'blue':
445
- return RGB_CHANNEL_RANGE;
446
- case 'alpha':
447
- return ALPHA_CHANNEL_RANGE;
448
- case 'hue':
449
- return HUE_CHANNEL_RANGE;
450
- case 'saturation':
451
- case 'brightness':
452
- case 'lightness':
453
- return PERCENT_CHANNEL_RANGE;
454
- default:
455
- throw new Error(`Invalid channel: ${channel}`);
456
- }
457
- }
458
-
459
- getChannelName(channel: ColorChannel, _locale: string): string {
460
- return CHANNEL_NAMES[channel] || channel;
461
- }
462
-
463
- getChannelFormatOptions(channel: ColorChannel): Intl.NumberFormatOptions {
464
- if (channel === 'alpha') {
465
- return { style: 'percent' };
466
- }
467
- return { maximumFractionDigits: 0 };
468
- }
469
-
470
- formatChannelValue(channel: ColorChannel, locale: string): string {
471
- const value = this.getChannelValue(channel);
472
- const options = this.getChannelFormatOptions(channel);
473
- return new Intl.NumberFormat(locale, options).format(value);
474
- }
475
-
476
- getColorSpace(): ColorSpace {
477
- return 'rgb';
478
- }
479
-
480
- getColorSpaceAxes(xyChannels?: { xChannel?: ColorChannel; yChannel?: ColorChannel }): ColorAxes {
481
- const xChannel = xyChannels?.xChannel ?? 'red';
482
- const yChannel = xyChannels?.yChannel ?? 'green';
483
- const channels: ColorChannel[] = ['red', 'green', 'blue'];
484
- const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ?? 'blue';
485
- return { xChannel, yChannel, zChannel };
486
- }
487
-
488
- getColorChannels(): [ColorChannel, ColorChannel, ColorChannel] {
489
- return ['red', 'green', 'blue'];
490
- }
491
-
492
- getColorName(locale: string): string {
493
- return getColorNameFromRgb(this.red, this.green, this.blue, this.alpha);
494
- }
495
-
496
- getHueName(_locale: string): string {
497
- const { h } = rgbToHsl(this.red, this.green, this.blue);
498
- return getHueNameFromAngle(h);
499
- }
500
- }
501
-
502
- /**
503
- * HSL Color implementation.
504
- */
505
- class HSLColorImpl implements Color {
506
- private hue: number;
507
- private saturation: number;
508
- private lightness: number;
509
- private alpha: number;
510
-
511
- constructor(hue: number, saturation: number, lightness: number, alpha: number = 1) {
512
- this.hue = clamp(Math.round(hue) % 360, 0, 360);
513
- this.saturation = clamp(Math.round(saturation), 0, 100);
514
- this.lightness = clamp(Math.round(lightness), 0, 100);
515
- this.alpha = clamp(toFixed(alpha, 2), 0, 1);
516
- }
517
-
518
- toFormat(format: ColorFormat): Color {
519
- switch (format) {
520
- case 'hsl':
521
- case 'hsla':
522
- return this.clone();
523
- case 'hex':
524
- case 'hexa':
525
- case 'rgb':
526
- case 'rgba': {
527
- const { r, g, b } = hslToRgb(this.hue, this.saturation, this.lightness);
528
- return new RGBColorImpl(r, g, b, this.alpha);
529
- }
530
- case 'hsb':
531
- case 'hsba': {
532
- const { r, g, b } = hslToRgb(this.hue, this.saturation, this.lightness);
533
- const hsb = rgbToHsb(r, g, b);
534
- return new HSBColorImpl(hsb.h, hsb.s, hsb.b, this.alpha);
535
- }
536
- default:
537
- throw new Error(`Unsupported format: ${format}`);
538
- }
539
- }
540
-
541
- toString(format?: ColorFormat | 'css'): string {
542
- const f = format ?? 'css';
543
-
544
- switch (f) {
545
- case 'hsl':
546
- return `hsl(${this.hue}, ${this.saturation}%, ${this.lightness}%)`;
547
- case 'hsla':
548
- case 'css':
549
- return this.alpha === 1
550
- ? `hsl(${this.hue}, ${this.saturation}%, ${this.lightness}%)`
551
- : `hsla(${this.hue}, ${this.saturation}%, ${this.lightness}%, ${this.alpha})`;
552
- default:
553
- return this.toFormat(f as ColorFormat).toString(f);
554
- }
555
- }
556
-
557
- clone(): Color {
558
- return new HSLColorImpl(this.hue, this.saturation, this.lightness, this.alpha);
559
- }
560
-
561
- toHexInt(): number {
562
- return this.toFormat('rgb').toHexInt();
563
- }
564
-
565
- getChannelValue(channel: ColorChannel): number {
566
- switch (channel) {
567
- case 'hue':
568
- return this.hue;
569
- case 'saturation':
570
- return this.saturation;
571
- case 'lightness':
572
- return this.lightness;
573
- case 'alpha':
574
- return this.alpha;
575
- // Cross-color-space channels
576
- case 'red':
577
- case 'green':
578
- case 'blue':
579
- return this.toFormat('rgb').getChannelValue(channel);
580
- case 'brightness':
581
- return this.toFormat('hsb').getChannelValue(channel);
582
- default:
583
- throw new Error(`Invalid channel: ${channel}`);
584
- }
585
- }
586
-
587
- withChannelValue(channel: ColorChannel, value: number): Color {
588
- switch (channel) {
589
- case 'hue':
590
- return new HSLColorImpl(value, this.saturation, this.lightness, this.alpha);
591
- case 'saturation':
592
- return new HSLColorImpl(this.hue, value, this.lightness, this.alpha);
593
- case 'lightness':
594
- return new HSLColorImpl(this.hue, this.saturation, value, this.alpha);
595
- case 'alpha':
596
- return new HSLColorImpl(this.hue, this.saturation, this.lightness, value);
597
- // Cross-color-space channels
598
- case 'red':
599
- case 'green':
600
- case 'blue':
601
- return this.toFormat('rgb').withChannelValue(channel, value).toFormat('hsl');
602
- case 'brightness':
603
- return this.toFormat('hsb').withChannelValue(channel, value).toFormat('hsl');
604
- default:
605
- throw new Error(`Invalid channel: ${channel}`);
606
- }
607
- }
608
-
609
- getChannelRange(channel: ColorChannel): ColorChannelRange {
610
- switch (channel) {
611
- case 'hue':
612
- return HUE_CHANNEL_RANGE;
613
- case 'saturation':
614
- case 'lightness':
615
- case 'brightness':
616
- return PERCENT_CHANNEL_RANGE;
617
- case 'alpha':
618
- return ALPHA_CHANNEL_RANGE;
619
- case 'red':
620
- case 'green':
621
- case 'blue':
622
- return RGB_CHANNEL_RANGE;
623
- default:
624
- throw new Error(`Invalid channel: ${channel}`);
625
- }
626
- }
627
-
628
- getChannelName(channel: ColorChannel, _locale: string): string {
629
- return CHANNEL_NAMES[channel] || channel;
630
- }
631
-
632
- getChannelFormatOptions(channel: ColorChannel): Intl.NumberFormatOptions {
633
- if (channel === 'alpha') {
634
- return { style: 'percent' };
635
- }
636
- if (channel === 'hue') {
637
- return { maximumFractionDigits: 0 };
638
- }
639
- return { style: 'percent', maximumFractionDigits: 0 };
640
- }
641
-
642
- formatChannelValue(channel: ColorChannel, locale: string): string {
643
- const value = this.getChannelValue(channel);
644
- const options = this.getChannelFormatOptions(channel);
645
- if (channel === 'saturation' || channel === 'lightness') {
646
- return new Intl.NumberFormat(locale, options).format(value / 100);
647
- }
648
- return new Intl.NumberFormat(locale, options).format(value);
649
- }
650
-
651
- getColorSpace(): ColorSpace {
652
- return 'hsl';
653
- }
654
-
655
- getColorSpaceAxes(xyChannels?: { xChannel?: ColorChannel; yChannel?: ColorChannel }): ColorAxes {
656
- const xChannel = xyChannels?.xChannel ?? 'saturation';
657
- const yChannel = xyChannels?.yChannel ?? 'lightness';
658
- const channels: ColorChannel[] = ['hue', 'saturation', 'lightness'];
659
- const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ?? 'hue';
660
- return { xChannel, yChannel, zChannel };
661
- }
662
-
663
- getColorChannels(): [ColorChannel, ColorChannel, ColorChannel] {
664
- return ['hue', 'saturation', 'lightness'];
665
- }
666
-
667
- getColorName(_locale: string): string {
668
- const { r, g, b } = hslToRgb(this.hue, this.saturation, this.lightness);
669
- return getColorNameFromRgb(r, g, b, this.alpha);
670
- }
671
-
672
- getHueName(_locale: string): string {
673
- return getHueNameFromAngle(this.hue);
674
- }
675
- }
676
-
677
- /**
678
- * HSB Color implementation.
679
- */
680
- class HSBColorImpl implements Color {
681
- private hue: number;
682
- private saturation: number;
683
- private brightness: number;
684
- private alpha: number;
685
-
686
- constructor(hue: number, saturation: number, brightness: number, alpha: number = 1) {
687
- this.hue = clamp(Math.round(hue) % 360, 0, 360);
688
- this.saturation = clamp(Math.round(saturation), 0, 100);
689
- this.brightness = clamp(Math.round(brightness), 0, 100);
690
- this.alpha = clamp(toFixed(alpha, 2), 0, 1);
691
- }
692
-
693
- toFormat(format: ColorFormat): Color {
694
- switch (format) {
695
- case 'hsb':
696
- case 'hsba':
697
- return this.clone();
698
- case 'hex':
699
- case 'hexa':
700
- case 'rgb':
701
- case 'rgba': {
702
- const { r, g, b } = hsbToRgb(this.hue, this.saturation, this.brightness);
703
- return new RGBColorImpl(r, g, b, this.alpha);
704
- }
705
- case 'hsl':
706
- case 'hsla': {
707
- const { r, g, b } = hsbToRgb(this.hue, this.saturation, this.brightness);
708
- const hsl = rgbToHsl(r, g, b);
709
- return new HSLColorImpl(hsl.h, hsl.s, hsl.l, this.alpha);
710
- }
711
- default:
712
- throw new Error(`Unsupported format: ${format}`);
713
- }
714
- }
715
-
716
- toString(format?: ColorFormat | 'css'): string {
717
- const f = format ?? 'css';
718
-
719
- switch (f) {
720
- case 'hsb':
721
- return `hsb(${this.hue}, ${this.saturation}%, ${this.brightness}%)`;
722
- case 'hsba':
723
- case 'css':
724
- // HSB is not a standard CSS format, convert to RGB
725
- return this.toFormat('rgba').toString('css');
726
- default:
727
- return this.toFormat(f as ColorFormat).toString(f);
728
- }
729
- }
730
-
731
- clone(): Color {
732
- return new HSBColorImpl(this.hue, this.saturation, this.brightness, this.alpha);
733
- }
734
-
735
- toHexInt(): number {
736
- return this.toFormat('rgb').toHexInt();
737
- }
738
-
739
- getChannelValue(channel: ColorChannel): number {
740
- switch (channel) {
741
- case 'hue':
742
- return this.hue;
743
- case 'saturation':
744
- return this.saturation;
745
- case 'brightness':
746
- return this.brightness;
747
- case 'alpha':
748
- return this.alpha;
749
- // Cross-color-space channels
750
- case 'red':
751
- case 'green':
752
- case 'blue':
753
- return this.toFormat('rgb').getChannelValue(channel);
754
- case 'lightness':
755
- return this.toFormat('hsl').getChannelValue(channel);
756
- default:
757
- throw new Error(`Invalid channel: ${channel}`);
758
- }
759
- }
760
-
761
- withChannelValue(channel: ColorChannel, value: number): Color {
762
- switch (channel) {
763
- case 'hue':
764
- return new HSBColorImpl(value, this.saturation, this.brightness, this.alpha);
765
- case 'saturation':
766
- return new HSBColorImpl(this.hue, value, this.brightness, this.alpha);
767
- case 'brightness':
768
- return new HSBColorImpl(this.hue, this.saturation, value, this.alpha);
769
- case 'alpha':
770
- return new HSBColorImpl(this.hue, this.saturation, this.brightness, value);
771
- // Cross-color-space channels
772
- case 'red':
773
- case 'green':
774
- case 'blue':
775
- return this.toFormat('rgb').withChannelValue(channel, value).toFormat('hsb');
776
- case 'lightness':
777
- return this.toFormat('hsl').withChannelValue(channel, value).toFormat('hsb');
778
- default:
779
- throw new Error(`Invalid channel: ${channel}`);
780
- }
781
- }
782
-
783
- getChannelRange(channel: ColorChannel): ColorChannelRange {
784
- switch (channel) {
785
- case 'hue':
786
- return HUE_CHANNEL_RANGE;
787
- case 'saturation':
788
- case 'brightness':
789
- case 'lightness':
790
- return PERCENT_CHANNEL_RANGE;
791
- case 'alpha':
792
- return ALPHA_CHANNEL_RANGE;
793
- case 'red':
794
- case 'green':
795
- case 'blue':
796
- return RGB_CHANNEL_RANGE;
797
- default:
798
- throw new Error(`Invalid channel: ${channel}`);
799
- }
800
- }
801
-
802
- getChannelName(channel: ColorChannel, _locale: string): string {
803
- return CHANNEL_NAMES[channel] || channel;
804
- }
805
-
806
- getChannelFormatOptions(channel: ColorChannel): Intl.NumberFormatOptions {
807
- if (channel === 'alpha') {
808
- return { style: 'percent' };
809
- }
810
- if (channel === 'hue') {
811
- return { maximumFractionDigits: 0 };
812
- }
813
- return { style: 'percent', maximumFractionDigits: 0 };
814
- }
815
-
816
- formatChannelValue(channel: ColorChannel, locale: string): string {
817
- const value = this.getChannelValue(channel);
818
- const options = this.getChannelFormatOptions(channel);
819
- if (channel === 'saturation' || channel === 'brightness') {
820
- return new Intl.NumberFormat(locale, options).format(value / 100);
821
- }
822
- return new Intl.NumberFormat(locale, options).format(value);
823
- }
824
-
825
- getColorSpace(): ColorSpace {
826
- return 'hsb';
827
- }
828
-
829
- getColorSpaceAxes(xyChannels?: { xChannel?: ColorChannel; yChannel?: ColorChannel }): ColorAxes {
830
- const xChannel = xyChannels?.xChannel ?? 'saturation';
831
- const yChannel = xyChannels?.yChannel ?? 'brightness';
832
- const channels: ColorChannel[] = ['hue', 'saturation', 'brightness'];
833
- const zChannel = channels.find((c) => c !== xChannel && c !== yChannel) ?? 'hue';
834
- return { xChannel, yChannel, zChannel };
835
- }
836
-
837
- getColorChannels(): [ColorChannel, ColorChannel, ColorChannel] {
838
- return ['hue', 'saturation', 'brightness'];
839
- }
840
-
841
- getColorName(_locale: string): string {
842
- const { r, g, b } = hsbToRgb(this.hue, this.saturation, this.brightness);
843
- return getColorNameFromRgb(r, g, b, this.alpha);
844
- }
845
-
846
- getHueName(_locale: string): string {
847
- return getHueNameFromAngle(this.hue);
848
- }
849
- }
850
-
851
- /**
852
- * Parse a color string into a Color object.
853
- */
854
- export function parseColor(value: string): Color {
855
- const trimmed = value.trim().toLowerCase();
856
-
857
- // Hex format
858
- if (trimmed.startsWith('#')) {
859
- const hex = trimmed.slice(1);
860
- if (hex.length === 3) {
861
- const r = parseInt(hex[0] + hex[0], 16);
862
- const g = parseInt(hex[1] + hex[1], 16);
863
- const b = parseInt(hex[2] + hex[2], 16);
864
- return new RGBColorImpl(r, g, b);
865
- }
866
- if (hex.length === 4) {
867
- const r = parseInt(hex[0] + hex[0], 16);
868
- const g = parseInt(hex[1] + hex[1], 16);
869
- const b = parseInt(hex[2] + hex[2], 16);
870
- const a = parseInt(hex[3] + hex[3], 16) / 255;
871
- return new RGBColorImpl(r, g, b, a);
872
- }
873
- if (hex.length === 6) {
874
- const r = parseInt(hex.slice(0, 2), 16);
875
- const g = parseInt(hex.slice(2, 4), 16);
876
- const b = parseInt(hex.slice(4, 6), 16);
877
- return new RGBColorImpl(r, g, b);
878
- }
879
- if (hex.length === 8) {
880
- const r = parseInt(hex.slice(0, 2), 16);
881
- const g = parseInt(hex.slice(2, 4), 16);
882
- const b = parseInt(hex.slice(4, 6), 16);
883
- const a = parseInt(hex.slice(6, 8), 16) / 255;
884
- return new RGBColorImpl(r, g, b, a);
885
- }
886
- throw new Error(`Invalid hex color: ${value}`);
887
- }
888
-
889
- // RGB/RGBA format
890
- const rgbMatch = trimmed.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/);
891
- if (rgbMatch) {
892
- const r = parseInt(rgbMatch[1], 10);
893
- const g = parseInt(rgbMatch[2], 10);
894
- const b = parseInt(rgbMatch[3], 10);
895
- const a = rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : 1;
896
- return new RGBColorImpl(r, g, b, a);
897
- }
898
-
899
- // HSL/HSLA format
900
- const hslMatch = trimmed.match(/^hsla?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(?:,\s*([\d.]+))?\s*\)$/);
901
- if (hslMatch) {
902
- const h = parseInt(hslMatch[1], 10);
903
- const s = parseInt(hslMatch[2], 10);
904
- const l = parseInt(hslMatch[3], 10);
905
- const a = hslMatch[4] !== undefined ? parseFloat(hslMatch[4]) : 1;
906
- return new HSLColorImpl(h, s, l, a);
907
- }
908
-
909
- // HSB/HSBA format
910
- const hsbMatch = trimmed.match(/^hsba?\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(?:,\s*([\d.]+))?\s*\)$/);
911
- if (hsbMatch) {
912
- const h = parseInt(hsbMatch[1], 10);
913
- const s = parseInt(hsbMatch[2], 10);
914
- const b = parseInt(hsbMatch[3], 10);
915
- const a = hsbMatch[4] !== undefined ? parseFloat(hsbMatch[4]) : 1;
916
- return new HSBColorImpl(h, s, b, a);
917
- }
918
-
919
- throw new Error(`Invalid color format: ${value}`);
920
- }
921
-
922
- /**
923
- * Create an RGB color.
924
- */
925
- export function createRGBColor(red: number, green: number, blue: number, alpha: number = 1): Color {
926
- return new RGBColorImpl(red, green, blue, alpha);
927
- }
928
-
929
- /**
930
- * Create an HSL color.
931
- */
932
- export function createHSLColor(hue: number, saturation: number, lightness: number, alpha: number = 1): Color {
933
- return new HSLColorImpl(hue, saturation, lightness, alpha);
934
- }
935
-
936
- /**
937
- * Create an HSB color.
938
- */
939
- export function createHSBColor(hue: number, saturation: number, brightness: number, alpha: number = 1): Color {
940
- return new HSBColorImpl(hue, saturation, brightness, alpha);
941
- }
942
-
943
- /**
944
- * Normalize a color value (string or Color) to a Color object.
945
- */
946
- export function normalizeColor(value: string | Color): Color {
947
- if (typeof value === 'string') {
948
- return parseColor(value);
949
- }
950
- return value;
951
- }