11import { useQuery } from '@tanstack/react-query' ;
22import { Pause , Play , Repeat , Volume2 , VolumeX , X } from 'lucide-react' ;
3- import { useEffect , useMemo , useRef , useState } from 'react' ;
3+ import { useEffect , useId , useMemo , useRef , useState } from 'react' ;
44import WaveSurfer from 'wavesurfer.js' ;
55import { Button } from '@/components/ui/button' ;
66import { Slider } from '@/components/ui/slider' ;
@@ -12,6 +12,7 @@ import { usePlatform } from '@/platform/PlatformContext';
1212
1313export function AudioPlayer ( ) {
1414 const platform = usePlatform ( ) ;
15+ const volumeLabelId = useId ( ) ;
1516 const {
1617 audioUrl,
1718 audioId,
@@ -831,6 +832,13 @@ export function AudioPlayer() {
831832 disabled = { isLoading || duration === 0 }
832833 className = "shrink-0"
833834 title = { duration === 0 && ! isLoading ? 'Audio not loaded' : '' }
835+ aria-label = {
836+ duration === 0 && ! isLoading
837+ ? 'Audio not loaded'
838+ : isPlaying
839+ ? 'Pause'
840+ : 'Play'
841+ }
834842 >
835843 { isPlaying ? < Pause className = "h-5 w-5" /> : < Play className = "h-5 w-5" /> }
836844 </ Button >
@@ -845,6 +853,8 @@ export function AudioPlayer() {
845853 max = { 100 }
846854 step = { 0.1 }
847855 className = "w-full"
856+ aria-label = "Playback position"
857+ aria-valuetext = { `${ formatAudioDuration ( currentTime ) } of ${ formatAudioDuration ( duration ) } ` }
848858 />
849859 ) }
850860 { isLoading && (
@@ -872,26 +882,33 @@ export function AudioPlayer() {
872882 onClick = { toggleLoop }
873883 className = { isLooping ? 'text-primary' : '' }
874884 title = "Toggle loop"
885+ aria-label = { isLooping ? 'Stop looping' : 'Loop' }
875886 >
876887 < Repeat className = "h-4 w-4" />
877888 </ Button >
878889
879890 { /* Volume Control */ }
880- < div className = "flex items-center gap-2 shrink-0 w-[120px]" >
891+ < div className = "flex items-center gap-2 shrink-0 w-[120px]" role = "group" aria-label = "Volume" >
881892 < Button
882893 variant = "ghost"
883894 size = "icon"
884895 onClick = { ( ) => setVolume ( volume > 0 ? 0 : 1 ) }
885896 className = "h-8 w-8"
897+ aria-label = { volume > 0 ? 'Mute' : 'Unmute' }
886898 >
887899 { volume > 0 ? < Volume2 className = "h-4 w-4" /> : < VolumeX className = "h-4 w-4" /> }
888900 </ Button >
901+ < span id = { volumeLabelId } className = "sr-only" >
902+ Volume level, { Math . round ( volume * 100 ) } %
903+ </ span >
889904 < Slider
890905 value = { [ volume * 100 ] }
891906 onValueChange = { handleVolumeChange }
892907 max = { 100 }
893908 step = { 1 }
894909 className = "flex-1"
910+ aria-labelledby = { volumeLabelId }
911+ aria-valuetext = { `${ Math . round ( volume * 100 ) } %` }
895912 />
896913 </ div >
897914
@@ -902,6 +919,7 @@ export function AudioPlayer() {
902919 onClick = { handleClose }
903920 className = "shrink-0"
904921 title = "Close player"
922+ aria-label = "Close player"
905923 >
906924 < X className = "h-5 w-5" />
907925 </ Button >
0 commit comments