xiod-ui 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.
- package/LICENSE +73 -0
- package/README.md +167 -0
- package/dist/components/accordion.d.ts +9 -0
- package/dist/components/accordion.js +46 -0
- package/dist/components/agent-steps.d.ts +47 -0
- package/dist/components/agent-steps.js +223 -0
- package/dist/components/alert-dialog.d.ts +21 -0
- package/dist/components/alert-dialog.js +74 -0
- package/dist/components/alert.d.ts +12 -0
- package/dist/components/alert.js +45 -0
- package/dist/components/aspect-ratio.d.ts +9 -0
- package/dist/components/aspect-ratio.js +22 -0
- package/dist/components/autocomplete.d.ts +35 -0
- package/dist/components/autocomplete.js +160 -0
- package/dist/components/avatar.d.ts +17 -0
- package/dist/components/avatar.js +65 -0
- package/dist/components/badge.d.ts +15 -0
- package/dist/components/badge.js +46 -0
- package/dist/components/breadcrumb.d.ts +12 -0
- package/dist/components/breadcrumb.js +75 -0
- package/dist/components/button-group.d.ts +14 -0
- package/dist/components/button-group.js +45 -0
- package/dist/components/button-split.d.ts +19 -0
- package/dist/components/button-split.js +73 -0
- package/dist/components/button.d.ts +15 -0
- package/dist/components/button.js +54 -0
- package/dist/components/calendar.d.ts +63 -0
- package/dist/components/calendar.js +425 -0
- package/dist/components/card.d.ts +17 -0
- package/dist/components/card.js +139 -0
- package/dist/components/carousel.d.ts +59 -0
- package/dist/components/carousel.js +503 -0
- package/dist/components/checkbox.d.ts +16 -0
- package/dist/components/checkbox.js +86 -0
- package/dist/components/circular-progress.d.ts +28 -0
- package/dist/components/circular-progress.js +127 -0
- package/dist/components/collapsible.d.ts +8 -0
- package/dist/components/collapsible.js +27 -0
- package/dist/components/color-picker.d.ts +159 -0
- package/dist/components/color-picker.js +1589 -0
- package/dist/components/combobox.d.ts +46 -0
- package/dist/components/combobox.js +237 -0
- package/dist/components/command.d.ts +22 -0
- package/dist/components/command.js +129 -0
- package/dist/components/context-menu.d.ts +38 -0
- package/dist/components/context-menu.js +164 -0
- package/dist/components/copy-to-clipboard.d.ts +32 -0
- package/dist/components/copy-to-clipboard.js +83 -0
- package/dist/components/corner-badge.d.ts +22 -0
- package/dist/components/corner-badge.js +84 -0
- package/dist/components/dashboard-grid.d.ts +83 -0
- package/dist/components/dashboard-grid.js +486 -0
- package/dist/components/date-picker.d.ts +29 -0
- package/dist/components/date-picker.js +261 -0
- package/dist/components/dialog.d.ts +26 -0
- package/dist/components/dialog.js +97 -0
- package/dist/components/dot-matrix.d.ts +32 -0
- package/dist/components/dot-matrix.js +707 -0
- package/dist/components/draggable.d.ts +42 -0
- package/dist/components/draggable.js +346 -0
- package/dist/components/drawer.d.ts +66 -0
- package/dist/components/drawer.js +304 -0
- package/dist/components/empty.d.ts +14 -0
- package/dist/components/empty.js +75 -0
- package/dist/components/field.d.ts +12 -0
- package/dist/components/field.js +44 -0
- package/dist/components/fieldset.d.ts +7 -0
- package/dist/components/fieldset.js +21 -0
- package/dist/components/file-upload.d.ts +61 -0
- package/dist/components/file-upload.js +482 -0
- package/dist/components/form.d.ts +6 -0
- package/dist/components/form.js +14 -0
- package/dist/components/frame.d.ts +10 -0
- package/dist/components/frame.js +47 -0
- package/dist/components/gauge.d.ts +27 -0
- package/dist/components/gauge.js +74 -0
- package/dist/components/grid.d.ts +26 -0
- package/dist/components/grid.js +204 -0
- package/dist/components/group.d.ts +19 -0
- package/dist/components/group.js +45 -0
- package/dist/components/input-group.d.ts +15 -0
- package/dist/components/input-group.js +61 -0
- package/dist/components/input-otp.d.ts +24 -0
- package/dist/components/input-otp.js +50 -0
- package/dist/components/input-payment.d.ts +131 -0
- package/dist/components/input-payment.js +1140 -0
- package/dist/components/input-phone.d.ts +70 -0
- package/dist/components/input-phone.js +657 -0
- package/dist/components/input-sensitive.d.ts +12 -0
- package/dist/components/input-sensitive.js +169 -0
- package/dist/components/input.d.ts +13 -0
- package/dist/components/input.js +26 -0
- package/dist/components/kbd.d.ts +6 -0
- package/dist/components/kbd.js +19 -0
- package/dist/components/kinetic-click.d.ts +48 -0
- package/dist/components/kinetic-click.js +1184 -0
- package/dist/components/label.d.ts +6 -0
- package/dist/components/label.js +18 -0
- package/dist/components/list-box.d.ts +20 -0
- package/dist/components/list-box.js +148 -0
- package/dist/components/loader.d.ts +13 -0
- package/dist/components/loader.js +430 -0
- package/dist/components/marker.d.ts +15 -0
- package/dist/components/marker.js +59 -0
- package/dist/components/menu.d.ts +42 -0
- package/dist/components/menu.js +186 -0
- package/dist/components/menubar.d.ts +40 -0
- package/dist/components/menubar.js +107 -0
- package/dist/components/message.d.ts +33 -0
- package/dist/components/message.js +112 -0
- package/dist/components/meter.d.ts +10 -0
- package/dist/components/meter.js +42 -0
- package/dist/components/morphic-toast.d.ts +61 -0
- package/dist/components/morphic-toast.js +916 -0
- package/dist/components/navigation-menu.d.ts +26 -0
- package/dist/components/navigation-menu.js +116 -0
- package/dist/components/number-field.d.ts +15 -0
- package/dist/components/number-field.js +86 -0
- package/dist/components/option-picker.d.ts +18 -0
- package/dist/components/option-picker.js +89 -0
- package/dist/components/orb.d.ts +38 -0
- package/dist/components/orb.js +1413 -0
- package/dist/components/pagination.d.ts +25 -0
- package/dist/components/pagination.js +147 -0
- package/dist/components/popover.d.ts +22 -0
- package/dist/components/popover.js +79 -0
- package/dist/components/preview-card.d.ts +16 -0
- package/dist/components/preview-card.js +60 -0
- package/dist/components/progress.d.ts +21 -0
- package/dist/components/progress.js +139 -0
- package/dist/components/radio.d.ts +16 -0
- package/dist/components/radio.js +68 -0
- package/dist/components/resizable.d.ts +58 -0
- package/dist/components/resizable.js +964 -0
- package/dist/components/ruler-picker.d.ts +27 -0
- package/dist/components/ruler-picker.js +253 -0
- package/dist/components/scroll-area.d.ts +13 -0
- package/dist/components/scroll-area.js +34 -0
- package/dist/components/scroll-bar.d.ts +13 -0
- package/dist/components/scroll-bar.js +47 -0
- package/dist/components/select.d.ts +29 -0
- package/dist/components/select.js +140 -0
- package/dist/components/separator.d.ts +6 -0
- package/dist/components/separator.js +15 -0
- package/dist/components/sidebar.d.ts +65 -0
- package/dist/components/sidebar.js +408 -0
- package/dist/components/skeleton.d.ts +5 -0
- package/dist/components/skeleton.js +12 -0
- package/dist/components/slider.d.ts +12 -0
- package/dist/components/slider.js +96 -0
- package/dist/components/sortable.d.ts +44 -0
- package/dist/components/sortable.js +327 -0
- package/dist/components/switch.d.ts +15 -0
- package/dist/components/switch.js +63 -0
- package/dist/components/table.d.ts +12 -0
- package/dist/components/table.js +65 -0
- package/dist/components/tabs.d.ts +12 -0
- package/dist/components/tabs.js +39 -0
- package/dist/components/text.d.ts +14 -0
- package/dist/components/text.js +69 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +20 -0
- package/dist/components/theme-provider.d.ts +52 -0
- package/dist/components/theme-provider.js +148 -0
- package/dist/components/timeline.d.ts +34 -0
- package/dist/components/timeline.js +147 -0
- package/dist/components/toast.d.ts +17 -0
- package/dist/components/toast.js +161 -0
- package/dist/components/toggle-group.d.ts +14 -0
- package/dist/components/toggle-group.js +53 -0
- package/dist/components/toggle.d.ts +11 -0
- package/dist/components/toggle.js +36 -0
- package/dist/components/toolbar.d.ts +11 -0
- package/dist/components/toolbar.js +49 -0
- package/dist/components/tooltip.d.ts +17 -0
- package/dist/components/tooltip.js +56 -0
- package/dist/components/waveform.d.ts +61 -0
- package/dist/components/waveform.js +500 -0
- package/dist/components/wheel-picker.d.ts +44 -0
- package/dist/components/wheel-picker.js +771 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts +9 -0
- package/dist/hooks/use-copy-to-clipboard.js +36 -0
- package/dist/hooks/use-media-query.d.ts +21 -0
- package/dist/hooks/use-media-query.js +58 -0
- package/dist/styles.css +202 -0
- package/dist/themes/afterglow.css +89 -0
- package/dist/themes/afterglow.scoped.css +90 -0
- package/dist/themes/blacklight.css +89 -0
- package/dist/themes/blacklight.scoped.css +90 -0
- package/dist/themes/cathode.css +89 -0
- package/dist/themes/cathode.scoped.css +90 -0
- package/dist/themes/cel.css +89 -0
- package/dist/themes/cel.scoped.css +90 -0
- package/dist/themes/cinder.css +89 -0
- package/dist/themes/cinder.scoped.css +90 -0
- package/dist/themes/clay.css +89 -0
- package/dist/themes/clay.scoped.css +90 -0
- package/dist/themes/cobalt.css +89 -0
- package/dist/themes/cobalt.scoped.css +90 -0
- package/dist/themes/crimson.css +89 -0
- package/dist/themes/crimson.scoped.css +90 -0
- package/dist/themes/ion.css +89 -0
- package/dist/themes/ion.scoped.css +90 -0
- package/dist/themes/iris.css +89 -0
- package/dist/themes/iris.scoped.css +90 -0
- package/dist/themes/lagoon.css +89 -0
- package/dist/themes/lagoon.scoped.css +90 -0
- package/dist/themes/mochi.css +89 -0
- package/dist/themes/mochi.scoped.css +90 -0
- package/dist/themes/riso.css +89 -0
- package/dist/themes/riso.scoped.css +90 -0
- package/dist/themes/roast.css +89 -0
- package/dist/themes/roast.scoped.css +90 -0
- package/dist/themes/sage.css +89 -0
- package/dist/themes/sage.scoped.css +90 -0
- package/dist/themes/sepia.css +89 -0
- package/dist/themes/sepia.scoped.css +90 -0
- package/dist/themes/sorbet.css +89 -0
- package/dist/themes/sorbet.scoped.css +90 -0
- package/dist/themes/taffy.css +89 -0
- package/dist/themes/taffy.scoped.css +90 -0
- package/dist/themes/toxin.css +89 -0
- package/dist/themes/toxin.scoped.css +90 -0
- package/package.json +140 -0
|
@@ -0,0 +1,1589 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "cn";
|
|
3
|
+
import { ChevronDown } from "xiod-icons/icons/ChevronDown";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import * as React$1 from "react";
|
|
6
|
+
import { Dropper } from "xiod-icons/icons/Dropper";
|
|
7
|
+
//#region src/components/color-picker.tsx
|
|
8
|
+
const OUTER_COLORS = [
|
|
9
|
+
{
|
|
10
|
+
h: 47,
|
|
11
|
+
s: 97,
|
|
12
|
+
l: 65
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
h: 37,
|
|
16
|
+
s: 98,
|
|
17
|
+
l: 65
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
h: 27,
|
|
21
|
+
s: 95,
|
|
22
|
+
l: 64
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
h: 14,
|
|
26
|
+
s: 90,
|
|
27
|
+
l: 64
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
h: 0,
|
|
31
|
+
s: 85,
|
|
32
|
+
l: 64
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
h: 327,
|
|
36
|
+
s: 75,
|
|
37
|
+
l: 62
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
h: 285,
|
|
41
|
+
s: 51,
|
|
42
|
+
l: 59
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
h: 257,
|
|
46
|
+
s: 65,
|
|
47
|
+
l: 64
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
h: 225,
|
|
51
|
+
s: 71,
|
|
52
|
+
l: 65
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
h: 202,
|
|
56
|
+
s: 68,
|
|
57
|
+
l: 65
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
h: 151,
|
|
61
|
+
s: 43,
|
|
62
|
+
l: 63
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
h: 96,
|
|
66
|
+
s: 49,
|
|
67
|
+
l: 67
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
const INNER_COLORS = [
|
|
71
|
+
{
|
|
72
|
+
h: 50,
|
|
73
|
+
s: 95,
|
|
74
|
+
l: 85
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
h: 26,
|
|
78
|
+
s: 89,
|
|
79
|
+
l: 89
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
h: 345,
|
|
83
|
+
s: 77,
|
|
84
|
+
l: 88
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
h: 283,
|
|
88
|
+
s: 47,
|
|
89
|
+
l: 84
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
h: 209,
|
|
93
|
+
s: 70,
|
|
94
|
+
l: 87
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
h: 116,
|
|
98
|
+
s: 42,
|
|
99
|
+
l: 87
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
const DEFAULT_COLORS = [...INNER_COLORS, ...OUTER_COLORS];
|
|
103
|
+
const BLOOM_EASING_CSS = "linear(0, 0.060 3%, 0.200 7%, 0.420 13%, 0.680 20%, 0.900 28%, 1.020 35%, 1.060 45%, 1.025 53%, 0.997 62%, 1.0 68%)";
|
|
104
|
+
const PETAL_STAGGER = 20;
|
|
105
|
+
const BAR_GAP = 20;
|
|
106
|
+
const BAR_WIDTH = 12;
|
|
107
|
+
const SLIDER_OFFSET = 30;
|
|
108
|
+
const ARC_GRADIENT_STEPS = 11;
|
|
109
|
+
const DEFAULT_VALUE = {
|
|
110
|
+
hue: 330,
|
|
111
|
+
saturation: 70,
|
|
112
|
+
alpha: 50,
|
|
113
|
+
layer: "outer"
|
|
114
|
+
};
|
|
115
|
+
function lightnessToSliderValue(l) {
|
|
116
|
+
const minLightness = 20;
|
|
117
|
+
const maxLightness = 100;
|
|
118
|
+
return (maxLightness - Math.max(minLightness, Math.min(maxLightness, l))) / 80 * 100;
|
|
119
|
+
}
|
|
120
|
+
function sliderValueToLightness(sliderValue) {
|
|
121
|
+
return 100 - sliderValue / 100 * 80;
|
|
122
|
+
}
|
|
123
|
+
function hexToHsl(hex) {
|
|
124
|
+
let cleanHex = hex.replace(/^#/, "");
|
|
125
|
+
if (cleanHex.length === 3) cleanHex = cleanHex.split("").map((c) => c + c).join("");
|
|
126
|
+
const r = parseInt(cleanHex.slice(0, 2), 16) / 255;
|
|
127
|
+
const g = parseInt(cleanHex.slice(2, 4), 16) / 255;
|
|
128
|
+
const b = parseInt(cleanHex.slice(4, 6), 16) / 255;
|
|
129
|
+
const max = Math.max(r, g, b);
|
|
130
|
+
const min = Math.min(r, g, b);
|
|
131
|
+
const l = (max + min) / 2;
|
|
132
|
+
if (max === min) return {
|
|
133
|
+
h: 0,
|
|
134
|
+
s: 0,
|
|
135
|
+
l: Math.round(l * 100)
|
|
136
|
+
};
|
|
137
|
+
const d = max - min;
|
|
138
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
139
|
+
let h = 0;
|
|
140
|
+
switch (max) {
|
|
141
|
+
case r:
|
|
142
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
143
|
+
break;
|
|
144
|
+
case g:
|
|
145
|
+
h = ((b - r) / d + 2) / 6;
|
|
146
|
+
break;
|
|
147
|
+
case b: h = ((r - g) / d + 4) / 6;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
h: Math.round(h * 360),
|
|
151
|
+
s: Math.round(s * 100),
|
|
152
|
+
l: Math.round(l * 100)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function rgbToHsl(r, g, b) {
|
|
156
|
+
r /= 255;
|
|
157
|
+
g /= 255;
|
|
158
|
+
b /= 255;
|
|
159
|
+
const max = Math.max(r, g, b);
|
|
160
|
+
const min = Math.min(r, g, b);
|
|
161
|
+
const l = (max + min) / 2;
|
|
162
|
+
if (max === min) return {
|
|
163
|
+
h: 0,
|
|
164
|
+
s: 0,
|
|
165
|
+
l: Math.round(l * 100)
|
|
166
|
+
};
|
|
167
|
+
const d = max - min;
|
|
168
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
169
|
+
let h = 0;
|
|
170
|
+
switch (max) {
|
|
171
|
+
case r:
|
|
172
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
173
|
+
break;
|
|
174
|
+
case g:
|
|
175
|
+
h = ((b - r) / d + 2) / 6;
|
|
176
|
+
break;
|
|
177
|
+
case b: h = ((r - g) / d + 4) / 6;
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
h: Math.round(h * 360),
|
|
181
|
+
s: Math.round(s * 100),
|
|
182
|
+
l: Math.round(l * 100)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function parseColor(input) {
|
|
186
|
+
if (typeof input === "object") return input;
|
|
187
|
+
const str = input.trim().toLowerCase();
|
|
188
|
+
if (str.startsWith("#")) return hexToHsl(str);
|
|
189
|
+
const hslMatch = str.match(/^hsla?\(\s*([\d.]+)[\s,]+([\d.]+)%?[\s,]+([\d.]+)%?/);
|
|
190
|
+
if (hslMatch) return {
|
|
191
|
+
h: Math.round(parseFloat(hslMatch[1])),
|
|
192
|
+
s: Math.round(parseFloat(hslMatch[2])),
|
|
193
|
+
l: Math.round(parseFloat(hslMatch[3]))
|
|
194
|
+
};
|
|
195
|
+
const rgbMatch = str.match(/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/);
|
|
196
|
+
if (rgbMatch) return rgbToHsl(parseFloat(rgbMatch[1]), parseFloat(rgbMatch[2]), parseFloat(rgbMatch[3]));
|
|
197
|
+
return {
|
|
198
|
+
h: 0,
|
|
199
|
+
s: 0,
|
|
200
|
+
l: 50
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function hslToHex(h, s, l) {
|
|
204
|
+
const sNorm = s / 100;
|
|
205
|
+
const lNorm = l / 100;
|
|
206
|
+
const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
|
|
207
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
208
|
+
const m = lNorm - c / 2;
|
|
209
|
+
let r = 0;
|
|
210
|
+
let g = 0;
|
|
211
|
+
let b = 0;
|
|
212
|
+
if (h >= 0 && h < 60) {
|
|
213
|
+
r = c;
|
|
214
|
+
g = x;
|
|
215
|
+
b = 0;
|
|
216
|
+
} else if (h >= 60 && h < 120) {
|
|
217
|
+
r = x;
|
|
218
|
+
g = c;
|
|
219
|
+
b = 0;
|
|
220
|
+
} else if (h >= 120 && h < 180) {
|
|
221
|
+
r = 0;
|
|
222
|
+
g = c;
|
|
223
|
+
b = x;
|
|
224
|
+
} else if (h >= 180 && h < 240) {
|
|
225
|
+
r = 0;
|
|
226
|
+
g = x;
|
|
227
|
+
b = c;
|
|
228
|
+
} else if (h >= 240 && h < 300) {
|
|
229
|
+
r = x;
|
|
230
|
+
g = 0;
|
|
231
|
+
b = c;
|
|
232
|
+
} else {
|
|
233
|
+
r = c;
|
|
234
|
+
g = 0;
|
|
235
|
+
b = x;
|
|
236
|
+
}
|
|
237
|
+
const toHex = (n) => {
|
|
238
|
+
const hex = Math.round((n + m) * 255).toString(16);
|
|
239
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
240
|
+
};
|
|
241
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
242
|
+
}
|
|
243
|
+
function getVisualSaturation(sliderValue, baseSaturation) {
|
|
244
|
+
return sliderValue < 10 ? sliderValue / 10 * baseSaturation : baseSaturation;
|
|
245
|
+
}
|
|
246
|
+
function hslToString(h, s, l) {
|
|
247
|
+
return `hsl(${Math.round(h)}, ${Math.round(s)}%, ${Math.round(l)}%)`;
|
|
248
|
+
}
|
|
249
|
+
function hslaToString(h, s, l, a) {
|
|
250
|
+
return `hsla(${Math.round(h)}, ${Math.round(s)}%, ${Math.round(l)}%, ${(a / 100).toFixed(2)})`;
|
|
251
|
+
}
|
|
252
|
+
function hslToRgb(h, s, l) {
|
|
253
|
+
const sNorm = s / 100;
|
|
254
|
+
const lNorm = l / 100;
|
|
255
|
+
const k = (n) => (n + h / 30) % 12;
|
|
256
|
+
const a = sNorm * Math.min(lNorm, 1 - lNorm);
|
|
257
|
+
const f = (n) => lNorm - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
|
258
|
+
return {
|
|
259
|
+
r: Math.round(255 * f(0)),
|
|
260
|
+
g: Math.round(255 * f(8)),
|
|
261
|
+
b: Math.round(255 * f(4))
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function rgbaToString(r, g, b, a) {
|
|
265
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${(a / 100).toFixed(2)})`;
|
|
266
|
+
}
|
|
267
|
+
function createColorOutput(hue, sliderValue, visualSaturation, baseSaturation, lightness, alpha, layer) {
|
|
268
|
+
const { r, g, b } = hslToRgb(hue, visualSaturation, lightness);
|
|
269
|
+
return {
|
|
270
|
+
hue,
|
|
271
|
+
saturation: sliderValue,
|
|
272
|
+
originalSaturation: baseSaturation,
|
|
273
|
+
lightness,
|
|
274
|
+
alpha,
|
|
275
|
+
layer,
|
|
276
|
+
r,
|
|
277
|
+
g,
|
|
278
|
+
b,
|
|
279
|
+
hex: hslToHex(hue, visualSaturation, lightness),
|
|
280
|
+
hsl: hslToString(hue, visualSaturation, lightness),
|
|
281
|
+
hsla: hslaToString(hue, visualSaturation, lightness, alpha),
|
|
282
|
+
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
283
|
+
rgba: rgbaToString(r, g, b, alpha)
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function organizeColorsIntoLayers(colors) {
|
|
287
|
+
if (!colors || colors.length === 0) return [];
|
|
288
|
+
const sortedByLightness = colors.toSorted((a, b) => b.l - a.l);
|
|
289
|
+
const total = sortedByLightness.length;
|
|
290
|
+
let layerCounts = [];
|
|
291
|
+
if (total <= 10) layerCounts = [total];
|
|
292
|
+
else if (total <= 24) {
|
|
293
|
+
const inner = Math.max(4, Math.floor(total * .35));
|
|
294
|
+
layerCounts = [inner, total - inner];
|
|
295
|
+
} else if (total <= 42) {
|
|
296
|
+
const inner = Math.max(5, Math.floor(total * .15));
|
|
297
|
+
const middle = Math.floor(total * .35);
|
|
298
|
+
layerCounts = [
|
|
299
|
+
inner,
|
|
300
|
+
middle,
|
|
301
|
+
total - inner - middle
|
|
302
|
+
];
|
|
303
|
+
} else {
|
|
304
|
+
const inner = Math.max(6, Math.floor(total * .1));
|
|
305
|
+
const mid1 = Math.floor(total * .2);
|
|
306
|
+
const mid2 = Math.floor(total * .3);
|
|
307
|
+
layerCounts = [
|
|
308
|
+
inner,
|
|
309
|
+
mid1,
|
|
310
|
+
mid2,
|
|
311
|
+
total - inner - mid1 - mid2
|
|
312
|
+
];
|
|
313
|
+
}
|
|
314
|
+
const layers = [];
|
|
315
|
+
let currentIndex = 0;
|
|
316
|
+
for (let i = 0; i < layerCounts.length; i++) {
|
|
317
|
+
const count = layerCounts[i];
|
|
318
|
+
const itemsForThisLayer = sortedByLightness.slice(currentIndex, currentIndex + count);
|
|
319
|
+
itemsForThisLayer.sort((a, b) => a.h - b.h);
|
|
320
|
+
if (itemsForThisLayer.length > 0) layers.push(itemsForThisLayer);
|
|
321
|
+
currentIndex += count;
|
|
322
|
+
}
|
|
323
|
+
return layers;
|
|
324
|
+
}
|
|
325
|
+
function calculateLayerRadii(layers, coreSize, petalSize) {
|
|
326
|
+
const radii = [];
|
|
327
|
+
const W = petalSize;
|
|
328
|
+
const Rc = coreSize / 2;
|
|
329
|
+
const Rp = petalSize / 2;
|
|
330
|
+
for (let i = 0; i < layers.length; i++) {
|
|
331
|
+
const N = layers[i].length;
|
|
332
|
+
const overlapFactor = N <= 8 ? .45 : N <= 12 ? .5 : .55;
|
|
333
|
+
const lateralGaplessR = N * W * overlapFactor / (2 * Math.PI);
|
|
334
|
+
let r = 0;
|
|
335
|
+
if (i === 0) {
|
|
336
|
+
const coreOverlap = N <= 5 ? W * .35 : W * .25;
|
|
337
|
+
const idealCoreR = Rc + Rp - coreOverlap;
|
|
338
|
+
r = Math.max(idealCoreR, lateralGaplessR);
|
|
339
|
+
} else {
|
|
340
|
+
const prevR = radii[i - 1];
|
|
341
|
+
const prevN = layers[i - 1].length;
|
|
342
|
+
const circumference = 2 * Math.PI * prevR;
|
|
343
|
+
const sparsity = prevN * W / circumference;
|
|
344
|
+
let adaptiveStep = W * .35;
|
|
345
|
+
if (sparsity < .85) adaptiveStep = W * .15;
|
|
346
|
+
else if (sparsity > 1.1) adaptiveStep = W * .45;
|
|
347
|
+
const idealNestleR = prevR + adaptiveStep;
|
|
348
|
+
r = Math.max(idealNestleR, lateralGaplessR);
|
|
349
|
+
r = Math.max(r, prevR + W * .1);
|
|
350
|
+
}
|
|
351
|
+
radii.push(r);
|
|
352
|
+
}
|
|
353
|
+
return radii;
|
|
354
|
+
}
|
|
355
|
+
function calculateLayerRotations(layers) {
|
|
356
|
+
const rotations = [0];
|
|
357
|
+
for (let i = 1; i < layers.length; i++) {
|
|
358
|
+
const offset = 360 / layers[i - 1].length / 2;
|
|
359
|
+
rotations.push(offset);
|
|
360
|
+
}
|
|
361
|
+
return rotations;
|
|
362
|
+
}
|
|
363
|
+
function calculateBarRadius(layerRadii, petalSize, coreSize, barGap) {
|
|
364
|
+
return layerRadii.length > 0 ? layerRadii[layerRadii.length - 1] + petalSize / 2 + barGap : coreSize / 2 + barGap;
|
|
365
|
+
}
|
|
366
|
+
function calculateContainerSize(barRadius, circularBarWidth, showAlphaSlider, showOpacitySlider, sliderOffset, sliderWidth) {
|
|
367
|
+
const barExtent = barRadius + circularBarWidth / 2;
|
|
368
|
+
const sliderExtent = showAlphaSlider || showOpacitySlider ? barRadius + sliderOffset + sliderWidth / 2 : 0;
|
|
369
|
+
return Math.max(barExtent, sliderExtent) * 2 + 4;
|
|
370
|
+
}
|
|
371
|
+
function getPetalZIndex(index, bottomIndex, totalPetals, layerIdx, totalLayers, isBottomLeft = false, isBottomRight = false) {
|
|
372
|
+
const baseZ = (totalLayers - layerIdx) * 100;
|
|
373
|
+
const maxLocalZ = totalPetals + 10;
|
|
374
|
+
if (index === bottomIndex) {
|
|
375
|
+
if (isBottomRight) return baseZ + maxLocalZ;
|
|
376
|
+
if (isBottomLeft) return baseZ;
|
|
377
|
+
return baseZ;
|
|
378
|
+
}
|
|
379
|
+
return baseZ + (index - bottomIndex + totalPetals) % totalPetals;
|
|
380
|
+
}
|
|
381
|
+
function polarToCartesian(cx, cy, r, angleInDegrees) {
|
|
382
|
+
const angleInRadians = angleInDegrees * Math.PI / 180;
|
|
383
|
+
return {
|
|
384
|
+
x: cx + r * Math.cos(angleInRadians),
|
|
385
|
+
y: cy + r * Math.sin(angleInRadians)
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function describeArc(cx, cy, r, startAng, endAng) {
|
|
389
|
+
const start = polarToCartesian(cx, cy, r, startAng);
|
|
390
|
+
const end = polarToCartesian(cx, cy, r, endAng);
|
|
391
|
+
const largeArcFlag = Math.abs(endAng - startAng) > 180 ? "1" : "0";
|
|
392
|
+
return `M ${start.x} ${start.y} A ${r} ${r} 0 ${largeArcFlag} 1 ${end.x} ${end.y}`;
|
|
393
|
+
}
|
|
394
|
+
function getCenterAngle(position) {
|
|
395
|
+
switch (position) {
|
|
396
|
+
case "top": return -90;
|
|
397
|
+
case "bottom": return 90;
|
|
398
|
+
case "left": return 180;
|
|
399
|
+
default: return 0;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function getOppositePosition(position) {
|
|
403
|
+
switch (position) {
|
|
404
|
+
case "left": return "right";
|
|
405
|
+
case "right": return "left";
|
|
406
|
+
case "top": return "bottom";
|
|
407
|
+
default: return "top";
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function calculateSliderValueFromPoint(dx, dy, centerAngle, halfSweep, position) {
|
|
411
|
+
let normalizedAngle = Math.atan2(dy, dx) * (180 / Math.PI) - centerAngle;
|
|
412
|
+
while (normalizedAngle > 180) normalizedAngle -= 360;
|
|
413
|
+
while (normalizedAngle < -180) normalizedAngle += 360;
|
|
414
|
+
normalizedAngle = Math.max(-halfSweep, Math.min(halfSweep, normalizedAngle));
|
|
415
|
+
let newValue;
|
|
416
|
+
if (position === "left") newValue = (halfSweep - normalizedAngle) / (2 * halfSweep) * 100;
|
|
417
|
+
else newValue = (normalizedAngle + halfSweep) / (2 * halfSweep) * 100;
|
|
418
|
+
return Math.round(Math.max(0, Math.min(100, newValue)));
|
|
419
|
+
}
|
|
420
|
+
function calculateArcGradientColors(hue, baseSaturation, steps) {
|
|
421
|
+
return Array.from({ length: steps }, (_, i) => {
|
|
422
|
+
const t = i / (steps - 1);
|
|
423
|
+
return hslToString(hue, getVisualSaturation(t * 100, baseSaturation), 100 - t * 80);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function computeAdaptivePosition({ elementRect, containerSize, currentShiftOffset, windowWidth, windowHeight, sliderPosition, adaptivePositioning, circularBarWidth, sliderOffset }) {
|
|
427
|
+
const halfSize = containerSize / 2;
|
|
428
|
+
const centerX = elementRect.left + elementRect.width / 2 - currentShiftOffset.x;
|
|
429
|
+
const centerY = elementRect.top + elementRect.height / 2 - currentShiftOffset.y;
|
|
430
|
+
let newShiftX = 0;
|
|
431
|
+
let newShiftY = 0;
|
|
432
|
+
if (adaptivePositioning) {
|
|
433
|
+
const padding = 10;
|
|
434
|
+
if (centerX + halfSize > windowWidth - padding) newShiftX = windowWidth - padding - (centerX + halfSize);
|
|
435
|
+
else if (centerX - halfSize < padding) newShiftX = padding - (centerX - halfSize);
|
|
436
|
+
if (centerY + halfSize > windowHeight - padding) newShiftY = windowHeight - padding - (centerY + halfSize);
|
|
437
|
+
else if (centerY - halfSize < padding) newShiftY = padding - (centerY - halfSize);
|
|
438
|
+
}
|
|
439
|
+
let effectivePosition = sliderPosition || "right";
|
|
440
|
+
if (!sliderPosition) {
|
|
441
|
+
const spaceRight = windowWidth - (centerX + newShiftX + halfSize);
|
|
442
|
+
const spaceLeft = centerX + newShiftX - halfSize;
|
|
443
|
+
const spaceTop = centerY + newShiftY - halfSize;
|
|
444
|
+
const spaceBottom = windowHeight - (centerY + newShiftY + halfSize);
|
|
445
|
+
const threshold = sliderOffset + circularBarWidth + 20;
|
|
446
|
+
if (spaceRight < threshold && spaceLeft > spaceRight) effectivePosition = "left";
|
|
447
|
+
else if (spaceLeft < threshold && spaceRight > spaceLeft) effectivePosition = "right";
|
|
448
|
+
else if (spaceBottom < threshold && spaceTop > spaceBottom) effectivePosition = "top";
|
|
449
|
+
else if (spaceTop < threshold && spaceBottom > spaceTop) effectivePosition = "bottom";
|
|
450
|
+
else effectivePosition = "right";
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
effectivePosition,
|
|
454
|
+
shiftOffset: {
|
|
455
|
+
x: newShiftX,
|
|
456
|
+
y: newShiftY
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function BlossomColorPicker({ value, defaultValue, colors, onChange, onCollapse, disabled = false, openOnHover = false, initialExpanded = false, animationDuration = 300, showAlphaSlider = true, showOpacitySlider = true, coreSize = 32, petalSize = 32, showCoreColor = true, sliderPosition, adaptivePositioning = true, circularBarWidth = BAR_WIDTH, sliderWidth = BAR_WIDTH, sliderOffset = SLIDER_OFFSET, collapsible = true, className, ref, ...props }) {
|
|
461
|
+
const rootRef = React$1.useRef(null);
|
|
462
|
+
const svgRef = React$1.useRef(null);
|
|
463
|
+
const alphaSvgRef = React$1.useRef(null);
|
|
464
|
+
React$1.useImperativeHandle(ref, () => rootRef.current);
|
|
465
|
+
const [internalValue, setInternalValue] = React$1.useState(defaultValue ?? DEFAULT_VALUE);
|
|
466
|
+
const [isExpanded, setIsExpanded] = React$1.useState(!collapsible || initialExpanded);
|
|
467
|
+
const [mousePos, setMousePos] = React$1.useState(null);
|
|
468
|
+
const [isHoveringCore, setIsHoveringCore] = React$1.useState(false);
|
|
469
|
+
const [hoveredPetal, setHoveredPetal] = React$1.useState(null);
|
|
470
|
+
const [shiftOffset, setShiftOffset] = React$1.useState({
|
|
471
|
+
x: 0,
|
|
472
|
+
y: 0
|
|
473
|
+
});
|
|
474
|
+
const [effectivePosition, setEffectivePosition] = React$1.useState(sliderPosition || "right");
|
|
475
|
+
const [isDraggingSlider, setIsDraggingSlider] = React$1.useState(false);
|
|
476
|
+
const [isDraggingAlpha, setIsDraggingAlpha] = React$1.useState(false);
|
|
477
|
+
const prevExpandedRef = React$1.useRef(isExpanded);
|
|
478
|
+
const [hasEyeDropper, setHasEyeDropper] = React$1.useState(false);
|
|
479
|
+
React$1.useEffect(() => {
|
|
480
|
+
setHasEyeDropper(typeof window !== "undefined" && "EyeDropper" in window);
|
|
481
|
+
}, []);
|
|
482
|
+
const [mounted, setMounted] = React$1.useState(false);
|
|
483
|
+
React$1.useEffect(() => {
|
|
484
|
+
setMounted(true);
|
|
485
|
+
}, []);
|
|
486
|
+
const currentValue = value ?? internalValue;
|
|
487
|
+
const normalizedColors = React$1.useMemo(() => {
|
|
488
|
+
return colors && colors.length > 0 ? colors.map(parseColor) : DEFAULT_COLORS;
|
|
489
|
+
}, [colors]);
|
|
490
|
+
const { layers, allColors, layerPrefixCounts, layerRadii, layerRotations, barRadius, containerSize } = React$1.useMemo(() => {
|
|
491
|
+
const lay = organizeColorsIntoLayers(normalizedColors);
|
|
492
|
+
const allCol = lay.flat();
|
|
493
|
+
const lpc = [0];
|
|
494
|
+
for (let i = 1; i < lay.length; i++) lpc.push(lpc[i - 1] + lay[i - 1].length);
|
|
495
|
+
const radii = calculateLayerRadii(lay, coreSize, petalSize);
|
|
496
|
+
const rots = calculateLayerRotations(lay);
|
|
497
|
+
const bRad = calculateBarRadius(radii, petalSize, coreSize, BAR_GAP);
|
|
498
|
+
return {
|
|
499
|
+
layers: lay,
|
|
500
|
+
allColors: allCol,
|
|
501
|
+
layerPrefixCounts: lpc,
|
|
502
|
+
layerRadii: radii,
|
|
503
|
+
layerRotations: rots,
|
|
504
|
+
barRadius: bRad,
|
|
505
|
+
containerSize: calculateContainerSize(bRad, circularBarWidth, showAlphaSlider, showOpacitySlider, sliderOffset, sliderWidth)
|
|
506
|
+
};
|
|
507
|
+
}, [
|
|
508
|
+
normalizedColors,
|
|
509
|
+
coreSize,
|
|
510
|
+
petalSize,
|
|
511
|
+
circularBarWidth,
|
|
512
|
+
showAlphaSlider,
|
|
513
|
+
showOpacitySlider,
|
|
514
|
+
sliderOffset,
|
|
515
|
+
sliderWidth
|
|
516
|
+
]);
|
|
517
|
+
const baseSaturation = React$1.useMemo(() => {
|
|
518
|
+
if (currentValue.originalSaturation !== void 0) return currentValue.originalSaturation;
|
|
519
|
+
return allColors.find((c) => c.h === currentValue.hue)?.s ?? 70;
|
|
520
|
+
}, [
|
|
521
|
+
allColors,
|
|
522
|
+
currentValue.hue,
|
|
523
|
+
currentValue.originalSaturation
|
|
524
|
+
]);
|
|
525
|
+
const currentLightness = React$1.useMemo(() => {
|
|
526
|
+
return currentValue.lightness ?? sliderValueToLightness(currentValue.saturation);
|
|
527
|
+
}, [currentValue.lightness, currentValue.saturation]);
|
|
528
|
+
const visualSaturation = React$1.useMemo(() => {
|
|
529
|
+
return getVisualSaturation(currentValue.saturation, baseSaturation);
|
|
530
|
+
}, [currentValue.saturation, baseSaturation]);
|
|
531
|
+
const coreColorString = React$1.useMemo(() => {
|
|
532
|
+
if (isExpanded && !showCoreColor) return "#FFFFFF";
|
|
533
|
+
return hslaToString(currentValue.hue, baseSaturation, currentLightness, currentValue.alpha);
|
|
534
|
+
}, [
|
|
535
|
+
isExpanded,
|
|
536
|
+
showCoreColor,
|
|
537
|
+
currentValue.hue,
|
|
538
|
+
baseSaturation,
|
|
539
|
+
currentLightness,
|
|
540
|
+
currentValue.alpha
|
|
541
|
+
]);
|
|
542
|
+
const outputColor = React$1.useMemo(() => {
|
|
543
|
+
return createColorOutput(currentValue.hue, currentValue.saturation, visualSaturation, baseSaturation, currentLightness, currentValue.alpha, currentValue.layer);
|
|
544
|
+
}, [
|
|
545
|
+
currentValue,
|
|
546
|
+
visualSaturation,
|
|
547
|
+
baseSaturation,
|
|
548
|
+
currentLightness
|
|
549
|
+
]);
|
|
550
|
+
React$1.useEffect(() => {
|
|
551
|
+
if (isExpanded && rootRef.current) {
|
|
552
|
+
const result = computeAdaptivePosition({
|
|
553
|
+
elementRect: rootRef.current.getBoundingClientRect(),
|
|
554
|
+
containerSize,
|
|
555
|
+
currentShiftOffset: {
|
|
556
|
+
x: 0,
|
|
557
|
+
y: 0
|
|
558
|
+
},
|
|
559
|
+
windowWidth: typeof window !== "undefined" ? window.innerWidth : 1e3,
|
|
560
|
+
windowHeight: typeof window !== "undefined" ? window.innerHeight : 800,
|
|
561
|
+
sliderPosition,
|
|
562
|
+
adaptivePositioning,
|
|
563
|
+
circularBarWidth,
|
|
564
|
+
sliderOffset
|
|
565
|
+
});
|
|
566
|
+
setShiftOffset(result.shiftOffset);
|
|
567
|
+
setEffectivePosition(result.effectivePosition);
|
|
568
|
+
} else if (!isExpanded) {
|
|
569
|
+
setShiftOffset({
|
|
570
|
+
x: 0,
|
|
571
|
+
y: 0
|
|
572
|
+
});
|
|
573
|
+
setEffectivePosition(sliderPosition || "right");
|
|
574
|
+
}
|
|
575
|
+
prevExpandedRef.current = isExpanded;
|
|
576
|
+
}, [
|
|
577
|
+
isExpanded,
|
|
578
|
+
containerSize,
|
|
579
|
+
sliderPosition,
|
|
580
|
+
adaptivePositioning,
|
|
581
|
+
circularBarWidth,
|
|
582
|
+
sliderOffset
|
|
583
|
+
]);
|
|
584
|
+
React$1.useEffect(() => {
|
|
585
|
+
if (!collapsible || !isExpanded) return;
|
|
586
|
+
const handleClick = (e) => {
|
|
587
|
+
if (!rootRef.current) return;
|
|
588
|
+
const rect = rootRef.current.getBoundingClientRect();
|
|
589
|
+
const centerX = rect.left + rect.width / 2;
|
|
590
|
+
const centerY = rect.top + rect.height / 2;
|
|
591
|
+
const dx = e.clientX - centerX;
|
|
592
|
+
const dy = e.clientY - centerY;
|
|
593
|
+
if (Math.sqrt(dx * dx + dy * dy) > Math.max(barRadius + circularBarWidth / 2, showAlphaSlider || showOpacitySlider ? barRadius + sliderOffset + sliderWidth / 2 : 0) + 4 || !rootRef.current.contains(e.target)) {
|
|
594
|
+
setIsExpanded(false);
|
|
595
|
+
if (onCollapse) onCollapse(outputColor);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
document.addEventListener("mousedown", handleClick);
|
|
599
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
600
|
+
}, [
|
|
601
|
+
collapsible,
|
|
602
|
+
isExpanded,
|
|
603
|
+
onCollapse,
|
|
604
|
+
outputColor,
|
|
605
|
+
barRadius,
|
|
606
|
+
circularBarWidth,
|
|
607
|
+
showAlphaSlider,
|
|
608
|
+
showOpacitySlider,
|
|
609
|
+
sliderOffset,
|
|
610
|
+
sliderWidth
|
|
611
|
+
]);
|
|
612
|
+
const updateValue = (nextVal) => {
|
|
613
|
+
if (!value) setInternalValue(nextVal);
|
|
614
|
+
if (onChange) {
|
|
615
|
+
const newBaseSaturation = nextVal.originalSaturation ?? allColors.find((c) => c.h === nextVal.hue)?.s ?? 70;
|
|
616
|
+
const nextLightness = nextVal.lightness ?? sliderValueToLightness(nextVal.saturation);
|
|
617
|
+
const nextVisualSat = getVisualSaturation(nextVal.saturation, newBaseSaturation);
|
|
618
|
+
const { r, g, b } = hslToRgb(nextVal.hue, nextVisualSat, nextLightness);
|
|
619
|
+
onChange({
|
|
620
|
+
...nextVal,
|
|
621
|
+
originalSaturation: newBaseSaturation,
|
|
622
|
+
lightness: nextLightness,
|
|
623
|
+
hex: hslToHex(nextVal.hue, nextVisualSat, nextLightness),
|
|
624
|
+
hsl: hslToString(nextVal.hue, nextVisualSat, nextLightness),
|
|
625
|
+
hsla: hslaToString(nextVal.hue, nextVisualSat, nextLightness, nextVal.alpha),
|
|
626
|
+
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
627
|
+
rgba: rgbaToString(r, g, b, nextVal.alpha),
|
|
628
|
+
r,
|
|
629
|
+
g,
|
|
630
|
+
b
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
const handleCoreClick = async () => {
|
|
635
|
+
if (disabled) return;
|
|
636
|
+
if (isExpanded && hasEyeDropper) {
|
|
637
|
+
try {
|
|
638
|
+
const EyeDropperClass = window.EyeDropper;
|
|
639
|
+
const result = await new EyeDropperClass().open();
|
|
640
|
+
if (result?.sRGBHex) {
|
|
641
|
+
const hex = result.sRGBHex;
|
|
642
|
+
const hsl = hexToHsl(hex);
|
|
643
|
+
updateValue({
|
|
644
|
+
hue: hsl.h,
|
|
645
|
+
saturation: lightnessToSliderValue(hsl.l),
|
|
646
|
+
lightness: hsl.l,
|
|
647
|
+
originalSaturation: hsl.s,
|
|
648
|
+
alpha: currentValue.alpha,
|
|
649
|
+
layer: "outer"
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
} catch (err) {
|
|
653
|
+
console.warn("EyeDropper failed or was cancelled:", err);
|
|
654
|
+
}
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (collapsible) {
|
|
658
|
+
const next = !isExpanded;
|
|
659
|
+
setIsExpanded(next);
|
|
660
|
+
if (!next && onCollapse) onCollapse(outputColor);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
const handlePetalClick = (color, layerIdx) => {
|
|
664
|
+
updateValue({
|
|
665
|
+
hue: color.h,
|
|
666
|
+
saturation: lightnessToSliderValue(color.l),
|
|
667
|
+
lightness: color.l,
|
|
668
|
+
originalSaturation: color.s,
|
|
669
|
+
alpha: currentValue.alpha,
|
|
670
|
+
layer: layerIdx === 0 ? "inner" : "outer"
|
|
671
|
+
});
|
|
672
|
+
};
|
|
673
|
+
const handleSliderChange = (val) => {
|
|
674
|
+
const lightness = sliderValueToLightness(val);
|
|
675
|
+
updateValue({
|
|
676
|
+
...currentValue,
|
|
677
|
+
saturation: val,
|
|
678
|
+
lightness,
|
|
679
|
+
originalSaturation: currentValue.originalSaturation ?? baseSaturation
|
|
680
|
+
});
|
|
681
|
+
};
|
|
682
|
+
const handleMouseEnter = () => {
|
|
683
|
+
if (disabled) return;
|
|
684
|
+
if (openOnHover && collapsible) setIsExpanded(true);
|
|
685
|
+
};
|
|
686
|
+
const handleMouseMove = (e) => {
|
|
687
|
+
if (!isExpanded) return;
|
|
688
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
689
|
+
const x = e.clientX - (rect.left + rect.width / 2);
|
|
690
|
+
const y = e.clientY - (rect.top + rect.height / 2);
|
|
691
|
+
setMousePos({
|
|
692
|
+
x,
|
|
693
|
+
y
|
|
694
|
+
});
|
|
695
|
+
};
|
|
696
|
+
const handleMouseLeave = () => {
|
|
697
|
+
setMousePos(null);
|
|
698
|
+
};
|
|
699
|
+
const handleSliderPointerDown = (e) => {
|
|
700
|
+
e.preventDefault();
|
|
701
|
+
setIsDraggingSlider(true);
|
|
702
|
+
updateSliderFromPointer(e.clientX, e.clientY);
|
|
703
|
+
};
|
|
704
|
+
const updateSliderFromPointer = (clientX, clientY) => {
|
|
705
|
+
if (!svgRef.current) return;
|
|
706
|
+
const rect = svgRef.current.getBoundingClientRect();
|
|
707
|
+
const centerX = rect.left + rect.width / 2;
|
|
708
|
+
const centerY = rect.top + rect.height / 2;
|
|
709
|
+
const val = calculateSliderValueFromPoint(clientX - centerX, clientY - centerY, getCenterAngle(effectivePosition), 30, effectivePosition);
|
|
710
|
+
handleSliderChange(val);
|
|
711
|
+
};
|
|
712
|
+
const updateSliderFromPointerRef = React$1.useRef(updateSliderFromPointer);
|
|
713
|
+
React$1.useEffect(() => {
|
|
714
|
+
updateSliderFromPointerRef.current = updateSliderFromPointer;
|
|
715
|
+
});
|
|
716
|
+
React$1.useEffect(() => {
|
|
717
|
+
if (!isDraggingSlider) return;
|
|
718
|
+
const handlePointerMove = (e) => {
|
|
719
|
+
updateSliderFromPointerRef.current(e.clientX, e.clientY);
|
|
720
|
+
};
|
|
721
|
+
const handlePointerUp = () => {
|
|
722
|
+
setIsDraggingSlider(false);
|
|
723
|
+
};
|
|
724
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
725
|
+
window.addEventListener("pointerup", handlePointerUp);
|
|
726
|
+
window.addEventListener("pointercancel", handlePointerUp);
|
|
727
|
+
return () => {
|
|
728
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
729
|
+
window.removeEventListener("pointerup", handlePointerUp);
|
|
730
|
+
window.removeEventListener("pointercancel", handlePointerUp);
|
|
731
|
+
};
|
|
732
|
+
}, [isDraggingSlider]);
|
|
733
|
+
const handleAlphaPointerDown = (e) => {
|
|
734
|
+
e.preventDefault();
|
|
735
|
+
setIsDraggingAlpha(true);
|
|
736
|
+
updateAlphaFromPointer(e.clientX, e.clientY);
|
|
737
|
+
};
|
|
738
|
+
const updateAlphaFromPointer = (clientX, clientY) => {
|
|
739
|
+
if (!alphaSvgRef.current) return;
|
|
740
|
+
const rect = alphaSvgRef.current.getBoundingClientRect();
|
|
741
|
+
const centerX = rect.left + rect.width / 2;
|
|
742
|
+
const centerY = rect.top + rect.height / 2;
|
|
743
|
+
const dx = clientX - centerX;
|
|
744
|
+
const dy = clientY - centerY;
|
|
745
|
+
const alphaPosition = getOppositePosition(effectivePosition);
|
|
746
|
+
const val = calculateSliderValueFromPoint(dx, dy, getCenterAngle(alphaPosition), 30, alphaPosition === "bottom" ? "left" : "right");
|
|
747
|
+
updateValue({
|
|
748
|
+
...currentValue,
|
|
749
|
+
alpha: val
|
|
750
|
+
});
|
|
751
|
+
};
|
|
752
|
+
const updateAlphaFromPointerRef = React$1.useRef(updateAlphaFromPointer);
|
|
753
|
+
React$1.useEffect(() => {
|
|
754
|
+
updateAlphaFromPointerRef.current = updateAlphaFromPointer;
|
|
755
|
+
});
|
|
756
|
+
React$1.useEffect(() => {
|
|
757
|
+
if (!isDraggingAlpha) return;
|
|
758
|
+
const handlePointerMove = (e) => {
|
|
759
|
+
updateAlphaFromPointerRef.current(e.clientX, e.clientY);
|
|
760
|
+
};
|
|
761
|
+
const handlePointerUp = () => {
|
|
762
|
+
setIsDraggingAlpha(false);
|
|
763
|
+
};
|
|
764
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
765
|
+
window.addEventListener("pointerup", handlePointerUp);
|
|
766
|
+
window.addEventListener("pointercancel", handlePointerUp);
|
|
767
|
+
return () => {
|
|
768
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
769
|
+
window.removeEventListener("pointerup", handlePointerUp);
|
|
770
|
+
window.removeEventListener("pointercancel", handlePointerUp);
|
|
771
|
+
};
|
|
772
|
+
}, [isDraggingAlpha]);
|
|
773
|
+
const renderPetals = () => {
|
|
774
|
+
const totalLayers = layers.length;
|
|
775
|
+
const items = [];
|
|
776
|
+
for (let layerIdx = 0; layerIdx < totalLayers; layerIdx++) {
|
|
777
|
+
const layerColors = layers[layerIdx];
|
|
778
|
+
const radius = layerRadii[layerIdx];
|
|
779
|
+
const rotation = layerRotations[layerIdx];
|
|
780
|
+
const previousItemsCount = layerPrefixCounts[layerIdx];
|
|
781
|
+
const totalPetals = layerColors.length;
|
|
782
|
+
const baseZ = (totalLayers - layerIdx) * 100;
|
|
783
|
+
let bottomIndex = 0;
|
|
784
|
+
let minDiff = Infinity;
|
|
785
|
+
for (let i = 0; i < totalPetals; i++) {
|
|
786
|
+
const normalizedAngle = ((i / totalPetals * 360 - 90 + rotation) % 360 + 360) % 360;
|
|
787
|
+
const diff = Math.min(Math.abs(normalizedAngle - 90), 360 - Math.abs(normalizedAngle - 90));
|
|
788
|
+
if (diff < minDiff) {
|
|
789
|
+
minDiff = diff;
|
|
790
|
+
bottomIndex = i;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
for (let index = 0; index < totalPetals; index++) {
|
|
794
|
+
const color = layerColors[index];
|
|
795
|
+
const staggerDelay = previousItemsCount * PETAL_STAGGER + index * PETAL_STAGGER;
|
|
796
|
+
const isSelected = currentValue.hue === color.h;
|
|
797
|
+
const createPetalStyle = (clip, zIndexOverride, isInteraction = false) => {
|
|
798
|
+
const radian = (index / totalPetals * 360 - 90 + rotation) * Math.PI / 180;
|
|
799
|
+
let px = Math.cos(radian) * radius;
|
|
800
|
+
let py = Math.sin(radian) * radius;
|
|
801
|
+
const isHovered = hoveredPetal?.layer === layerIdx && hoveredPetal?.index === index;
|
|
802
|
+
if (isExpanded && mousePos && !isHovered && !isInteraction) {
|
|
803
|
+
const dx = px - mousePos.x;
|
|
804
|
+
const dy = py - mousePos.y;
|
|
805
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
806
|
+
const minDistance = 60;
|
|
807
|
+
if (dist < minDistance) {
|
|
808
|
+
const pushStrength = (1 - dist / minDistance) * 6;
|
|
809
|
+
const pushAngle = Math.atan2(dy, dx);
|
|
810
|
+
px += Math.cos(pushAngle) * pushStrength;
|
|
811
|
+
py += Math.sin(pushAngle) * pushStrength;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const scale = isHovered && !isInteraction ? 1.1 : 1;
|
|
815
|
+
return {
|
|
816
|
+
backgroundColor: hslaToString(color.h, color.s, color.l, isInteraction ? 0 : 100),
|
|
817
|
+
transform: isExpanded ? `translate(${px}px, ${py}px) scale(${scale})` : "translate(0px, 0px) scale(0)",
|
|
818
|
+
opacity: isExpanded ? 1 : 0,
|
|
819
|
+
filter: isHovered && !isInteraction ? "brightness(1.1)" : "brightness(1)",
|
|
820
|
+
boxShadow: isExpanded && !isInteraction ? isHovered ? "0 4px 12px rgba(0,0,0,0.25)" : "0 2px 6px rgba(0,0,0,0.15)" : "none",
|
|
821
|
+
zIndex: zIndexOverride ?? baseZ + index,
|
|
822
|
+
pointerEvents: isExpanded ? index === bottomIndex ? isInteraction ? "auto" : "none" : "auto" : "none",
|
|
823
|
+
clipPath: clip === "left" ? "polygon(0% -50%, 50% -50%, 50% 150%, 0% 150%)" : clip === "right" ? "polygon(50% -50%, 100% -50%, 100% 150%, 50% 150%)" : void 0,
|
|
824
|
+
transition: `
|
|
825
|
+
transform ${isExpanded && mousePos && !isHovered ? "150ms ease-out" : `${animationDuration}ms ${BLOOM_EASING_CSS} ${isExpanded && !isHovered ? staggerDelay : 0}ms`},
|
|
826
|
+
opacity ${animationDuration}ms ${BLOOM_EASING_CSS} ${isExpanded && !isHovered ? staggerDelay : 0}ms,
|
|
827
|
+
background-color 200ms ease,
|
|
828
|
+
box-shadow 200ms ease,
|
|
829
|
+
filter 200ms ease
|
|
830
|
+
`
|
|
831
|
+
};
|
|
832
|
+
};
|
|
833
|
+
if (index === bottomIndex) {
|
|
834
|
+
const uZ = baseZ - 1;
|
|
835
|
+
const lZ = getPetalZIndex(index, bottomIndex, totalPetals, layerIdx, totalLayers, true, false);
|
|
836
|
+
const rZ = getPetalZIndex(index, bottomIndex, totalPetals, layerIdx, totalLayers, false, true);
|
|
837
|
+
const iZ = baseZ + totalPetals + 20;
|
|
838
|
+
items.push(/* @__PURE__ */ jsx("div", {
|
|
839
|
+
className: "absolute rounded-full pointer-events-none",
|
|
840
|
+
style: {
|
|
841
|
+
width: `${petalSize}px`,
|
|
842
|
+
height: `${petalSize}px`,
|
|
843
|
+
marginLeft: `${-petalSize / 2}px`,
|
|
844
|
+
marginTop: `${-petalSize / 2}px`,
|
|
845
|
+
left: "50%",
|
|
846
|
+
top: "50%",
|
|
847
|
+
...createPetalStyle(void 0, uZ)
|
|
848
|
+
}
|
|
849
|
+
}, `${layerIdx}-${index}-underlay`), /* @__PURE__ */ jsx("div", {
|
|
850
|
+
className: cn("absolute rounded-full pointer-events-none", isSelected && isExpanded && "ring-2 ring-white ring-offset-2 ring-offset-background"),
|
|
851
|
+
style: {
|
|
852
|
+
width: `${petalSize}px`,
|
|
853
|
+
height: `${petalSize}px`,
|
|
854
|
+
marginLeft: `${-petalSize / 2}px`,
|
|
855
|
+
marginTop: `${-petalSize / 2}px`,
|
|
856
|
+
left: "50%",
|
|
857
|
+
top: "50%",
|
|
858
|
+
...createPetalStyle("left", lZ)
|
|
859
|
+
}
|
|
860
|
+
}, `${layerIdx}-${index}-left`), /* @__PURE__ */ jsx("div", {
|
|
861
|
+
className: cn("absolute rounded-full pointer-events-none", isSelected && isExpanded && "ring-2 ring-white ring-offset-2 ring-offset-background"),
|
|
862
|
+
style: {
|
|
863
|
+
width: `${petalSize}px`,
|
|
864
|
+
height: `${petalSize}px`,
|
|
865
|
+
marginLeft: `${-petalSize / 2}px`,
|
|
866
|
+
marginTop: `${-petalSize / 2}px`,
|
|
867
|
+
left: "50%",
|
|
868
|
+
top: "50%",
|
|
869
|
+
...createPetalStyle("right", rZ)
|
|
870
|
+
}
|
|
871
|
+
}, `${layerIdx}-${index}-right`), /* @__PURE__ */ jsx("button", {
|
|
872
|
+
type: "button",
|
|
873
|
+
tabIndex: isExpanded ? 0 : -1,
|
|
874
|
+
onClick: () => handlePetalClick(color, layerIdx),
|
|
875
|
+
onMouseEnter: () => setHoveredPetal({
|
|
876
|
+
layer: layerIdx,
|
|
877
|
+
index
|
|
878
|
+
}),
|
|
879
|
+
onMouseLeave: () => setHoveredPetal(null),
|
|
880
|
+
"aria-label": `Select color hue ${color.h}`,
|
|
881
|
+
className: "absolute rounded-full cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-white",
|
|
882
|
+
style: {
|
|
883
|
+
width: `${petalSize}px`,
|
|
884
|
+
height: `${petalSize}px`,
|
|
885
|
+
marginLeft: `${-petalSize / 2}px`,
|
|
886
|
+
marginTop: `${-petalSize / 2}px`,
|
|
887
|
+
left: "50%",
|
|
888
|
+
top: "50%",
|
|
889
|
+
...createPetalStyle(void 0, iZ, true)
|
|
890
|
+
}
|
|
891
|
+
}, `${layerIdx}-${index}-interaction`));
|
|
892
|
+
} else items.push(/* @__PURE__ */ jsx("button", {
|
|
893
|
+
type: "button",
|
|
894
|
+
tabIndex: isExpanded ? 0 : -1,
|
|
895
|
+
onClick: () => handlePetalClick(color, layerIdx),
|
|
896
|
+
onMouseEnter: () => setHoveredPetal({
|
|
897
|
+
layer: layerIdx,
|
|
898
|
+
index
|
|
899
|
+
}),
|
|
900
|
+
onMouseLeave: () => setHoveredPetal(null),
|
|
901
|
+
"aria-label": `Select color hue ${color.h}`,
|
|
902
|
+
className: cn("absolute rounded-full cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-white", isSelected && isExpanded && "ring-2 ring-white ring-offset-2 ring-offset-background"),
|
|
903
|
+
style: {
|
|
904
|
+
width: `${petalSize}px`,
|
|
905
|
+
height: `${petalSize}px`,
|
|
906
|
+
marginLeft: `${-petalSize / 2}px`,
|
|
907
|
+
marginTop: `${-petalSize / 2}px`,
|
|
908
|
+
left: "50%",
|
|
909
|
+
top: "50%",
|
|
910
|
+
...createPetalStyle(void 0, getPetalZIndex(index, bottomIndex, totalPetals, layerIdx, totalLayers))
|
|
911
|
+
}
|
|
912
|
+
}, `${layerIdx}-${index}`));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return items;
|
|
916
|
+
};
|
|
917
|
+
const renderArcSlider = () => {
|
|
918
|
+
if (!showAlphaSlider) return null;
|
|
919
|
+
const centerAngle = getCenterAngle(effectivePosition);
|
|
920
|
+
const drawStartAngle = centerAngle - 30;
|
|
921
|
+
const drawEndAngle = centerAngle + 30;
|
|
922
|
+
const size = (barRadius + sliderOffset + sliderWidth / 2 + circularBarWidth) * 2 + 20;
|
|
923
|
+
const center = size / 2;
|
|
924
|
+
const arcD = describeArc(center, center, barRadius + sliderOffset, drawStartAngle, drawEndAngle);
|
|
925
|
+
const valStartAngle = effectivePosition === "left" ? drawEndAngle : drawStartAngle;
|
|
926
|
+
const valEndAngle = effectivePosition === "left" ? drawStartAngle : drawEndAngle;
|
|
927
|
+
const gradStart = polarToCartesian(center, center, barRadius + sliderOffset, valStartAngle);
|
|
928
|
+
const gradEnd = polarToCartesian(center, center, barRadius + sliderOffset, valEndAngle);
|
|
929
|
+
const handleAngle = valStartAngle + currentValue.saturation / 100 * (valEndAngle - valStartAngle);
|
|
930
|
+
const handlePos = polarToCartesian(center, center, barRadius + sliderOffset, handleAngle);
|
|
931
|
+
const handleLightness = sliderValueToLightness(currentValue.saturation);
|
|
932
|
+
const handleSaturation = getVisualSaturation(currentValue.saturation, baseSaturation);
|
|
933
|
+
const handleColor = hslToString(currentValue.hue, handleSaturation, handleLightness);
|
|
934
|
+
const gradientColors = calculateArcGradientColors(currentValue.hue, baseSaturation, ARC_GRADIENT_STEPS);
|
|
935
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
936
|
+
ref: svgRef,
|
|
937
|
+
width: size,
|
|
938
|
+
height: size,
|
|
939
|
+
onPointerDown: handleSliderPointerDown,
|
|
940
|
+
className: "absolute pointer-events-none select-none",
|
|
941
|
+
style: {
|
|
942
|
+
left: "50%",
|
|
943
|
+
top: "50%",
|
|
944
|
+
marginLeft: `${-size / 2}px`,
|
|
945
|
+
marginTop: `${-size / 2}px`,
|
|
946
|
+
zIndex: 50,
|
|
947
|
+
opacity: isExpanded ? 1 : 0,
|
|
948
|
+
transform: isExpanded ? "scale(1)" : "scale(0.8)",
|
|
949
|
+
transition: `opacity ${animationDuration}ms ${BLOOM_EASING_CSS} ${animationDuration / 2}ms, transform ${animationDuration}ms ${BLOOM_EASING_CSS} ${animationDuration / 2}ms`
|
|
950
|
+
},
|
|
951
|
+
children: [
|
|
952
|
+
/* @__PURE__ */ jsx("title", { children: "Saturation & Lightness Arc Slider" }),
|
|
953
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("linearGradient", {
|
|
954
|
+
id: `bcp-arc-grad-${currentValue.hue}`,
|
|
955
|
+
gradientUnits: "userSpaceOnUse",
|
|
956
|
+
x1: gradStart.x,
|
|
957
|
+
y1: gradStart.y,
|
|
958
|
+
x2: gradEnd.x,
|
|
959
|
+
y2: gradEnd.y,
|
|
960
|
+
children: gradientColors.map((col, i) => /* @__PURE__ */ jsx("stop", {
|
|
961
|
+
offset: `${i / 10 * 100}%`,
|
|
962
|
+
stopColor: col
|
|
963
|
+
}, `${col}-${i}`))
|
|
964
|
+
}) }),
|
|
965
|
+
/* @__PURE__ */ jsx("path", {
|
|
966
|
+
d: arcD,
|
|
967
|
+
fill: "none",
|
|
968
|
+
stroke: "rgba(0,0,0,0.06)",
|
|
969
|
+
strokeWidth: sliderWidth,
|
|
970
|
+
strokeLinecap: "round"
|
|
971
|
+
}),
|
|
972
|
+
/* @__PURE__ */ jsx("path", {
|
|
973
|
+
d: arcD,
|
|
974
|
+
fill: "none",
|
|
975
|
+
stroke: `url(#bcp-arc-grad-${currentValue.hue})`,
|
|
976
|
+
strokeWidth: sliderWidth,
|
|
977
|
+
strokeLinecap: "round",
|
|
978
|
+
className: cn("touch-none", isExpanded ? "pointer-events-auto cursor-pointer" : "pointer-events-none")
|
|
979
|
+
}),
|
|
980
|
+
/* @__PURE__ */ jsx("circle", {
|
|
981
|
+
cx: handlePos.x,
|
|
982
|
+
cy: handlePos.y,
|
|
983
|
+
r: sliderWidth / 2,
|
|
984
|
+
fill: handleColor,
|
|
985
|
+
stroke: "white",
|
|
986
|
+
strokeWidth: "2",
|
|
987
|
+
className: cn("touch-none active:cursor-grabbing shadow-[0_1px_4px_rgba(0,0,0,0.3)]", isExpanded ? "pointer-events-auto cursor-grab" : "pointer-events-none"),
|
|
988
|
+
style: { transition: isDraggingSlider ? "none" : `cx ${animationDuration / 3}ms ease, cy ${animationDuration / 3}ms ease` }
|
|
989
|
+
})
|
|
990
|
+
]
|
|
991
|
+
});
|
|
992
|
+
};
|
|
993
|
+
const renderAlphaSlider = () => {
|
|
994
|
+
if (!showOpacitySlider) return null;
|
|
995
|
+
const alphaPosition = getOppositePosition(effectivePosition);
|
|
996
|
+
const centerAngle = getCenterAngle(alphaPosition);
|
|
997
|
+
const drawStartAngle = centerAngle - 30;
|
|
998
|
+
const drawEndAngle = centerAngle + 30;
|
|
999
|
+
const size = (barRadius + sliderOffset + sliderWidth / 2 + circularBarWidth) * 2 + 20;
|
|
1000
|
+
const center = size / 2;
|
|
1001
|
+
const arcD = describeArc(center, center, barRadius + sliderOffset, drawStartAngle, drawEndAngle);
|
|
1002
|
+
const valStartAngle = alphaPosition === "bottom" ? drawEndAngle : drawStartAngle;
|
|
1003
|
+
const valEndAngle = alphaPosition === "bottom" ? drawStartAngle : drawEndAngle;
|
|
1004
|
+
const gradStart = polarToCartesian(center, center, barRadius + sliderOffset, valStartAngle);
|
|
1005
|
+
const gradEnd = polarToCartesian(center, center, barRadius + sliderOffset, valEndAngle);
|
|
1006
|
+
const handleAngle = valStartAngle + currentValue.alpha / 100 * (valEndAngle - valStartAngle);
|
|
1007
|
+
const handlePos = polarToCartesian(center, center, barRadius + sliderOffset, handleAngle);
|
|
1008
|
+
const colorStart = hslaToString(currentValue.hue, visualSaturation, currentLightness, 0);
|
|
1009
|
+
const colorEnd = hslaToString(currentValue.hue, visualSaturation, currentLightness, 100);
|
|
1010
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
1011
|
+
ref: alphaSvgRef,
|
|
1012
|
+
width: size,
|
|
1013
|
+
height: size,
|
|
1014
|
+
onPointerDown: handleAlphaPointerDown,
|
|
1015
|
+
className: "absolute pointer-events-none select-none",
|
|
1016
|
+
style: {
|
|
1017
|
+
left: "50%",
|
|
1018
|
+
top: "50%",
|
|
1019
|
+
marginLeft: `${-size / 2}px`,
|
|
1020
|
+
marginTop: `${-size / 2}px`,
|
|
1021
|
+
zIndex: 51,
|
|
1022
|
+
opacity: isExpanded ? 1 : 0,
|
|
1023
|
+
transform: isExpanded ? "scale(1)" : "scale(0.8)",
|
|
1024
|
+
transition: `opacity ${animationDuration}ms ${BLOOM_EASING_CSS} ${animationDuration / 2}ms, transform ${animationDuration}ms ${BLOOM_EASING_CSS} ${animationDuration / 2}ms`
|
|
1025
|
+
},
|
|
1026
|
+
children: [
|
|
1027
|
+
/* @__PURE__ */ jsx("title", { children: "Alpha Opacity Arc Slider" }),
|
|
1028
|
+
/* @__PURE__ */ jsxs("defs", { children: [/* @__PURE__ */ jsxs("pattern", {
|
|
1029
|
+
id: "bcp-checkerboard",
|
|
1030
|
+
width: "8",
|
|
1031
|
+
height: "8",
|
|
1032
|
+
patternUnits: "userSpaceOnUse",
|
|
1033
|
+
children: [
|
|
1034
|
+
/* @__PURE__ */ jsx("rect", {
|
|
1035
|
+
width: "8",
|
|
1036
|
+
height: "8",
|
|
1037
|
+
fill: "white"
|
|
1038
|
+
}),
|
|
1039
|
+
/* @__PURE__ */ jsx("rect", {
|
|
1040
|
+
width: "4",
|
|
1041
|
+
height: "4",
|
|
1042
|
+
fill: "#e2e8f0"
|
|
1043
|
+
}),
|
|
1044
|
+
/* @__PURE__ */ jsx("rect", {
|
|
1045
|
+
x: "4",
|
|
1046
|
+
y: "4",
|
|
1047
|
+
width: "4",
|
|
1048
|
+
height: "4",
|
|
1049
|
+
fill: "#e2e8f0"
|
|
1050
|
+
})
|
|
1051
|
+
]
|
|
1052
|
+
}), /* @__PURE__ */ jsxs("linearGradient", {
|
|
1053
|
+
id: `bcp-alpha-grad-${currentValue.hue}`,
|
|
1054
|
+
gradientUnits: "userSpaceOnUse",
|
|
1055
|
+
x1: gradStart.x,
|
|
1056
|
+
y1: gradStart.y,
|
|
1057
|
+
x2: gradEnd.x,
|
|
1058
|
+
y2: gradEnd.y,
|
|
1059
|
+
children: [/* @__PURE__ */ jsx("stop", {
|
|
1060
|
+
offset: "0%",
|
|
1061
|
+
stopColor: colorStart
|
|
1062
|
+
}), /* @__PURE__ */ jsx("stop", {
|
|
1063
|
+
offset: "100%",
|
|
1064
|
+
stopColor: colorEnd
|
|
1065
|
+
})]
|
|
1066
|
+
})] }),
|
|
1067
|
+
/* @__PURE__ */ jsx("path", {
|
|
1068
|
+
d: arcD,
|
|
1069
|
+
fill: "none",
|
|
1070
|
+
stroke: "url(#bcp-checkerboard)",
|
|
1071
|
+
strokeWidth: sliderWidth,
|
|
1072
|
+
strokeLinecap: "round"
|
|
1073
|
+
}),
|
|
1074
|
+
/* @__PURE__ */ jsx("path", {
|
|
1075
|
+
d: arcD,
|
|
1076
|
+
fill: "none",
|
|
1077
|
+
stroke: `url(#bcp-alpha-grad-${currentValue.hue})`,
|
|
1078
|
+
strokeWidth: sliderWidth,
|
|
1079
|
+
strokeLinecap: "round",
|
|
1080
|
+
className: cn("touch-none", isExpanded ? "pointer-events-auto cursor-pointer" : "pointer-events-none")
|
|
1081
|
+
}),
|
|
1082
|
+
/* @__PURE__ */ jsx("circle", {
|
|
1083
|
+
cx: handlePos.x,
|
|
1084
|
+
cy: handlePos.y,
|
|
1085
|
+
r: sliderWidth / 2,
|
|
1086
|
+
fill: colorEnd,
|
|
1087
|
+
stroke: "white",
|
|
1088
|
+
strokeWidth: "2",
|
|
1089
|
+
className: cn("touch-none active:cursor-grabbing shadow-[0_1px_4px_rgba(0,0,0,0.3)]", isExpanded ? "pointer-events-auto cursor-grab" : "pointer-events-none"),
|
|
1090
|
+
style: { transition: isDraggingAlpha ? "none" : `cx ${animationDuration / 3}ms ease, cy ${animationDuration / 3}ms ease` }
|
|
1091
|
+
})
|
|
1092
|
+
]
|
|
1093
|
+
});
|
|
1094
|
+
};
|
|
1095
|
+
const renderColorBar = () => {
|
|
1096
|
+
const size = (barRadius + circularBarWidth / 2) * 2 + 4;
|
|
1097
|
+
const strokeColor = hslaToString(currentValue.hue, visualSaturation, currentLightness, currentValue.alpha);
|
|
1098
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
1099
|
+
width: size,
|
|
1100
|
+
height: size,
|
|
1101
|
+
className: "absolute pointer-events-none",
|
|
1102
|
+
style: {
|
|
1103
|
+
left: "50%",
|
|
1104
|
+
top: "50%",
|
|
1105
|
+
marginLeft: `${-size / 2}px`,
|
|
1106
|
+
marginTop: `${-size / 2}px`,
|
|
1107
|
+
zIndex: 5,
|
|
1108
|
+
opacity: isExpanded ? 1 : 0,
|
|
1109
|
+
transform: isExpanded ? "scale(1)" : "scale(0.8)",
|
|
1110
|
+
transition: `opacity ${animationDuration}ms ${BLOOM_EASING_CSS}, transform ${animationDuration}ms ${BLOOM_EASING_CSS}`
|
|
1111
|
+
},
|
|
1112
|
+
children: [
|
|
1113
|
+
/* @__PURE__ */ jsx("title", { children: "Current Color Ring" }),
|
|
1114
|
+
/* @__PURE__ */ jsx("circle", {
|
|
1115
|
+
cx: size / 2,
|
|
1116
|
+
cy: size / 2,
|
|
1117
|
+
r: barRadius,
|
|
1118
|
+
fill: "none",
|
|
1119
|
+
stroke: "rgba(0,0,0,0.06)",
|
|
1120
|
+
strokeWidth: circularBarWidth
|
|
1121
|
+
}),
|
|
1122
|
+
/* @__PURE__ */ jsx("circle", {
|
|
1123
|
+
cx: size / 2,
|
|
1124
|
+
cy: size / 2,
|
|
1125
|
+
r: barRadius,
|
|
1126
|
+
fill: "none",
|
|
1127
|
+
stroke: strokeColor,
|
|
1128
|
+
strokeWidth: circularBarWidth
|
|
1129
|
+
})
|
|
1130
|
+
]
|
|
1131
|
+
});
|
|
1132
|
+
};
|
|
1133
|
+
if (!mounted) {
|
|
1134
|
+
const size = collapsible && !initialExpanded ? coreSize : containerSize;
|
|
1135
|
+
const initialH = defaultValue?.hue ?? DEFAULT_VALUE.hue;
|
|
1136
|
+
const initialS = defaultValue?.saturation ?? DEFAULT_VALUE.saturation;
|
|
1137
|
+
const initialBgColor = `hsla(${initialH}, ${initialS}%, ${defaultValue?.lightness ?? sliderValueToLightness(initialS)}%, ${(defaultValue?.alpha ?? DEFAULT_VALUE.alpha) / 100})`;
|
|
1138
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1139
|
+
ref: rootRef,
|
|
1140
|
+
role: "group",
|
|
1141
|
+
"aria-label": "Color picker",
|
|
1142
|
+
className: cn("relative inline-flex items-center justify-center select-none", className),
|
|
1143
|
+
style: {
|
|
1144
|
+
width: `${size}px`,
|
|
1145
|
+
height: `${size}px`
|
|
1146
|
+
},
|
|
1147
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1148
|
+
className: "relative rounded-full border border-black/5 dark:border-white/10",
|
|
1149
|
+
style: {
|
|
1150
|
+
width: `${coreSize}px`,
|
|
1151
|
+
height: `${coreSize}px`,
|
|
1152
|
+
backgroundColor: initialBgColor
|
|
1153
|
+
}
|
|
1154
|
+
})
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1158
|
+
ref: rootRef,
|
|
1159
|
+
role: "group",
|
|
1160
|
+
"aria-label": "Color picker",
|
|
1161
|
+
...props,
|
|
1162
|
+
onMouseEnter: handleMouseEnter,
|
|
1163
|
+
onMouseMove: handleMouseMove,
|
|
1164
|
+
onMouseLeave: handleMouseLeave,
|
|
1165
|
+
className: cn("relative inline-flex items-center justify-center select-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
|
|
1166
|
+
style: {
|
|
1167
|
+
width: isExpanded ? `${containerSize}px` : `${coreSize}px`,
|
|
1168
|
+
height: isExpanded ? `${containerSize}px` : `${coreSize}px`,
|
|
1169
|
+
transition: `width ${animationDuration}ms ${BLOOM_EASING_CSS}, height ${animationDuration}ms ${BLOOM_EASING_CSS}`
|
|
1170
|
+
},
|
|
1171
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1172
|
+
className: "absolute flex items-center justify-center",
|
|
1173
|
+
style: {
|
|
1174
|
+
width: isExpanded ? `${containerSize}px` : `${coreSize}px`,
|
|
1175
|
+
height: isExpanded ? `${containerSize}px` : `${coreSize}px`,
|
|
1176
|
+
transform: `translate(calc(-50% + ${shiftOffset.x}px), calc(-50% + ${shiftOffset.y}px))`,
|
|
1177
|
+
transition: `width ${animationDuration}ms ${BLOOM_EASING_CSS}, height ${animationDuration}ms ${BLOOM_EASING_CSS}, transform ${animationDuration}ms ${BLOOM_EASING_CSS}`,
|
|
1178
|
+
left: "50%",
|
|
1179
|
+
top: "50%",
|
|
1180
|
+
zIndex: isExpanded ? 50 : 0
|
|
1181
|
+
},
|
|
1182
|
+
children: [
|
|
1183
|
+
renderColorBar(),
|
|
1184
|
+
renderPetals(),
|
|
1185
|
+
renderArcSlider(),
|
|
1186
|
+
renderAlphaSlider(),
|
|
1187
|
+
/* @__PURE__ */ jsx("button", {
|
|
1188
|
+
type: "button",
|
|
1189
|
+
disabled,
|
|
1190
|
+
onClick: handleCoreClick,
|
|
1191
|
+
onMouseEnter: () => setIsHoveringCore(true),
|
|
1192
|
+
onMouseLeave: () => setIsHoveringCore(false),
|
|
1193
|
+
"aria-label": isExpanded && hasEyeDropper ? "Pick color from screen" : `Color picker${isExpanded ? ", expanded" : ""}`,
|
|
1194
|
+
"aria-expanded": isExpanded,
|
|
1195
|
+
className: cn("relative rounded-full cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:opacity-64 disabled:cursor-not-allowed border border-black/5 dark:border-white/10 flex items-center justify-center text-white"),
|
|
1196
|
+
style: {
|
|
1197
|
+
width: `${coreSize}px`,
|
|
1198
|
+
height: `${coreSize}px`,
|
|
1199
|
+
backgroundColor: coreColorString,
|
|
1200
|
+
transform: isExpanded ? "scale(1)" : isHoveringCore ? "scale(1.08)" : "scale(1)",
|
|
1201
|
+
boxShadow: isExpanded ? "0 0 0 2px rgba(0,0,0,0.1), 0 4px 12px rgba(0,0,0,0.15)" : "0 2px 8px rgba(0,0,0,0.15)",
|
|
1202
|
+
transition: `transform ${animationDuration}ms ${BLOOM_EASING_CSS}, box-shadow ${animationDuration}ms ${BLOOM_EASING_CSS}, background-color 200ms ease`,
|
|
1203
|
+
zIndex: 1e3
|
|
1204
|
+
},
|
|
1205
|
+
children: isExpanded && hasEyeDropper && /* @__PURE__ */ jsx(Dropper, {
|
|
1206
|
+
className: "size-4 shrink-0 transition-opacity duration-200",
|
|
1207
|
+
style: { color: currentLightness > 70 ? "rgba(0,0,0,0.6)" : "white" }
|
|
1208
|
+
})
|
|
1209
|
+
})
|
|
1210
|
+
]
|
|
1211
|
+
})
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
function ColorPicker({ value, defaultValue, colors, onChange, onCollapse, disabled = false, openOnHover = false, initialExpanded = true, animationDuration = 300, showAlphaSlider = true, coreSize = 36, petalSize = 32, showCoreColor = true, sliderPosition, adaptivePositioning = true, circularBarWidth = BAR_WIDTH, sliderWidth = BAR_WIDTH, sliderOffset = SLIDER_OFFSET, collapsible = false, darkMode, className, ref, ...props }) {
|
|
1215
|
+
const [colorMode, setColorMode] = React$1.useState("HEX");
|
|
1216
|
+
const [internalPickerVal, setInternalPickerVal] = React$1.useState(defaultValue ?? {
|
|
1217
|
+
hue: 210,
|
|
1218
|
+
saturation: 50,
|
|
1219
|
+
alpha: 100,
|
|
1220
|
+
layer: "outer"
|
|
1221
|
+
});
|
|
1222
|
+
const currentValue = value ?? internalPickerVal;
|
|
1223
|
+
const normalizedColors = React$1.useMemo(() => {
|
|
1224
|
+
return colors && colors.length > 0 ? colors.map(parseColor) : DEFAULT_COLORS;
|
|
1225
|
+
}, [colors]);
|
|
1226
|
+
const baseSaturation = React$1.useMemo(() => {
|
|
1227
|
+
if (currentValue.originalSaturation !== void 0) return currentValue.originalSaturation;
|
|
1228
|
+
return normalizedColors.find((c) => c.h === currentValue.hue)?.s ?? 70;
|
|
1229
|
+
}, [
|
|
1230
|
+
normalizedColors,
|
|
1231
|
+
currentValue.hue,
|
|
1232
|
+
currentValue.originalSaturation
|
|
1233
|
+
]);
|
|
1234
|
+
const currentLightness = React$1.useMemo(() => {
|
|
1235
|
+
return currentValue.lightness ?? sliderValueToLightness(currentValue.saturation);
|
|
1236
|
+
}, [currentValue.lightness, currentValue.saturation]);
|
|
1237
|
+
const visualSaturation = React$1.useMemo(() => {
|
|
1238
|
+
return getVisualSaturation(currentValue.saturation, baseSaturation);
|
|
1239
|
+
}, [currentValue.saturation, baseSaturation]);
|
|
1240
|
+
const parsedOutput = React$1.useMemo(() => {
|
|
1241
|
+
return createColorOutput(currentValue.hue, currentValue.saturation, visualSaturation, baseSaturation, currentLightness, currentValue.alpha, currentValue.layer);
|
|
1242
|
+
}, [
|
|
1243
|
+
currentValue,
|
|
1244
|
+
visualSaturation,
|
|
1245
|
+
baseSaturation,
|
|
1246
|
+
currentLightness
|
|
1247
|
+
]);
|
|
1248
|
+
const [hexInput, setHexInput] = React$1.useState(parsedOutput.hex.toUpperCase());
|
|
1249
|
+
const [rgbaInputs, setRgbaInputs] = React$1.useState({
|
|
1250
|
+
r: String(parsedOutput.r),
|
|
1251
|
+
g: String(parsedOutput.g),
|
|
1252
|
+
b: String(parsedOutput.b),
|
|
1253
|
+
a: String(parsedOutput.alpha === 100 ? 1 : (parsedOutput.alpha / 100).toFixed(2))
|
|
1254
|
+
});
|
|
1255
|
+
const [hslaInputs, setHslaInputs] = React$1.useState({
|
|
1256
|
+
h: String(Math.round(parsedOutput.hue)),
|
|
1257
|
+
s: String(Math.round(parsedOutput.saturation)),
|
|
1258
|
+
l: String(Math.round(parsedOutput.lightness ?? 50)),
|
|
1259
|
+
a: String(parsedOutput.alpha === 100 ? 1 : (parsedOutput.alpha / 100).toFixed(2))
|
|
1260
|
+
});
|
|
1261
|
+
React$1.useEffect(() => {
|
|
1262
|
+
setHexInput(parsedOutput.hex.toUpperCase());
|
|
1263
|
+
setRgbaInputs({
|
|
1264
|
+
r: String(parsedOutput.r),
|
|
1265
|
+
g: String(parsedOutput.g),
|
|
1266
|
+
b: String(parsedOutput.b),
|
|
1267
|
+
a: String(parsedOutput.alpha === 100 ? 1 : (parsedOutput.alpha / 100).toFixed(2))
|
|
1268
|
+
});
|
|
1269
|
+
setHslaInputs({
|
|
1270
|
+
h: String(Math.round(parsedOutput.hue)),
|
|
1271
|
+
s: String(Math.round(parsedOutput.saturation)),
|
|
1272
|
+
l: String(Math.round(parsedOutput.lightness ?? 50)),
|
|
1273
|
+
a: String(parsedOutput.alpha === 100 ? 1 : (parsedOutput.alpha / 100).toFixed(2))
|
|
1274
|
+
});
|
|
1275
|
+
}, [parsedOutput]);
|
|
1276
|
+
const updateColor = (nextVal) => {
|
|
1277
|
+
if (!value) setInternalPickerVal(nextVal);
|
|
1278
|
+
if (onChange) {
|
|
1279
|
+
const newBaseSaturation = nextVal.originalSaturation ?? normalizedColors.find((c) => c.h === nextVal.hue)?.s ?? 70;
|
|
1280
|
+
const nextLightness = nextVal.lightness ?? sliderValueToLightness(nextVal.saturation);
|
|
1281
|
+
const nextVisualSat = getVisualSaturation(nextVal.saturation, newBaseSaturation);
|
|
1282
|
+
const { r, g, b } = hslToRgb(nextVal.hue, nextVisualSat, nextLightness);
|
|
1283
|
+
onChange({
|
|
1284
|
+
...nextVal,
|
|
1285
|
+
originalSaturation: newBaseSaturation,
|
|
1286
|
+
lightness: nextLightness,
|
|
1287
|
+
hex: hslToHex(nextVal.hue, nextVisualSat, nextLightness),
|
|
1288
|
+
hsl: hslToString(nextVal.hue, nextVisualSat, nextLightness),
|
|
1289
|
+
hsla: hslaToString(nextVal.hue, nextVisualSat, nextLightness, nextVal.alpha),
|
|
1290
|
+
rgb: `rgb(${r}, ${g}, ${b})`,
|
|
1291
|
+
rgba: rgbaToString(r, g, b, nextVal.alpha),
|
|
1292
|
+
r,
|
|
1293
|
+
g,
|
|
1294
|
+
b
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
const handleRadialPickerChange = (color) => {
|
|
1299
|
+
updateColor({
|
|
1300
|
+
hue: color.hue,
|
|
1301
|
+
saturation: color.saturation,
|
|
1302
|
+
lightness: color.lightness,
|
|
1303
|
+
originalSaturation: color.originalSaturation,
|
|
1304
|
+
alpha: currentValue.alpha,
|
|
1305
|
+
layer: color.layer
|
|
1306
|
+
});
|
|
1307
|
+
};
|
|
1308
|
+
const handleAlphaChange = (nextAlpha) => {
|
|
1309
|
+
updateColor({
|
|
1310
|
+
...currentValue,
|
|
1311
|
+
alpha: nextAlpha
|
|
1312
|
+
});
|
|
1313
|
+
};
|
|
1314
|
+
const toggleMode = () => {
|
|
1315
|
+
setColorMode((prev) => {
|
|
1316
|
+
if (prev === "HEX") return "RGBA";
|
|
1317
|
+
if (prev === "RGBA") return "HSLA";
|
|
1318
|
+
return "HEX";
|
|
1319
|
+
});
|
|
1320
|
+
};
|
|
1321
|
+
const handleHexInputChange = (val) => {
|
|
1322
|
+
setHexInput(val);
|
|
1323
|
+
if (/^#[0-9A-F]{6}$/i.test(val) || /^[0-9A-F]{6}$/i.test(val)) {
|
|
1324
|
+
const hsl = hexToHsl(val.startsWith("#") ? val : `#${val}`);
|
|
1325
|
+
updateColor({
|
|
1326
|
+
hue: hsl.h,
|
|
1327
|
+
saturation: lightnessToSliderValue(hsl.l),
|
|
1328
|
+
lightness: hsl.l,
|
|
1329
|
+
originalSaturation: hsl.s,
|
|
1330
|
+
alpha: currentValue.alpha,
|
|
1331
|
+
layer: currentValue.layer
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
const handleRgbaInputChange = (key, val) => {
|
|
1336
|
+
const nextRgba = {
|
|
1337
|
+
...rgbaInputs,
|
|
1338
|
+
[key]: val
|
|
1339
|
+
};
|
|
1340
|
+
setRgbaInputs(nextRgba);
|
|
1341
|
+
const r = parseInt(nextRgba.r, 10);
|
|
1342
|
+
const g = parseInt(nextRgba.g, 10);
|
|
1343
|
+
const b = parseInt(nextRgba.b, 10);
|
|
1344
|
+
const a = parseFloat(nextRgba.a);
|
|
1345
|
+
if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
|
|
1346
|
+
const hsl = rgbToHsl(r, g, b);
|
|
1347
|
+
updateColor({
|
|
1348
|
+
hue: hsl.h,
|
|
1349
|
+
saturation: lightnessToSliderValue(hsl.l),
|
|
1350
|
+
lightness: hsl.l,
|
|
1351
|
+
originalSaturation: hsl.s,
|
|
1352
|
+
alpha: Number.isNaN(a) ? currentValue.alpha : Math.max(0, Math.min(100, a * 100)),
|
|
1353
|
+
layer: currentValue.layer
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
const handleHslaInputChange = (key, val) => {
|
|
1358
|
+
const nextHsla = {
|
|
1359
|
+
...hslaInputs,
|
|
1360
|
+
[key]: val
|
|
1361
|
+
};
|
|
1362
|
+
setHslaInputs(nextHsla);
|
|
1363
|
+
const h = parseInt(nextHsla.h, 10);
|
|
1364
|
+
const s = parseInt(nextHsla.s, 10);
|
|
1365
|
+
const l = parseInt(nextHsla.l, 10);
|
|
1366
|
+
const a = parseFloat(nextHsla.a);
|
|
1367
|
+
if (!Number.isNaN(h) && !Number.isNaN(s) && !Number.isNaN(l)) updateColor({
|
|
1368
|
+
hue: Math.max(0, Math.min(360, h)),
|
|
1369
|
+
saturation: Math.max(0, Math.min(100, s)),
|
|
1370
|
+
lightness: Math.max(0, Math.min(100, l)),
|
|
1371
|
+
originalSaturation: baseSaturation,
|
|
1372
|
+
alpha: Number.isNaN(a) ? currentValue.alpha : Math.max(0, Math.min(100, a * 100)),
|
|
1373
|
+
layer: currentValue.layer
|
|
1374
|
+
});
|
|
1375
|
+
};
|
|
1376
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1377
|
+
ref,
|
|
1378
|
+
"data-slot": "color-picker",
|
|
1379
|
+
...props,
|
|
1380
|
+
className: cn("inline-flex flex-col rounded-2xl border bg-card text-card-foreground shadow-xs/5 overflow-hidden w-[250px] select-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", darkMode && "dark", className),
|
|
1381
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1382
|
+
className: "flex justify-center items-center pt-10 pb-[calc(--spacing(10)-1px)] px-4 bg-muted/20 border-b",
|
|
1383
|
+
children: /* @__PURE__ */ jsx(BlossomColorPicker, {
|
|
1384
|
+
value: currentValue,
|
|
1385
|
+
colors,
|
|
1386
|
+
onChange: handleRadialPickerChange,
|
|
1387
|
+
onCollapse,
|
|
1388
|
+
disabled,
|
|
1389
|
+
openOnHover,
|
|
1390
|
+
initialExpanded,
|
|
1391
|
+
animationDuration,
|
|
1392
|
+
showAlphaSlider,
|
|
1393
|
+
showOpacitySlider: false,
|
|
1394
|
+
coreSize,
|
|
1395
|
+
petalSize,
|
|
1396
|
+
showCoreColor,
|
|
1397
|
+
sliderPosition,
|
|
1398
|
+
adaptivePositioning,
|
|
1399
|
+
circularBarWidth,
|
|
1400
|
+
sliderWidth,
|
|
1401
|
+
sliderOffset,
|
|
1402
|
+
collapsible
|
|
1403
|
+
})
|
|
1404
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1405
|
+
className: "p-4 flex flex-col gap-4",
|
|
1406
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1407
|
+
className: "flex items-center gap-3",
|
|
1408
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1409
|
+
className: "relative size-9 rounded-full border border-black/5 dark:border-white/10 overflow-hidden shrink-0",
|
|
1410
|
+
children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[linear-gradient(45deg,#ccc_25%,transparent_25%),linear-gradient(-45deg,#ccc_25%,transparent_25%),linear-gradient(45deg,transparent_75%,#ccc_75%),linear-gradient(-45deg,transparent_75%,#ccc_75%)] bg-size-[8px_8px] bg-position-[0_0,0_4px,4px_-4px,-4px_0px] bg-white opacity-40" }), /* @__PURE__ */ jsx("div", {
|
|
1411
|
+
className: "absolute inset-0 rounded-full shadow-inner",
|
|
1412
|
+
style: { backgroundColor: parsedOutput.rgba }
|
|
1413
|
+
})]
|
|
1414
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1415
|
+
className: "relative flex-1 h-3 flex items-center",
|
|
1416
|
+
children: [
|
|
1417
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1418
|
+
className: "relative w-full h-2.5 rounded-full border border-black/5 overflow-hidden",
|
|
1419
|
+
children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-[linear-gradient(45deg,#ccc_25%,transparent_25%),linear-gradient(-45deg,#ccc_25%,transparent_25%),linear-gradient(45deg,transparent_75%,#ccc_75%),linear-gradient(-45deg,transparent_75%,#ccc_75%)] bg-size-[8px_8px] bg-position-[0_0,0_4px,4px_-4px,-4px_0px] bg-white opacity-40" }), /* @__PURE__ */ jsx("div", {
|
|
1420
|
+
className: "absolute inset-0",
|
|
1421
|
+
style: { background: `linear-gradient(to right, rgba(${parsedOutput.r}, ${parsedOutput.g}, ${parsedOutput.b}, 0), rgb(${parsedOutput.r}, ${parsedOutput.g}, ${parsedOutput.b}))` }
|
|
1422
|
+
})]
|
|
1423
|
+
}),
|
|
1424
|
+
/* @__PURE__ */ jsx("div", {
|
|
1425
|
+
className: "absolute size-3.5 -ml-1.75 bg-card border border-input shadow-xs/5 rounded-full pointer-events-none top-1/2 -translate-y-1/2",
|
|
1426
|
+
style: { left: `${currentValue.alpha}%` }
|
|
1427
|
+
}),
|
|
1428
|
+
/* @__PURE__ */ jsx("input", {
|
|
1429
|
+
type: "range",
|
|
1430
|
+
"aria-label": "Opacity",
|
|
1431
|
+
min: 0,
|
|
1432
|
+
max: 100,
|
|
1433
|
+
disabled,
|
|
1434
|
+
value: currentValue.alpha,
|
|
1435
|
+
onChange: (e) => handleAlphaChange(Number(e.target.value)),
|
|
1436
|
+
className: "absolute inset-0 opacity-0 cursor-pointer w-full h-full disabled:cursor-not-allowed"
|
|
1437
|
+
})
|
|
1438
|
+
]
|
|
1439
|
+
})]
|
|
1440
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1441
|
+
className: "flex gap-2 items-start",
|
|
1442
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1443
|
+
className: "flex-1 flex gap-1.5 min-w-0",
|
|
1444
|
+
children: [
|
|
1445
|
+
colorMode === "HEX" && /* @__PURE__ */ jsxs("div", {
|
|
1446
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1447
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1448
|
+
type: "text",
|
|
1449
|
+
"aria-label": "Hex color",
|
|
1450
|
+
disabled,
|
|
1451
|
+
value: hexInput,
|
|
1452
|
+
onChange: (e) => handleHexInputChange(e.target.value),
|
|
1453
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 px-[calc(--spacing(2)-1px)] py-[calc(--spacing(1)-1px)] text-center font-mono text-xs uppercase shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1454
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1455
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1456
|
+
children: "Hex"
|
|
1457
|
+
})]
|
|
1458
|
+
}),
|
|
1459
|
+
colorMode === "RGBA" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1460
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1461
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1462
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1463
|
+
type: "text",
|
|
1464
|
+
"aria-label": "Red",
|
|
1465
|
+
disabled,
|
|
1466
|
+
value: rgbaInputs.r,
|
|
1467
|
+
onChange: (e) => handleRgbaInputChange("r", e.target.value),
|
|
1468
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1469
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1470
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1471
|
+
children: "R"
|
|
1472
|
+
})]
|
|
1473
|
+
}),
|
|
1474
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1475
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1476
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1477
|
+
type: "text",
|
|
1478
|
+
"aria-label": "Green",
|
|
1479
|
+
disabled,
|
|
1480
|
+
value: rgbaInputs.g,
|
|
1481
|
+
onChange: (e) => handleRgbaInputChange("g", e.target.value),
|
|
1482
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1483
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1484
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1485
|
+
children: "G"
|
|
1486
|
+
})]
|
|
1487
|
+
}),
|
|
1488
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1489
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1490
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1491
|
+
type: "text",
|
|
1492
|
+
"aria-label": "Blue",
|
|
1493
|
+
disabled,
|
|
1494
|
+
value: rgbaInputs.b,
|
|
1495
|
+
onChange: (e) => handleRgbaInputChange("b", e.target.value),
|
|
1496
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1497
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1498
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1499
|
+
children: "B"
|
|
1500
|
+
})]
|
|
1501
|
+
}),
|
|
1502
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1503
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1504
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1505
|
+
type: "text",
|
|
1506
|
+
"aria-label": "Alpha",
|
|
1507
|
+
disabled,
|
|
1508
|
+
value: rgbaInputs.a,
|
|
1509
|
+
onChange: (e) => handleRgbaInputChange("a", e.target.value),
|
|
1510
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1511
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1512
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1513
|
+
children: "A"
|
|
1514
|
+
})]
|
|
1515
|
+
})
|
|
1516
|
+
] }),
|
|
1517
|
+
colorMode === "HSLA" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1518
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1519
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1520
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1521
|
+
type: "text",
|
|
1522
|
+
"aria-label": "Hue",
|
|
1523
|
+
disabled,
|
|
1524
|
+
value: hslaInputs.h,
|
|
1525
|
+
onChange: (e) => handleHslaInputChange("h", e.target.value),
|
|
1526
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1527
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1528
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1529
|
+
children: "H"
|
|
1530
|
+
})]
|
|
1531
|
+
}),
|
|
1532
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1533
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1534
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1535
|
+
type: "text",
|
|
1536
|
+
"aria-label": "Saturation",
|
|
1537
|
+
disabled,
|
|
1538
|
+
value: hslaInputs.s,
|
|
1539
|
+
onChange: (e) => handleHslaInputChange("s", e.target.value),
|
|
1540
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1541
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1542
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1543
|
+
children: "S"
|
|
1544
|
+
})]
|
|
1545
|
+
}),
|
|
1546
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1547
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1548
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1549
|
+
type: "text",
|
|
1550
|
+
"aria-label": "Lightness",
|
|
1551
|
+
disabled,
|
|
1552
|
+
value: hslaInputs.l,
|
|
1553
|
+
onChange: (e) => handleHslaInputChange("l", e.target.value),
|
|
1554
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1555
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1556
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1557
|
+
children: "L"
|
|
1558
|
+
})]
|
|
1559
|
+
}),
|
|
1560
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1561
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
1562
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
1563
|
+
type: "text",
|
|
1564
|
+
"aria-label": "Alpha",
|
|
1565
|
+
disabled,
|
|
1566
|
+
value: hslaInputs.a,
|
|
1567
|
+
onChange: (e) => handleHslaInputChange("a", e.target.value),
|
|
1568
|
+
className: "w-full h-7 rounded-md border border-input bg-background/50 py-[calc(--spacing(1)-1px)] text-center font-mono text-xs shadow-2xs outline-none focus:border-primary/40 focus:ring-1 focus:ring-primary/20 disabled:opacity-64"
|
|
1569
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1570
|
+
className: "text-[10px] text-muted-foreground font-medium uppercase tracking-wider",
|
|
1571
|
+
children: "A"
|
|
1572
|
+
})]
|
|
1573
|
+
})
|
|
1574
|
+
] })
|
|
1575
|
+
]
|
|
1576
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
1577
|
+
type: "button",
|
|
1578
|
+
disabled,
|
|
1579
|
+
onClick: toggleMode,
|
|
1580
|
+
"aria-label": "Switch color format",
|
|
1581
|
+
className: "relative flex items-center justify-center h-7 px-[calc(--spacing(2)-1px)] rounded-md border border-input bg-background/30 hover:bg-muted/70 active:bg-muted/90 cursor-pointer text-muted-foreground transition-colors disabled:opacity-64 disabled:cursor-not-allowed shadow-2xs focus:outline-none focus-visible:ring-1 focus-visible:ring-primary/40 pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11",
|
|
1582
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5" })
|
|
1583
|
+
})]
|
|
1584
|
+
})]
|
|
1585
|
+
})]
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
//#endregion
|
|
1589
|
+
export { BlossomColorPicker, ColorPicker, DEFAULT_COLORS, INNER_COLORS, OUTER_COLORS, calculateArcGradientColors, calculateBarRadius, calculateContainerSize, calculateLayerRadii, calculateLayerRotations, calculateSliderValueFromPoint, computeAdaptivePosition, createColorOutput, describeArc, getCenterAngle, getOppositePosition, getPetalZIndex, getVisualSaturation, hexToHsl, hslToHex, hslToRgb, hslToString, hslaToString, lightnessToSliderValue, organizeColorsIntoLayers, parseColor, polarToCartesian, rgbToHsl, rgbaToString, sliderValueToLightness };
|