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.
Files changed (224) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +167 -0
  3. package/dist/components/accordion.d.ts +9 -0
  4. package/dist/components/accordion.js +46 -0
  5. package/dist/components/agent-steps.d.ts +47 -0
  6. package/dist/components/agent-steps.js +223 -0
  7. package/dist/components/alert-dialog.d.ts +21 -0
  8. package/dist/components/alert-dialog.js +74 -0
  9. package/dist/components/alert.d.ts +12 -0
  10. package/dist/components/alert.js +45 -0
  11. package/dist/components/aspect-ratio.d.ts +9 -0
  12. package/dist/components/aspect-ratio.js +22 -0
  13. package/dist/components/autocomplete.d.ts +35 -0
  14. package/dist/components/autocomplete.js +160 -0
  15. package/dist/components/avatar.d.ts +17 -0
  16. package/dist/components/avatar.js +65 -0
  17. package/dist/components/badge.d.ts +15 -0
  18. package/dist/components/badge.js +46 -0
  19. package/dist/components/breadcrumb.d.ts +12 -0
  20. package/dist/components/breadcrumb.js +75 -0
  21. package/dist/components/button-group.d.ts +14 -0
  22. package/dist/components/button-group.js +45 -0
  23. package/dist/components/button-split.d.ts +19 -0
  24. package/dist/components/button-split.js +73 -0
  25. package/dist/components/button.d.ts +15 -0
  26. package/dist/components/button.js +54 -0
  27. package/dist/components/calendar.d.ts +63 -0
  28. package/dist/components/calendar.js +425 -0
  29. package/dist/components/card.d.ts +17 -0
  30. package/dist/components/card.js +139 -0
  31. package/dist/components/carousel.d.ts +59 -0
  32. package/dist/components/carousel.js +503 -0
  33. package/dist/components/checkbox.d.ts +16 -0
  34. package/dist/components/checkbox.js +86 -0
  35. package/dist/components/circular-progress.d.ts +28 -0
  36. package/dist/components/circular-progress.js +127 -0
  37. package/dist/components/collapsible.d.ts +8 -0
  38. package/dist/components/collapsible.js +27 -0
  39. package/dist/components/color-picker.d.ts +159 -0
  40. package/dist/components/color-picker.js +1589 -0
  41. package/dist/components/combobox.d.ts +46 -0
  42. package/dist/components/combobox.js +237 -0
  43. package/dist/components/command.d.ts +22 -0
  44. package/dist/components/command.js +129 -0
  45. package/dist/components/context-menu.d.ts +38 -0
  46. package/dist/components/context-menu.js +164 -0
  47. package/dist/components/copy-to-clipboard.d.ts +32 -0
  48. package/dist/components/copy-to-clipboard.js +83 -0
  49. package/dist/components/corner-badge.d.ts +22 -0
  50. package/dist/components/corner-badge.js +84 -0
  51. package/dist/components/dashboard-grid.d.ts +83 -0
  52. package/dist/components/dashboard-grid.js +486 -0
  53. package/dist/components/date-picker.d.ts +29 -0
  54. package/dist/components/date-picker.js +261 -0
  55. package/dist/components/dialog.d.ts +26 -0
  56. package/dist/components/dialog.js +97 -0
  57. package/dist/components/dot-matrix.d.ts +32 -0
  58. package/dist/components/dot-matrix.js +707 -0
  59. package/dist/components/draggable.d.ts +42 -0
  60. package/dist/components/draggable.js +346 -0
  61. package/dist/components/drawer.d.ts +66 -0
  62. package/dist/components/drawer.js +304 -0
  63. package/dist/components/empty.d.ts +14 -0
  64. package/dist/components/empty.js +75 -0
  65. package/dist/components/field.d.ts +12 -0
  66. package/dist/components/field.js +44 -0
  67. package/dist/components/fieldset.d.ts +7 -0
  68. package/dist/components/fieldset.js +21 -0
  69. package/dist/components/file-upload.d.ts +61 -0
  70. package/dist/components/file-upload.js +482 -0
  71. package/dist/components/form.d.ts +6 -0
  72. package/dist/components/form.js +14 -0
  73. package/dist/components/frame.d.ts +10 -0
  74. package/dist/components/frame.js +47 -0
  75. package/dist/components/gauge.d.ts +27 -0
  76. package/dist/components/gauge.js +74 -0
  77. package/dist/components/grid.d.ts +26 -0
  78. package/dist/components/grid.js +204 -0
  79. package/dist/components/group.d.ts +19 -0
  80. package/dist/components/group.js +45 -0
  81. package/dist/components/input-group.d.ts +15 -0
  82. package/dist/components/input-group.js +61 -0
  83. package/dist/components/input-otp.d.ts +24 -0
  84. package/dist/components/input-otp.js +50 -0
  85. package/dist/components/input-payment.d.ts +131 -0
  86. package/dist/components/input-payment.js +1140 -0
  87. package/dist/components/input-phone.d.ts +70 -0
  88. package/dist/components/input-phone.js +657 -0
  89. package/dist/components/input-sensitive.d.ts +12 -0
  90. package/dist/components/input-sensitive.js +169 -0
  91. package/dist/components/input.d.ts +13 -0
  92. package/dist/components/input.js +26 -0
  93. package/dist/components/kbd.d.ts +6 -0
  94. package/dist/components/kbd.js +19 -0
  95. package/dist/components/kinetic-click.d.ts +48 -0
  96. package/dist/components/kinetic-click.js +1184 -0
  97. package/dist/components/label.d.ts +6 -0
  98. package/dist/components/label.js +18 -0
  99. package/dist/components/list-box.d.ts +20 -0
  100. package/dist/components/list-box.js +148 -0
  101. package/dist/components/loader.d.ts +13 -0
  102. package/dist/components/loader.js +430 -0
  103. package/dist/components/marker.d.ts +15 -0
  104. package/dist/components/marker.js +59 -0
  105. package/dist/components/menu.d.ts +42 -0
  106. package/dist/components/menu.js +186 -0
  107. package/dist/components/menubar.d.ts +40 -0
  108. package/dist/components/menubar.js +107 -0
  109. package/dist/components/message.d.ts +33 -0
  110. package/dist/components/message.js +112 -0
  111. package/dist/components/meter.d.ts +10 -0
  112. package/dist/components/meter.js +42 -0
  113. package/dist/components/morphic-toast.d.ts +61 -0
  114. package/dist/components/morphic-toast.js +916 -0
  115. package/dist/components/navigation-menu.d.ts +26 -0
  116. package/dist/components/navigation-menu.js +116 -0
  117. package/dist/components/number-field.d.ts +15 -0
  118. package/dist/components/number-field.js +86 -0
  119. package/dist/components/option-picker.d.ts +18 -0
  120. package/dist/components/option-picker.js +89 -0
  121. package/dist/components/orb.d.ts +38 -0
  122. package/dist/components/orb.js +1413 -0
  123. package/dist/components/pagination.d.ts +25 -0
  124. package/dist/components/pagination.js +147 -0
  125. package/dist/components/popover.d.ts +22 -0
  126. package/dist/components/popover.js +79 -0
  127. package/dist/components/preview-card.d.ts +16 -0
  128. package/dist/components/preview-card.js +60 -0
  129. package/dist/components/progress.d.ts +21 -0
  130. package/dist/components/progress.js +139 -0
  131. package/dist/components/radio.d.ts +16 -0
  132. package/dist/components/radio.js +68 -0
  133. package/dist/components/resizable.d.ts +58 -0
  134. package/dist/components/resizable.js +964 -0
  135. package/dist/components/ruler-picker.d.ts +27 -0
  136. package/dist/components/ruler-picker.js +253 -0
  137. package/dist/components/scroll-area.d.ts +13 -0
  138. package/dist/components/scroll-area.js +34 -0
  139. package/dist/components/scroll-bar.d.ts +13 -0
  140. package/dist/components/scroll-bar.js +47 -0
  141. package/dist/components/select.d.ts +29 -0
  142. package/dist/components/select.js +140 -0
  143. package/dist/components/separator.d.ts +6 -0
  144. package/dist/components/separator.js +15 -0
  145. package/dist/components/sidebar.d.ts +65 -0
  146. package/dist/components/sidebar.js +408 -0
  147. package/dist/components/skeleton.d.ts +5 -0
  148. package/dist/components/skeleton.js +12 -0
  149. package/dist/components/slider.d.ts +12 -0
  150. package/dist/components/slider.js +96 -0
  151. package/dist/components/sortable.d.ts +44 -0
  152. package/dist/components/sortable.js +327 -0
  153. package/dist/components/switch.d.ts +15 -0
  154. package/dist/components/switch.js +63 -0
  155. package/dist/components/table.d.ts +12 -0
  156. package/dist/components/table.js +65 -0
  157. package/dist/components/tabs.d.ts +12 -0
  158. package/dist/components/tabs.js +39 -0
  159. package/dist/components/text.d.ts +14 -0
  160. package/dist/components/text.js +69 -0
  161. package/dist/components/textarea.d.ts +9 -0
  162. package/dist/components/textarea.js +20 -0
  163. package/dist/components/theme-provider.d.ts +52 -0
  164. package/dist/components/theme-provider.js +148 -0
  165. package/dist/components/timeline.d.ts +34 -0
  166. package/dist/components/timeline.js +147 -0
  167. package/dist/components/toast.d.ts +17 -0
  168. package/dist/components/toast.js +161 -0
  169. package/dist/components/toggle-group.d.ts +14 -0
  170. package/dist/components/toggle-group.js +53 -0
  171. package/dist/components/toggle.d.ts +11 -0
  172. package/dist/components/toggle.js +36 -0
  173. package/dist/components/toolbar.d.ts +11 -0
  174. package/dist/components/toolbar.js +49 -0
  175. package/dist/components/tooltip.d.ts +17 -0
  176. package/dist/components/tooltip.js +56 -0
  177. package/dist/components/waveform.d.ts +61 -0
  178. package/dist/components/waveform.js +500 -0
  179. package/dist/components/wheel-picker.d.ts +44 -0
  180. package/dist/components/wheel-picker.js +771 -0
  181. package/dist/hooks/use-copy-to-clipboard.d.ts +9 -0
  182. package/dist/hooks/use-copy-to-clipboard.js +36 -0
  183. package/dist/hooks/use-media-query.d.ts +21 -0
  184. package/dist/hooks/use-media-query.js +58 -0
  185. package/dist/styles.css +202 -0
  186. package/dist/themes/afterglow.css +89 -0
  187. package/dist/themes/afterglow.scoped.css +90 -0
  188. package/dist/themes/blacklight.css +89 -0
  189. package/dist/themes/blacklight.scoped.css +90 -0
  190. package/dist/themes/cathode.css +89 -0
  191. package/dist/themes/cathode.scoped.css +90 -0
  192. package/dist/themes/cel.css +89 -0
  193. package/dist/themes/cel.scoped.css +90 -0
  194. package/dist/themes/cinder.css +89 -0
  195. package/dist/themes/cinder.scoped.css +90 -0
  196. package/dist/themes/clay.css +89 -0
  197. package/dist/themes/clay.scoped.css +90 -0
  198. package/dist/themes/cobalt.css +89 -0
  199. package/dist/themes/cobalt.scoped.css +90 -0
  200. package/dist/themes/crimson.css +89 -0
  201. package/dist/themes/crimson.scoped.css +90 -0
  202. package/dist/themes/ion.css +89 -0
  203. package/dist/themes/ion.scoped.css +90 -0
  204. package/dist/themes/iris.css +89 -0
  205. package/dist/themes/iris.scoped.css +90 -0
  206. package/dist/themes/lagoon.css +89 -0
  207. package/dist/themes/lagoon.scoped.css +90 -0
  208. package/dist/themes/mochi.css +89 -0
  209. package/dist/themes/mochi.scoped.css +90 -0
  210. package/dist/themes/riso.css +89 -0
  211. package/dist/themes/riso.scoped.css +90 -0
  212. package/dist/themes/roast.css +89 -0
  213. package/dist/themes/roast.scoped.css +90 -0
  214. package/dist/themes/sage.css +89 -0
  215. package/dist/themes/sage.scoped.css +90 -0
  216. package/dist/themes/sepia.css +89 -0
  217. package/dist/themes/sepia.scoped.css +90 -0
  218. package/dist/themes/sorbet.css +89 -0
  219. package/dist/themes/sorbet.scoped.css +90 -0
  220. package/dist/themes/taffy.css +89 -0
  221. package/dist/themes/taffy.scoped.css +90 -0
  222. package/dist/themes/toxin.css +89 -0
  223. package/dist/themes/toxin.scoped.css +90 -0
  224. package/package.json +140 -0
@@ -0,0 +1,500 @@
1
+ "use client";
2
+ import { cn } from "cn";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import * as React$1 from "react";
5
+ import { mergeProps } from "@base-ui/react/merge-props";
6
+ import { useRender } from "@base-ui/react/use-render";
7
+ //#region src/components/waveform.tsx
8
+ const WaveformContext = React$1.createContext(null);
9
+ function useWaveform() {
10
+ const context = React$1.useContext(WaveformContext);
11
+ if (!context) throw new Error("Waveform components must be wrapped in <Waveform />");
12
+ return context;
13
+ }
14
+ function generateDeterministicData(length, seed) {
15
+ const data = [];
16
+ let currentSeed = seed;
17
+ const rand = () => {
18
+ currentSeed = (currentSeed * 1664525 + 1013904223) % 4294967296;
19
+ return currentSeed / 4294967296;
20
+ };
21
+ for (let i = 0; i < length; i++) {
22
+ const wave1 = Math.sin(i * .1) * .25;
23
+ const wave2 = Math.cos(i * .05) * .15;
24
+ const randomVal = rand() * .3;
25
+ const val = Math.max(.08, Math.min(.9, .45 + wave1 + wave2 + randomVal));
26
+ data.push(val);
27
+ }
28
+ return data;
29
+ }
30
+ function Waveform({ children, className, value: controlledValue, defaultValue, onValueChange, duration = 1, active = false, processing = false, mode = "static", microphone = false, deviceId, barWidth = 3, barGap = 2, barRadius = 1.5, barColor, progressColor, fadeEdges = true, fadeWidth = 24, sensitivity = 1, updateRate = 30, seed = 42, data, ...props }) {
31
+ const [internalValue, setInternalValue] = React$1.useState(defaultValue ?? 0);
32
+ const value = controlledValue !== void 0 ? controlledValue : internalValue;
33
+ const [isDragging, setIsDragging] = React$1.useState(false);
34
+ const canvasRef = React$1.useRef(null);
35
+ const containerRef = React$1.useRef(null);
36
+ const liveDataRef = React$1.useRef([]);
37
+ const needsRedrawRef = React$1.useRef(true);
38
+ const audioContextRef = React$1.useRef(null);
39
+ const analyserRef = React$1.useRef(null);
40
+ const streamRef = React$1.useRef(null);
41
+ const triggerRedraw = React$1.useCallback(() => {
42
+ needsRedrawRef.current = true;
43
+ }, []);
44
+ const seekTo = React$1.useCallback((clientX) => {
45
+ const container = containerRef.current;
46
+ if (!container) return;
47
+ const rect = container.getBoundingClientRect();
48
+ const newValue = Math.max(0, Math.min(clientX - rect.left, rect.width)) / rect.width * duration;
49
+ if (controlledValue === void 0) setInternalValue(newValue);
50
+ onValueChange?.(newValue);
51
+ triggerRedraw();
52
+ }, [
53
+ duration,
54
+ onValueChange,
55
+ controlledValue,
56
+ triggerRedraw
57
+ ]);
58
+ React$1.useEffect(() => {
59
+ if (!active || !microphone) {
60
+ if (streamRef.current) {
61
+ streamRef.current.getTracks().forEach((track) => {
62
+ track.stop();
63
+ });
64
+ streamRef.current = null;
65
+ }
66
+ if (audioContextRef.current && audioContextRef.current.state !== "closed") {
67
+ audioContextRef.current.close();
68
+ audioContextRef.current = null;
69
+ }
70
+ analyserRef.current = null;
71
+ return;
72
+ }
73
+ const setupMicrophone = async () => {
74
+ try {
75
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: deviceId ? {
76
+ deviceId: { exact: deviceId },
77
+ echoCancellation: true,
78
+ noiseSuppression: true,
79
+ autoGainControl: true
80
+ } : {
81
+ echoCancellation: true,
82
+ noiseSuppression: true,
83
+ autoGainControl: true
84
+ } });
85
+ streamRef.current = stream;
86
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
87
+ const analyser = audioContext.createAnalyser();
88
+ analyser.fftSize = 256;
89
+ analyser.smoothingTimeConstant = .8;
90
+ audioContext.createMediaStreamSource(stream).connect(analyser);
91
+ audioContextRef.current = audioContext;
92
+ analyserRef.current = analyser;
93
+ liveDataRef.current = [];
94
+ } catch (err) {
95
+ console.error("Microphone access failed in Waveform root:", err);
96
+ }
97
+ };
98
+ setupMicrophone();
99
+ return () => {
100
+ if (streamRef.current) streamRef.current.getTracks().forEach((track) => {
101
+ track.stop();
102
+ });
103
+ if (audioContextRef.current && audioContextRef.current.state !== "closed") audioContextRef.current.close();
104
+ };
105
+ }, [
106
+ active,
107
+ microphone,
108
+ deviceId
109
+ ]);
110
+ React$1.useEffect(() => {
111
+ if (!active || !microphone) return;
112
+ let rafId;
113
+ let lastTime = 0;
114
+ const captureSample = (time) => {
115
+ if (time - lastTime >= updateRate && analyserRef.current) {
116
+ lastTime = time;
117
+ const bufferLength = analyserRef.current.frequencyBinCount;
118
+ const dataArray = new Uint8Array(bufferLength);
119
+ analyserRef.current.getByteFrequencyData(dataArray);
120
+ const start = Math.floor(bufferLength * .05);
121
+ const end = Math.floor(bufferLength * .45);
122
+ const sampleSlice = dataArray.slice(start, end);
123
+ if (mode === "scrolling") {
124
+ let sum = 0;
125
+ for (let i = 0; i < sampleSlice.length; i++) sum += sampleSlice[i];
126
+ const avg = sum / sampleSlice.length / 255 * sensitivity;
127
+ liveDataRef.current.push(Math.min(1, Math.max(.08, avg)));
128
+ if (liveDataRef.current.length > 120) liveDataRef.current.shift();
129
+ } else {
130
+ const bars = [];
131
+ const count = Math.max(10, Math.floor((containerRef.current?.getBoundingClientRect().width || 300) / (barWidth + barGap)));
132
+ const half = Math.floor(count / 2);
133
+ for (let i = 0; i < half; i++) {
134
+ const dataIdx = Math.floor(i / half * sampleSlice.length);
135
+ const val = Math.min(1, sampleSlice[dataIdx] / 255 * sensitivity);
136
+ bars.unshift(Math.max(.06, val));
137
+ }
138
+ for (let i = 0; i < half; i++) {
139
+ const dataIdx = Math.floor(i / half * sampleSlice.length);
140
+ const val = Math.min(1, sampleSlice[dataIdx] / 255 * sensitivity);
141
+ bars.push(Math.max(.06, val));
142
+ }
143
+ liveDataRef.current = bars;
144
+ }
145
+ needsRedrawRef.current = true;
146
+ }
147
+ rafId = requestAnimationFrame(captureSample);
148
+ };
149
+ rafId = requestAnimationFrame(captureSample);
150
+ return () => cancelAnimationFrame(rafId);
151
+ }, [
152
+ active,
153
+ microphone,
154
+ mode,
155
+ barWidth,
156
+ barGap,
157
+ sensitivity,
158
+ updateRate
159
+ ]);
160
+ React$1.useEffect(() => {
161
+ if (!processing || active) return;
162
+ let rafId;
163
+ let time = 0;
164
+ const generateWave = () => {
165
+ time += .04;
166
+ const count = Math.max(10, Math.floor((containerRef.current?.getBoundingClientRect().width || 300) / (barWidth + barGap)));
167
+ const bars = [];
168
+ if (mode === "scrolling") {
169
+ const val = Math.min(1, Math.max(.08, .35 + Math.sin(time) * .2 + Math.cos(time * 1.7) * .1));
170
+ liveDataRef.current.push(val);
171
+ if (liveDataRef.current.length > 120) liveDataRef.current.shift();
172
+ } else {
173
+ const half = Math.floor(count / 2);
174
+ for (let i = 0; i < count; i++) {
175
+ const normDist = (i - half) / half;
176
+ const amplitudeWeight = 1 - Math.abs(normDist) * .45;
177
+ const sine1 = Math.sin(time * 1.6 + normDist * 3.5) * .25;
178
+ const sine2 = Math.sin(time * .9 - normDist * 2.2) * .18;
179
+ const sine3 = Math.cos(time * 2.4 + normDist) * .12;
180
+ const combined = sine1 + sine2 + sine3;
181
+ bars.push(Math.max(.06, Math.min(1, (.35 + combined) * amplitudeWeight)));
182
+ }
183
+ liveDataRef.current = bars;
184
+ }
185
+ needsRedrawRef.current = true;
186
+ rafId = requestAnimationFrame(generateWave);
187
+ };
188
+ rafId = requestAnimationFrame(generateWave);
189
+ return () => cancelAnimationFrame(rafId);
190
+ }, [
191
+ processing,
192
+ active,
193
+ mode,
194
+ barWidth,
195
+ barGap
196
+ ]);
197
+ React$1.useEffect(() => {
198
+ triggerRedraw();
199
+ const t1 = setTimeout(triggerRedraw, 50);
200
+ const t2 = setTimeout(triggerRedraw, 250);
201
+ const t3 = setTimeout(triggerRedraw, 1e3);
202
+ let observer = null;
203
+ if (typeof document !== "undefined") {
204
+ observer = new MutationObserver(() => {
205
+ triggerRedraw();
206
+ });
207
+ observer.observe(document.documentElement, {
208
+ attributes: true,
209
+ attributeFilter: ["class"]
210
+ });
211
+ }
212
+ return () => {
213
+ clearTimeout(t1);
214
+ clearTimeout(t2);
215
+ clearTimeout(t3);
216
+ if (observer) observer.disconnect();
217
+ };
218
+ }, [triggerRedraw]);
219
+ const contextValue = React$1.useMemo(() => ({
220
+ value,
221
+ duration,
222
+ onValueChange,
223
+ active,
224
+ processing,
225
+ mode,
226
+ barWidth,
227
+ barGap,
228
+ barRadius,
229
+ barColor,
230
+ progressColor,
231
+ fadeEdges,
232
+ fadeWidth,
233
+ sensitivity,
234
+ updateRate,
235
+ seed,
236
+ data,
237
+ canvasRef,
238
+ containerRef,
239
+ isDragging,
240
+ setIsDragging,
241
+ seekTo,
242
+ liveDataRef,
243
+ needsRedrawRef,
244
+ triggerRedraw
245
+ }), [
246
+ value,
247
+ duration,
248
+ onValueChange,
249
+ active,
250
+ processing,
251
+ mode,
252
+ barWidth,
253
+ barGap,
254
+ barRadius,
255
+ barColor,
256
+ progressColor,
257
+ fadeEdges,
258
+ fadeWidth,
259
+ sensitivity,
260
+ updateRate,
261
+ seed,
262
+ data,
263
+ isDragging,
264
+ seekTo,
265
+ triggerRedraw
266
+ ]);
267
+ return /* @__PURE__ */ jsx(WaveformContext.Provider, {
268
+ value: contextValue,
269
+ children: /* @__PURE__ */ jsx("div", {
270
+ ref: containerRef,
271
+ "data-slot": "waveform",
272
+ className: cn("relative w-full select-none flex items-center justify-center min-h-[64px]", className),
273
+ ...props,
274
+ children: children || /* @__PURE__ */ jsx(WaveformVisual, {})
275
+ })
276
+ });
277
+ }
278
+ function WaveformVisual({ className, ...props }) {
279
+ const { canvasRef, containerRef, data, seed, mode, active, processing, barWidth, barGap, barRadius, barColor, progressColor, fadeEdges, fadeWidth, value, duration, liveDataRef, needsRedrawRef } = useWaveform();
280
+ const animationRef = React$1.useRef(0);
281
+ const gradientCacheRef = React$1.useRef(null);
282
+ const lastWidthRef = React$1.useRef(0);
283
+ React$1.useEffect(() => {
284
+ const canvas = canvasRef.current;
285
+ const container = containerRef.current;
286
+ if (!canvas || !container) return;
287
+ const resizeObserver = new ResizeObserver(() => {
288
+ const rect = container.getBoundingClientRect();
289
+ const dpr = window.devicePixelRatio || 1;
290
+ canvas.width = rect.width * dpr;
291
+ canvas.height = rect.height * dpr;
292
+ canvas.style.width = `${rect.width}px`;
293
+ canvas.style.height = `${rect.height}px`;
294
+ const ctx = canvas.getContext("2d");
295
+ if (ctx) ctx.scale(dpr, dpr);
296
+ gradientCacheRef.current = null;
297
+ lastWidthRef.current = rect.width;
298
+ needsRedrawRef.current = true;
299
+ });
300
+ resizeObserver.observe(container);
301
+ return () => resizeObserver.disconnect();
302
+ }, [
303
+ canvasRef,
304
+ containerRef,
305
+ needsRedrawRef
306
+ ]);
307
+ React$1.useEffect(() => {
308
+ const canvas = canvasRef.current;
309
+ if (!canvas) return;
310
+ const ctx = canvas.getContext("2d");
311
+ if (!ctx) return;
312
+ const draw = () => {
313
+ if (!needsRedrawRef.current) {
314
+ animationRef.current = requestAnimationFrame(draw);
315
+ return;
316
+ }
317
+ const rect = canvas.getBoundingClientRect();
318
+ if (rect.width === 0 || rect.height === 0) {
319
+ animationRef.current = requestAnimationFrame(draw);
320
+ return;
321
+ }
322
+ ctx.clearRect(0, 0, rect.width, rect.height);
323
+ const style = getComputedStyle(canvas);
324
+ const computedBarColor = barColor || style.getPropertyValue("--border").trim() || style.getPropertyValue("--color-border").trim() || "rgba(0, 0, 0, 0.16)";
325
+ const computedProgressColor = progressColor || style.getPropertyValue("--primary").trim() || style.getPropertyValue("--color-primary").trim() || "#000";
326
+ const step = barWidth + barGap;
327
+ const barCount = Math.floor(rect.width / step);
328
+ const centerY = rect.height / 2;
329
+ let currentBars = [];
330
+ const hasLiveData = active || processing;
331
+ if (hasLiveData) currentBars = [...liveDataRef.current];
332
+ else if (data && data.length > 0) currentBars = data;
333
+ else currentBars = generateDeterministicData(barCount, seed);
334
+ const progressFraction = duration > 0 ? Math.min(1, Math.max(0, value / duration)) : 0;
335
+ const progressX = rect.width * progressFraction;
336
+ if (mode === "scrolling" && hasLiveData) for (let i = 0; i < barCount && i < currentBars.length; i++) {
337
+ const valIndex = currentBars.length - 1 - i;
338
+ const val = currentBars[valIndex] || .08;
339
+ const x = rect.width - (i + 1) * step;
340
+ const height = Math.max(4, val * rect.height * .82);
341
+ const y = centerY - height / 2;
342
+ ctx.fillStyle = computedProgressColor;
343
+ ctx.globalAlpha = .45 + val * .55;
344
+ if (barRadius > 0) {
345
+ ctx.beginPath();
346
+ ctx.roundRect(x, y, barWidth, height, barRadius);
347
+ ctx.fill();
348
+ } else ctx.fillRect(x, y, barWidth, height);
349
+ }
350
+ else {
351
+ Math.min(barCount, currentBars.length);
352
+ for (let i = 0; i < barCount; i++) {
353
+ const dataIndex = Math.min(currentBars.length - 1, Math.floor(i / barCount * currentBars.length));
354
+ const val = currentBars[dataIndex] || .08;
355
+ const x = i * step;
356
+ const height = Math.max(4, val * rect.height * .82);
357
+ const y = centerY - height / 2;
358
+ const isPlayed = x < progressX && !hasLiveData;
359
+ ctx.fillStyle = isPlayed ? computedProgressColor : computedBarColor;
360
+ ctx.globalAlpha = isPlayed ? 1 : .45;
361
+ if (barRadius > 0) {
362
+ ctx.beginPath();
363
+ ctx.roundRect(x, y, barWidth, height, barRadius);
364
+ ctx.fill();
365
+ } else ctx.fillRect(x, y, barWidth, height);
366
+ }
367
+ }
368
+ if (fadeEdges && fadeWidth > 0) {
369
+ if (!gradientCacheRef.current || lastWidthRef.current !== rect.width) {
370
+ const gradient = ctx.createLinearGradient(0, 0, rect.width, 0);
371
+ const fadePercent = Math.min(.35, fadeWidth / rect.width);
372
+ gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
373
+ gradient.addColorStop(fadePercent, "rgba(255, 255, 255, 0)");
374
+ gradient.addColorStop(1 - fadePercent, "rgba(255, 255, 255, 0)");
375
+ gradient.addColorStop(1, "rgba(255, 255, 255, 1)");
376
+ gradientCacheRef.current = gradient;
377
+ lastWidthRef.current = rect.width;
378
+ }
379
+ ctx.globalCompositeOperation = "destination-out";
380
+ ctx.fillStyle = gradientCacheRef.current;
381
+ ctx.fillRect(0, 0, rect.width, rect.height);
382
+ ctx.globalCompositeOperation = "source-over";
383
+ }
384
+ ctx.globalAlpha = 1;
385
+ needsRedrawRef.current = active || processing;
386
+ animationRef.current = requestAnimationFrame(draw);
387
+ };
388
+ animationRef.current = requestAnimationFrame(draw);
389
+ return () => cancelAnimationFrame(animationRef.current);
390
+ }, [
391
+ canvasRef,
392
+ barWidth,
393
+ barGap,
394
+ barRadius,
395
+ barColor,
396
+ progressColor,
397
+ fadeEdges,
398
+ fadeWidth,
399
+ value,
400
+ duration,
401
+ mode,
402
+ active,
403
+ processing,
404
+ data,
405
+ seed,
406
+ liveDataRef,
407
+ needsRedrawRef
408
+ ]);
409
+ return /* @__PURE__ */ jsx("canvas", {
410
+ "aria-hidden": "true",
411
+ ref: canvasRef,
412
+ "data-slot": "waveform-visual",
413
+ className: cn("block w-full h-full text-foreground border-border", className),
414
+ ...props
415
+ });
416
+ }
417
+ function WaveformScrubber({ className, ...props }) {
418
+ const { value, duration, onValueChange, seekTo, setIsDragging, triggerRedraw } = useWaveform();
419
+ const handlePointerDown = (e) => {
420
+ e.currentTarget.setPointerCapture(e.pointerId);
421
+ setIsDragging(true);
422
+ seekTo(e.clientX);
423
+ };
424
+ const handlePointerMove = (e) => {
425
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) seekTo(e.clientX);
426
+ };
427
+ const handlePointerUp = (e) => {
428
+ e.currentTarget.releasePointerCapture(e.pointerId);
429
+ setIsDragging(false);
430
+ };
431
+ const handleKeyDown = (e) => {
432
+ let delta = 0;
433
+ const stepSize = duration * .01;
434
+ const largeStepSize = duration * .1;
435
+ switch (e.key) {
436
+ case "ArrowRight":
437
+ case "ArrowUp":
438
+ delta = stepSize;
439
+ break;
440
+ case "ArrowLeft":
441
+ case "ArrowDown":
442
+ delta = -stepSize;
443
+ break;
444
+ case "PageUp":
445
+ delta = largeStepSize;
446
+ break;
447
+ case "PageDown":
448
+ delta = -largeStepSize;
449
+ break;
450
+ case "Home":
451
+ onValueChange?.(0);
452
+ triggerRedraw();
453
+ return;
454
+ case "End":
455
+ onValueChange?.(duration);
456
+ triggerRedraw();
457
+ return;
458
+ default: return;
459
+ }
460
+ e.preventDefault();
461
+ const newValue = Math.min(duration, Math.max(0, value + delta));
462
+ onValueChange?.(newValue);
463
+ triggerRedraw();
464
+ };
465
+ return /* @__PURE__ */ jsx("div", {
466
+ role: "slider",
467
+ "aria-label": "Audio waveform scrubber",
468
+ "aria-valuemin": 0,
469
+ "aria-valuemax": duration,
470
+ "aria-valuenow": value,
471
+ tabIndex: 0,
472
+ "data-slot": "waveform-scrubber",
473
+ onPointerDown: handlePointerDown,
474
+ onPointerMove: handlePointerMove,
475
+ onPointerUp: handlePointerUp,
476
+ onPointerCancel: handlePointerUp,
477
+ onKeyDown: handleKeyDown,
478
+ className: cn("absolute inset-0 cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-md select-none", "pointer-coarse:after:absolute pointer-coarse:after:inset-y-[-12px] pointer-coarse:after:inset-x-0 pointer-coarse:after:min-h-[44px]", className),
479
+ ...props
480
+ });
481
+ }
482
+ function WaveformHandle({ className, render, ...props }) {
483
+ const { value, duration } = useWaveform();
484
+ const progressPercent = `${(duration > 0 ? Math.min(1, Math.max(0, value / duration)) : 0) * 100}%`;
485
+ const defaultProps = {
486
+ "data-slot": "waveform-handle",
487
+ className: cn("absolute top-1/2 -translate-y-1/2 size-4.5 sm:size-4 rounded-full bg-primary border-2 border-background shadow-md/20 pointer-events-none transition-transform duration-100 ease-out", className),
488
+ style: {
489
+ left: progressPercent,
490
+ transform: "translate(-50%, -50%)"
491
+ }
492
+ };
493
+ return useRender({
494
+ defaultTagName: "div",
495
+ props: mergeProps(defaultProps, props),
496
+ render
497
+ });
498
+ }
499
+ //#endregion
500
+ export { Waveform, WaveformHandle, WaveformScrubber, WaveformVisual, useWaveform };
@@ -0,0 +1,44 @@
1
+ import { VariantProps } from "class-variance-authority";
2
+ import * as React$1 from "react";
3
+ //#region src/components/wheel-picker.d.ts
4
+ interface WheelPickerGroupContextValue {
5
+ activeIndex: number;
6
+ setActiveIndex: (index: number) => void;
7
+ register: (existingIndex: number | null, ref: HTMLDivElement) => number;
8
+ getPickerRef: (index: number) => HTMLDivElement | null;
9
+ getPickerIndices: () => number[];
10
+ }
11
+ export declare function useWheelPickerGroup(): WheelPickerGroupContextValue | null;
12
+ export interface WheelPickerGroupProps extends React$1.ComponentPropsWithoutRef<"div"> {
13
+ children: React$1.ReactNode;
14
+ }
15
+ export declare function WheelPickerGroup({ className, children, ...props }: WheelPickerGroupProps): React$1.JSX.Element;
16
+ export type WheelPickerValue = string | number;
17
+ export interface WheelPickerOption<T extends WheelPickerValue = WheelPickerValue> {
18
+ value: T;
19
+ label: React$1.ReactNode;
20
+ textValue?: string;
21
+ disabled?: boolean;
22
+ }
23
+ export interface WheelPickerClassNames {
24
+ optionItem?: string;
25
+ highlightWrapper?: string;
26
+ highlightItem?: string;
27
+ }
28
+ export declare const wheelPickerVariants: (props?: ({
29
+ size?: "sm" | "md" | "lg" | null | undefined;
30
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
31
+ export interface WheelPickerProps<T extends WheelPickerValue = WheelPickerValue> extends Omit<React$1.ComponentPropsWithoutRef<"div">, "value" | "defaultValue" | "onChange">, VariantProps<typeof wheelPickerVariants> {
32
+ options: WheelPickerOption<T>[];
33
+ value?: T;
34
+ defaultValue?: T;
35
+ onValueChange?: (value: T) => void;
36
+ infinite?: boolean;
37
+ visibleCount?: number;
38
+ dragSensitivity?: number;
39
+ scrollSensitivity?: number;
40
+ optionItemHeight?: number;
41
+ classNames?: WheelPickerClassNames;
42
+ }
43
+ export declare function WheelPicker<T extends WheelPickerValue = WheelPickerValue>({ className, options: optionsProp, value: valueProp, defaultValue, onValueChange, infinite, visibleCount, dragSensitivity, scrollSensitivity, optionItemHeight, classNames, size, ...props }: WheelPickerProps<T>): React$1.JSX.Element;
44
+ //#endregion