@zentauri-ui/zentauri-components 2.1.4 → 2.1.6

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 (70) hide show
  1. package/README.md +9 -6
  2. package/cli/cli.integration.test.ts +44 -2
  3. package/cli/index.mjs +134 -28
  4. package/cli/index.test.ts +180 -0
  5. package/cli/props.json +15180 -0
  6. package/cli/props.test.ts +80 -0
  7. package/cli/registry.json +2 -0
  8. package/dist/chunk-3W2UUKWP.js +19 -0
  9. package/dist/{chunk-D2GISTDL.js.map → chunk-3W2UUKWP.js.map} +1 -1
  10. package/dist/{chunk-BL6UVCV7.mjs → chunk-A4IB3C23.mjs} +16 -7
  11. package/dist/chunk-A4IB3C23.mjs.map +1 -0
  12. package/dist/{chunk-WBZKMSXW.mjs → chunk-CHI6MBTI.mjs} +3 -3
  13. package/dist/{chunk-WBZKMSXW.mjs.map → chunk-CHI6MBTI.mjs.map} +1 -1
  14. package/dist/chunk-COCPCZMR.mjs +77 -0
  15. package/dist/chunk-COCPCZMR.mjs.map +1 -0
  16. package/dist/chunk-PG7LQVU6.js +86 -0
  17. package/dist/chunk-PG7LQVU6.js.map +1 -0
  18. package/dist/{chunk-RENXBUZY.js → chunk-QE7OJW4J.js} +6 -6
  19. package/dist/{chunk-RENXBUZY.js.map → chunk-QE7OJW4J.js.map} +1 -1
  20. package/dist/{chunk-NZSZE36T.js → chunk-VA6SB6NN.js} +16 -7
  21. package/dist/{chunk-BL6UVCV7.mjs.map → chunk-VA6SB6NN.js.map} +1 -1
  22. package/dist/{chunk-PAG5CTLN.mjs → chunk-WWKAJHIV.mjs} +3 -3
  23. package/dist/{chunk-PAG5CTLN.mjs.map → chunk-WWKAJHIV.mjs.map} +1 -1
  24. package/dist/design-system/audio-player.d.ts +61 -0
  25. package/dist/design-system/audio-player.d.ts.map +1 -0
  26. package/dist/design-system/facade.js +8 -7
  27. package/dist/design-system/facade.js.map +1 -1
  28. package/dist/design-system/facade.mjs +7 -6
  29. package/dist/design-system/facade.mjs.map +1 -1
  30. package/dist/design-system/index.d.ts +1 -0
  31. package/dist/design-system/index.d.ts.map +1 -1
  32. package/dist/ui/audio-player/audio-player-base.d.ts +20 -0
  33. package/dist/ui/audio-player/audio-player-base.d.ts.map +1 -0
  34. package/dist/ui/audio-player/audio-player.d.ts +6 -0
  35. package/dist/ui/audio-player/audio-player.d.ts.map +1 -0
  36. package/dist/ui/audio-player/index.d.ts +5 -0
  37. package/dist/ui/audio-player/index.d.ts.map +1 -0
  38. package/dist/ui/audio-player/types.d.ts +44 -0
  39. package/dist/ui/audio-player/types.d.ts.map +1 -0
  40. package/dist/ui/audio-player/variants.d.ts +12 -0
  41. package/dist/ui/audio-player/variants.d.ts.map +1 -0
  42. package/dist/ui/audio-player.js +556 -0
  43. package/dist/ui/audio-player.js.map +1 -0
  44. package/dist/ui/audio-player.mjs +545 -0
  45. package/dist/ui/audio-player.mjs.map +1 -0
  46. package/dist/ui/buttons/animated.js +10 -9
  47. package/dist/ui/buttons/animated.js.map +1 -1
  48. package/dist/ui/buttons/animated.mjs +8 -7
  49. package/dist/ui/buttons/animated.mjs.map +1 -1
  50. package/dist/ui/buttons.js +11 -10
  51. package/dist/ui/buttons.mjs +9 -8
  52. package/dist/ui/dynamic-stepper.js +20 -19
  53. package/dist/ui/dynamic-stepper.js.map +1 -1
  54. package/dist/ui/dynamic-stepper.mjs +9 -8
  55. package/dist/ui/dynamic-stepper.mjs.map +1 -1
  56. package/dist/ui/pagination.js +16 -15
  57. package/dist/ui/pagination.js.map +1 -1
  58. package/dist/ui/pagination.mjs +8 -7
  59. package/dist/ui/pagination.mjs.map +1 -1
  60. package/package.json +5 -2
  61. package/src/design-system/audio-player.ts +109 -0
  62. package/src/design-system/index.ts +1 -0
  63. package/src/ui/audio-player/audio-player-base.tsx +557 -0
  64. package/src/ui/audio-player/audio-player.test.tsx +485 -0
  65. package/src/ui/audio-player/audio-player.tsx +8 -0
  66. package/src/ui/audio-player/index.ts +24 -0
  67. package/src/ui/audio-player/types.ts +57 -0
  68. package/src/ui/audio-player/variants.ts +43 -0
  69. package/dist/chunk-D2GISTDL.js +0 -19
  70. package/dist/chunk-NZSZE36T.js.map +0 -1
@@ -0,0 +1,557 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ } from "react";
12
+ import type { KeyboardEvent, MouseEvent, PointerEvent } from "react";
13
+
14
+ import { cn } from "../../lib/utils";
15
+
16
+ import type {
17
+ AudioPlayerCtx,
18
+ AudioPlayerProgressProps,
19
+ AudioPlayerProps,
20
+ AudioPlayerTimeProps,
21
+ AudioPlayerVolumeProps,
22
+ } from "./types";
23
+ import {
24
+ audioPlayerBarVariants,
25
+ audioPlayerTimeVariants,
26
+ audioPlayerTrackVariants,
27
+ audioPlayerVariants,
28
+ } from "./variants";
29
+
30
+ export const AudioPlayerContext = createContext<AudioPlayerCtx | null>(null);
31
+
32
+ export function useAudioPlayer(): AudioPlayerCtx {
33
+ const ctx = useContext(AudioPlayerContext);
34
+ if (!ctx) {
35
+ throw new Error("useAudioPlayer must be used within <AudioPlayer>");
36
+ }
37
+ return ctx;
38
+ }
39
+
40
+ function formatTime(seconds: number): string {
41
+ if (!isFinite(seconds) || isNaN(seconds)) return "0:00";
42
+ const m = Math.floor(seconds / 60);
43
+ const s = Math.floor(seconds % 60);
44
+ return `${m}:${s.toString().padStart(2, "0")}`;
45
+ }
46
+
47
+ export function AudioPlayerBase(props: AudioPlayerProps) {
48
+ const {
49
+ className,
50
+ appearance = "default",
51
+ size = "md",
52
+ shape = "rounded",
53
+ src,
54
+ children,
55
+ autoPlay = false,
56
+ loop = false,
57
+ onEnded,
58
+ onPlay,
59
+ onPause,
60
+ onTimeUpdate,
61
+ ref,
62
+ ...rest
63
+ } = props;
64
+
65
+ const audioRef = useRef<HTMLAudioElement>(null);
66
+ const [isPlaying, setIsPlaying] = useState(false);
67
+ const [currentTime, setCurrentTime] = useState(0);
68
+ const [duration, setDuration] = useState(0);
69
+ const [volume, setVolumeState] = useState(1);
70
+ const [muted, setMuted] = useState(false);
71
+
72
+ const progress = duration > 0 ? (currentTime / duration) * 100 : 0;
73
+
74
+ const callbacksRef = useRef({ onEnded, onPlay, onPause, onTimeUpdate });
75
+ useEffect(() => {
76
+ callbacksRef.current = { onEnded, onPlay, onPause, onTimeUpdate };
77
+ });
78
+
79
+ useEffect(() => {
80
+ setIsPlaying(false);
81
+ setCurrentTime(0);
82
+ setDuration(0);
83
+ }, [src]);
84
+
85
+ useEffect(() => {
86
+ const audio = audioRef.current;
87
+ if (!audio) return;
88
+
89
+ const handleTimeUpdate = () => {
90
+ setCurrentTime(audio.currentTime);
91
+ callbacksRef.current.onTimeUpdate?.(audio.currentTime, audio.duration);
92
+ };
93
+ const handleDurationChange = () => {
94
+ if (isFinite(audio.duration)) setDuration(audio.duration);
95
+ };
96
+ const handlePlay = () => {
97
+ setIsPlaying(true);
98
+ callbacksRef.current.onPlay?.();
99
+ };
100
+ const handlePause = () => {
101
+ setIsPlaying(false);
102
+ callbacksRef.current.onPause?.();
103
+ };
104
+ const handleEnded = () => {
105
+ setIsPlaying(false);
106
+ callbacksRef.current.onEnded?.();
107
+ };
108
+ const handleVolumeChange = () => {
109
+ setVolumeState(audio.volume);
110
+ setMuted(audio.muted);
111
+ };
112
+
113
+ audio.addEventListener("timeupdate", handleTimeUpdate);
114
+ audio.addEventListener("durationchange", handleDurationChange);
115
+ audio.addEventListener("loadedmetadata", handleDurationChange);
116
+ audio.addEventListener("play", handlePlay);
117
+ audio.addEventListener("pause", handlePause);
118
+ audio.addEventListener("ended", handleEnded);
119
+ audio.addEventListener("volumechange", handleVolumeChange);
120
+
121
+ return () => {
122
+ audio.removeEventListener("timeupdate", handleTimeUpdate);
123
+ audio.removeEventListener("durationchange", handleDurationChange);
124
+ audio.removeEventListener("loadedmetadata", handleDurationChange);
125
+ audio.removeEventListener("play", handlePlay);
126
+ audio.removeEventListener("pause", handlePause);
127
+ audio.removeEventListener("ended", handleEnded);
128
+ audio.removeEventListener("volumechange", handleVolumeChange);
129
+ };
130
+ }, []);
131
+
132
+ const play = useCallback(() => {
133
+ audioRef.current?.play().catch(() => {});
134
+ }, []);
135
+
136
+ const pause = useCallback(() => {
137
+ audioRef.current?.pause();
138
+ }, []);
139
+
140
+ const toggle = useCallback(() => {
141
+ const audio = audioRef.current;
142
+ if (!audio) return;
143
+ if (audio.paused) {
144
+ audio.play().catch(() => {});
145
+ } else {
146
+ audio.pause();
147
+ }
148
+ }, []);
149
+
150
+ const reset = useCallback(() => {
151
+ const audio = audioRef.current;
152
+ if (!audio) return;
153
+ audio.pause();
154
+ audio.currentTime = 0;
155
+ setIsPlaying(false);
156
+ setCurrentTime(0);
157
+ }, []);
158
+
159
+ const seek = useCallback((seconds: number) => {
160
+ const audio = audioRef.current;
161
+ if (!audio || !isFinite(audio.duration) || audio.duration <= 0) return;
162
+ const nextTime = Math.max(0, Math.min(seconds, audio.duration));
163
+ audio.currentTime = nextTime;
164
+ setDuration(audio.duration);
165
+ setCurrentTime(nextTime);
166
+ }, []);
167
+
168
+ const seekByPercent = useCallback((percent: number) => {
169
+ const audio = audioRef.current;
170
+ if (
171
+ !audio ||
172
+ !isFinite(percent) ||
173
+ !isFinite(audio.duration) ||
174
+ audio.duration <= 0
175
+ ) {
176
+ return;
177
+ }
178
+ const clamped = Math.max(0, Math.min(percent, 100));
179
+ const nextTime = (clamped / 100) * audio.duration;
180
+ audio.currentTime = nextTime;
181
+ setDuration(audio.duration);
182
+ setCurrentTime(nextTime);
183
+ }, []);
184
+
185
+ const setVolume = useCallback((vol: number) => {
186
+ const audio = audioRef.current;
187
+ if (!audio || !isFinite(vol)) return;
188
+ const nextVolume = Math.max(0, Math.min(vol, 1));
189
+ audio.volume = nextVolume;
190
+ setVolumeState(nextVolume);
191
+ }, []);
192
+
193
+ const toggleMute = useCallback(() => {
194
+ const audio = audioRef.current;
195
+ if (!audio) return;
196
+ const nextMuted = !audio.muted;
197
+ audio.muted = nextMuted;
198
+ setMuted(nextMuted);
199
+ }, []);
200
+
201
+ const ctx = useMemo<AudioPlayerCtx>(
202
+ () => ({
203
+ isPlaying,
204
+ currentTime,
205
+ duration,
206
+ progress,
207
+ volume,
208
+ muted,
209
+ play,
210
+ pause,
211
+ toggle,
212
+ reset,
213
+ seek,
214
+ seekByPercent,
215
+ setVolume,
216
+ toggleMute,
217
+ size: size ?? "md",
218
+ shape: shape ?? "rounded",
219
+ appearance: appearance ?? "default",
220
+ }),
221
+ [
222
+ isPlaying,
223
+ currentTime,
224
+ duration,
225
+ progress,
226
+ volume,
227
+ muted,
228
+ play,
229
+ pause,
230
+ toggle,
231
+ reset,
232
+ seek,
233
+ seekByPercent,
234
+ setVolume,
235
+ toggleMute,
236
+ size,
237
+ shape,
238
+ appearance,
239
+ ],
240
+ );
241
+
242
+ return (
243
+ <AudioPlayerContext.Provider value={ctx}>
244
+ <div
245
+ ref={ref}
246
+ data-slot="audio-player"
247
+ className={cn(
248
+ audioPlayerVariants({ appearance, size, shape }),
249
+ className,
250
+ )}
251
+ {...rest}
252
+ >
253
+ <audio
254
+ ref={audioRef}
255
+ src={src}
256
+ autoPlay={autoPlay}
257
+ loop={loop}
258
+ preload="metadata"
259
+ className="hidden"
260
+ aria-hidden="true"
261
+ />
262
+ {children}
263
+ </div>
264
+ </AudioPlayerContext.Provider>
265
+ );
266
+ }
267
+
268
+ AudioPlayerBase.displayName = "AudioPlayer";
269
+
270
+ export function AudioPlayerProgress({
271
+ className,
272
+ ref,
273
+ ...rest
274
+ }: AudioPlayerProgressProps) {
275
+ const { progress, seekByPercent, size, shape } = useAudioPlayer();
276
+ const trackRef = useRef<HTMLDivElement>(null);
277
+ const draggingRef = useRef(false);
278
+
279
+ const getPercentFromEvent = useCallback((clientX: number): number => {
280
+ const el = trackRef.current;
281
+ if (!el) return 0;
282
+ const { left, width } = el.getBoundingClientRect();
283
+ return Math.max(0, Math.min(((clientX - left) / width) * 100, 100));
284
+ }, []);
285
+
286
+ const handleClick = useCallback(
287
+ (e: MouseEvent<HTMLDivElement>) => {
288
+ if (draggingRef.current) return;
289
+ seekByPercent(getPercentFromEvent(e.clientX));
290
+ },
291
+ [getPercentFromEvent, seekByPercent],
292
+ );
293
+
294
+ const handlePointerDown = useCallback(
295
+ (e: PointerEvent<HTMLDivElement>) => {
296
+ draggingRef.current = true;
297
+ e.currentTarget.setPointerCapture?.(e.pointerId);
298
+ seekByPercent(getPercentFromEvent(e.clientX));
299
+ },
300
+ [getPercentFromEvent, seekByPercent],
301
+ );
302
+
303
+ const handlePointerMove = useCallback(
304
+ (e: PointerEvent<HTMLDivElement>) => {
305
+ if (!draggingRef.current) return;
306
+ seekByPercent(getPercentFromEvent(e.clientX));
307
+ },
308
+ [getPercentFromEvent, seekByPercent],
309
+ );
310
+
311
+ const handlePointerUp = useCallback(() => {
312
+ draggingRef.current = false;
313
+ }, []);
314
+
315
+ const handleKeyDown = useCallback(
316
+ (e: KeyboardEvent<HTMLDivElement>) => {
317
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") {
318
+ e.preventDefault();
319
+ seekByPercent(Math.min(progress + 1, 100));
320
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
321
+ e.preventDefault();
322
+ seekByPercent(Math.max(progress - 1, 0));
323
+ } else if (e.key === "Home") {
324
+ e.preventDefault();
325
+ seekByPercent(0);
326
+ } else if (e.key === "End") {
327
+ e.preventDefault();
328
+ seekByPercent(100);
329
+ }
330
+ },
331
+ [progress, seekByPercent],
332
+ );
333
+
334
+ return (
335
+ <div
336
+ ref={(node) => {
337
+ (trackRef as React.MutableRefObject<HTMLDivElement | null>).current =
338
+ node;
339
+ if (typeof ref === "function") ref(node);
340
+ else if (ref)
341
+ (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
342
+ }}
343
+ data-slot="audio-player-progress"
344
+ role="slider"
345
+ aria-label="Audio progress"
346
+ aria-valuenow={Math.round(progress)}
347
+ aria-valuemin={0}
348
+ aria-valuemax={100}
349
+ tabIndex={0}
350
+ className={cn(
351
+ audioPlayerTrackVariants({ size, shape }),
352
+ "group",
353
+ className,
354
+ )}
355
+ onClick={handleClick}
356
+ onPointerDown={handlePointerDown}
357
+ onPointerMove={handlePointerMove}
358
+ onPointerUp={handlePointerUp}
359
+ onPointerCancel={handlePointerUp}
360
+ onKeyDown={handleKeyDown}
361
+ {...rest}
362
+ >
363
+ <div
364
+ data-slot="audio-player-bar"
365
+ className={cn(
366
+ audioPlayerBarVariants(),
367
+ "rounded-[inherit] group-hover:opacity-90 transition-opacity",
368
+ )}
369
+ style={{ transform: `scaleX(${progress / 100})` }}
370
+ />
371
+ </div>
372
+ );
373
+ }
374
+
375
+ AudioPlayerProgress.displayName = "AudioPlayerProgress";
376
+
377
+ export function AudioPlayerTime({
378
+ className,
379
+ format = formatTime,
380
+ }: AudioPlayerTimeProps) {
381
+ const { currentTime, duration } = useAudioPlayer();
382
+ return (
383
+ <div
384
+ data-slot="audio-player-time"
385
+ className={cn(
386
+ audioPlayerTimeVariants(),
387
+ "flex items-center gap-1",
388
+ className,
389
+ )}
390
+ >
391
+ <span aria-label="Current time">{format(currentTime)}</span>
392
+ <span aria-hidden="true">/</span>
393
+ <span aria-label="Total duration">{format(duration)}</span>
394
+ </div>
395
+ );
396
+ }
397
+
398
+ AudioPlayerTime.displayName = "AudioPlayerTime";
399
+
400
+ export function AudioPlayerVolume({
401
+ className,
402
+ ref,
403
+ ...rest
404
+ }: AudioPlayerVolumeProps) {
405
+ const { volume, muted, setVolume, toggleMute } = useAudioPlayer();
406
+ const trackRef = useRef<HTMLDivElement>(null);
407
+ const draggingRef = useRef(false);
408
+
409
+ const getVolumeFromEvent = useCallback((clientX: number): number => {
410
+ const el = trackRef.current;
411
+ if (!el) return 0;
412
+ const { left, width } = el.getBoundingClientRect();
413
+ return Math.max(0, Math.min((clientX - left) / width, 1));
414
+ }, []);
415
+
416
+ const handlePointerDown = useCallback(
417
+ (e: PointerEvent<HTMLDivElement>) => {
418
+ draggingRef.current = true;
419
+ e.currentTarget.setPointerCapture?.(e.pointerId);
420
+ setVolume(getVolumeFromEvent(e.clientX));
421
+ },
422
+ [getVolumeFromEvent, setVolume],
423
+ );
424
+
425
+ const handlePointerMove = useCallback(
426
+ (e: PointerEvent<HTMLDivElement>) => {
427
+ if (!draggingRef.current) return;
428
+ setVolume(getVolumeFromEvent(e.clientX));
429
+ },
430
+ [getVolumeFromEvent, setVolume],
431
+ );
432
+
433
+ const handlePointerUp = useCallback(() => {
434
+ draggingRef.current = false;
435
+ }, []);
436
+
437
+ const handleClick = useCallback(
438
+ (e: MouseEvent<HTMLDivElement>) => {
439
+ if (draggingRef.current) return;
440
+ setVolume(getVolumeFromEvent(e.clientX));
441
+ },
442
+ [getVolumeFromEvent, setVolume],
443
+ );
444
+
445
+ const displayVolume = muted ? 0 : volume;
446
+
447
+ const handleKeyDown = useCallback(
448
+ (e: KeyboardEvent<HTMLDivElement>) => {
449
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") {
450
+ e.preventDefault();
451
+ setVolume(Math.min(volume + 0.05, 1));
452
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
453
+ e.preventDefault();
454
+ setVolume(Math.max(volume - 0.05, 0));
455
+ } else if (e.key === "Home") {
456
+ e.preventDefault();
457
+ setVolume(0);
458
+ } else if (e.key === "End") {
459
+ e.preventDefault();
460
+ setVolume(1);
461
+ }
462
+ },
463
+ [volume, setVolume],
464
+ );
465
+
466
+ return (
467
+ <div
468
+ data-slot="audio-player-volume"
469
+ className={cn("flex items-center gap-2", className)}
470
+ {...rest}
471
+ >
472
+ <button
473
+ type="button"
474
+ aria-label={muted ? "Unmute" : "Mute"}
475
+ onClick={toggleMute}
476
+ className="shrink-0 rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--audio-fill,#0f172a)]"
477
+ >
478
+ {displayVolume === 0 ? (
479
+ <svg
480
+ className="h-4 w-4"
481
+ viewBox="0 0 24 24"
482
+ fill="none"
483
+ stroke="currentColor"
484
+ strokeWidth={2}
485
+ strokeLinecap="round"
486
+ strokeLinejoin="round"
487
+ aria-hidden="true"
488
+ >
489
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
490
+ <line x1="23" y1="9" x2="17" y2="15" />
491
+ <line x1="17" y1="9" x2="23" y2="15" />
492
+ </svg>
493
+ ) : displayVolume < 0.5 ? (
494
+ <svg
495
+ className="h-4 w-4"
496
+ viewBox="0 0 24 24"
497
+ fill="none"
498
+ stroke="currentColor"
499
+ strokeWidth={2}
500
+ strokeLinecap="round"
501
+ strokeLinejoin="round"
502
+ aria-hidden="true"
503
+ >
504
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
505
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
506
+ </svg>
507
+ ) : (
508
+ <svg
509
+ className="h-4 w-4"
510
+ viewBox="0 0 24 24"
511
+ fill="none"
512
+ stroke="currentColor"
513
+ strokeWidth={2}
514
+ strokeLinecap="round"
515
+ strokeLinejoin="round"
516
+ aria-hidden="true"
517
+ >
518
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" />
519
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14" />
520
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07" />
521
+ </svg>
522
+ )}
523
+ </button>
524
+ <div
525
+ ref={(node) => {
526
+ (trackRef as React.MutableRefObject<HTMLDivElement | null>).current =
527
+ node;
528
+ if (typeof ref === "function") ref(node);
529
+ else if (ref)
530
+ (ref as React.MutableRefObject<HTMLDivElement | null>).current =
531
+ node;
532
+ }}
533
+ role="slider"
534
+ aria-label="Volume"
535
+ aria-valuenow={Math.round(displayVolume * 100)}
536
+ aria-valuemin={0}
537
+ aria-valuemax={100}
538
+ tabIndex={0}
539
+ className="relative h-1.5 w-20 cursor-pointer overflow-hidden rounded-full bg-[var(--zui-audio-player-track-bg,var(--zui-surface-muted,#0000001a))] dark:bg-[var(--zui-audio-player-track-bg-dark,var(--zui-surface-muted-dark,#ffffff1a))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--audio-fill,#0f172a)]"
540
+ onClick={handleClick}
541
+ onPointerDown={handlePointerDown}
542
+ onPointerMove={handlePointerMove}
543
+ onPointerUp={handlePointerUp}
544
+ onPointerCancel={handlePointerUp}
545
+ onKeyDown={handleKeyDown}
546
+ >
547
+ <div
548
+ data-slot="audio-player-volume-bar"
549
+ className="h-full origin-left [background:var(--audio-fill)] rounded-[inherit]"
550
+ style={{ transform: `scaleX(${displayVolume})` }}
551
+ />
552
+ </div>
553
+ </div>
554
+ );
555
+ }
556
+
557
+ AudioPlayerVolume.displayName = "AudioPlayerVolume";