b57b24d051
Add all source files for a feature-rich, reusable video player built with React, TypeScript, and Vite. Includes core components, context, hooks, utilities, styles, demo app, and configuration files.
121 lines
2.6 KiB
TypeScript
121 lines
2.6 KiB
TypeScript
import { useEffect } from 'react'
|
|
import { usePlayerContext } from '../contexts/PlayerContext'
|
|
|
|
export const useKeyboardShortcuts = (enabled: boolean = true) => {
|
|
const {
|
|
videoState,
|
|
togglePlay,
|
|
seek,
|
|
setVolume,
|
|
toggleMute,
|
|
toggleFullscreen,
|
|
togglePictureInPicture,
|
|
} = usePlayerContext()
|
|
|
|
useEffect(() => {
|
|
if (!enabled) return
|
|
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
// Don't trigger if user is typing in an input
|
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
|
|
return
|
|
}
|
|
|
|
switch (e.key.toLowerCase()) {
|
|
case ' ':
|
|
case 'k':
|
|
e.preventDefault()
|
|
togglePlay()
|
|
break
|
|
|
|
case 'arrowleft':
|
|
e.preventDefault()
|
|
seek(Math.max(0, videoState.currentTime - 5))
|
|
break
|
|
|
|
case 'arrowright':
|
|
e.preventDefault()
|
|
seek(Math.min(videoState.duration, videoState.currentTime + 5))
|
|
break
|
|
|
|
case 'j':
|
|
e.preventDefault()
|
|
seek(Math.max(0, videoState.currentTime - 10))
|
|
break
|
|
|
|
case 'l':
|
|
e.preventDefault()
|
|
seek(Math.min(videoState.duration, videoState.currentTime + 10))
|
|
break
|
|
|
|
case 'arrowup':
|
|
e.preventDefault()
|
|
setVolume(Math.min(1, videoState.volume + 0.1))
|
|
break
|
|
|
|
case 'arrowdown':
|
|
e.preventDefault()
|
|
setVolume(Math.max(0, videoState.volume - 0.1))
|
|
break
|
|
|
|
case 'm':
|
|
e.preventDefault()
|
|
toggleMute()
|
|
break
|
|
|
|
case 'f':
|
|
e.preventDefault()
|
|
toggleFullscreen()
|
|
break
|
|
|
|
case 'p':
|
|
e.preventDefault()
|
|
togglePictureInPicture()
|
|
break
|
|
|
|
case '0':
|
|
case 'home':
|
|
e.preventDefault()
|
|
seek(0)
|
|
break
|
|
|
|
case 'end':
|
|
e.preventDefault()
|
|
seek(videoState.duration)
|
|
break
|
|
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
e.preventDefault()
|
|
const percent = parseInt(e.key) / 10
|
|
seek(videoState.duration * percent)
|
|
break
|
|
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
window.addEventListener('keydown', handleKeyDown)
|
|
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
}, [
|
|
enabled,
|
|
videoState.currentTime,
|
|
videoState.duration,
|
|
videoState.volume,
|
|
togglePlay,
|
|
seek,
|
|
setVolume,
|
|
toggleMute,
|
|
toggleFullscreen,
|
|
togglePictureInPicture,
|
|
])
|
|
}
|