2025-12-03 21:56:50 +00:00
|
|
|
/**
|
|
|
|
|
* Hook pour gérer les raccourcis clavier du player
|
|
|
|
|
* - Espace : play/pause
|
|
|
|
|
* - Flèches gauche/droite : seek backward/forward
|
|
|
|
|
* - Flèches haut/bas : volume up/down
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { useEffect, useCallback } from 'react';
|
|
|
|
|
import type { PlayerHook } from '../types';
|
|
|
|
|
|
|
|
|
|
export interface UseKeyboardShortcutsOptions {
|
|
|
|
|
enabled?: boolean;
|
|
|
|
|
seekStep?: number; // En secondes
|
|
|
|
|
volumeStep?: number; // En pourcentage
|
|
|
|
|
preventDefault?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DEFAULT_SEEK_STEP = 5; // 5 secondes
|
|
|
|
|
const DEFAULT_VOLUME_STEP = 5; // 5%
|
|
|
|
|
|
|
|
|
|
export function useKeyboardShortcuts(
|
|
|
|
|
player: PlayerHook,
|
2025-12-13 02:34:34 +00:00
|
|
|
options: UseKeyboardShortcutsOptions = {},
|
2025-12-03 21:56:50 +00:00
|
|
|
) {
|
|
|
|
|
const {
|
|
|
|
|
enabled = true,
|
|
|
|
|
seekStep = DEFAULT_SEEK_STEP,
|
|
|
|
|
volumeStep = DEFAULT_VOLUME_STEP,
|
|
|
|
|
preventDefault = true,
|
|
|
|
|
} = options;
|
|
|
|
|
|
|
|
|
|
const handleKeyDown = useCallback(
|
|
|
|
|
(e: KeyboardEvent) => {
|
|
|
|
|
if (!enabled) return;
|
|
|
|
|
|
|
|
|
|
// Ignorer si l'utilisateur est en train de taper dans un input/textarea
|
|
|
|
|
const target = e.target as HTMLElement;
|
|
|
|
|
if (
|
|
|
|
|
target &&
|
|
|
|
|
(target.tagName === 'INPUT' ||
|
|
|
|
|
target.tagName === 'TEXTAREA' ||
|
|
|
|
|
target.isContentEditable === true)
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (e.code) {
|
|
|
|
|
case 'Space': {
|
|
|
|
|
// Espace : play/pause
|
|
|
|
|
if (preventDefault) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
if (player.isPlaying) {
|
|
|
|
|
player.pause();
|
|
|
|
|
} else {
|
|
|
|
|
player.resume();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'ArrowLeft': {
|
|
|
|
|
// Flèche gauche : reculer
|
|
|
|
|
if (preventDefault) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
const newTime = Math.max(0, player.currentTime - seekStep);
|
|
|
|
|
player.seek(newTime);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'ArrowRight': {
|
|
|
|
|
// Flèche droite : avancer
|
|
|
|
|
if (preventDefault) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
const newTime = Math.min(
|
|
|
|
|
player.duration || 0,
|
2025-12-13 02:34:34 +00:00
|
|
|
player.currentTime + seekStep,
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
player.seek(newTime);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'ArrowUp': {
|
|
|
|
|
// Flèche haut : augmenter le volume
|
|
|
|
|
if (preventDefault) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
const newVolume = Math.min(100, player.volume + volumeStep);
|
|
|
|
|
player.setVolume(newVolume);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'ArrowDown': {
|
|
|
|
|
// Flèche bas : diminuer le volume
|
|
|
|
|
if (preventDefault) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
const newVolume = Math.max(0, player.volume - volumeStep);
|
|
|
|
|
player.setVolume(newVolume);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[
|
|
|
|
|
enabled,
|
|
|
|
|
preventDefault,
|
|
|
|
|
seekStep,
|
|
|
|
|
volumeStep,
|
|
|
|
|
player.isPlaying,
|
|
|
|
|
player.currentTime,
|
|
|
|
|
player.duration,
|
|
|
|
|
player.volume,
|
|
|
|
|
player.pause,
|
|
|
|
|
player.resume,
|
|
|
|
|
player.seek,
|
|
|
|
|
player.setVolume,
|
2025-12-13 02:34:34 +00:00
|
|
|
],
|
2025-12-03 21:56:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!enabled) return;
|
|
|
|
|
|
|
|
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener('keydown', handleKeyDown);
|
|
|
|
|
};
|
|
|
|
|
}, [enabled, handleKeyDown]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default useKeyboardShortcuts;
|