feat: add configurable props for DX improvements
- Configurable keyboard shortcuts (seekSmall, seekLarge, volumeStep, disabled keys) - Configurable touch gestures (maxSeekSeconds, maxVolumeChange, doubleTapSeekSeconds) - Configurable auto-hide timeout via controlsAutoHideDelay prop - Configurable playback rates via playbackRates prop - Aspect ratio support (16:9, 4:3, 21:9, 1:1, 9:16, custom) - Extended theme system (fontFamily, borderRadius, overlayOpacity, controlsBackground, etc.) - Custom translations support via translations prop - Children/slot system (children, controlsLeftExtra, controlsRightExtra) - Ref forwarding with VideoPlayerHandle imperative API - Analytics events (onFirstPlay, onBufferStart, onBufferEnd, onQualityChange) - iOS Safari volume slider auto-hiding - SSR guards for feature detection utilities - prefers-reduced-motion CSS media query support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState, useCallback, lazy, Suspense } from 'react'
|
||||
import React, { useEffect, useRef, useState, useCallback, lazy, Suspense, type ReactNode } from 'react'
|
||||
import { usePlayerContext } from '../contexts/PlayerContext'
|
||||
import { PlayPauseButton } from './controls/PlayPauseButton'
|
||||
import { ProgressBar } from './controls/ProgressBar'
|
||||
@@ -11,25 +11,38 @@ import { LoadingSpinner } from './overlays/LoadingSpinner'
|
||||
import { CenterPlayButton } from './controls/CenterPlayButton'
|
||||
import { useKeyboardShortcuts } from '../hooks/useKeyboardShortcuts'
|
||||
import { useTouchGestures } from '../hooks/useTouchGestures'
|
||||
import type { SubtitleTrack, AudioTrack, VideoQuality } from '../types'
|
||||
import { features } from '../utils/polyfills'
|
||||
import type { SubtitleTrack, AudioTrack, VideoQuality, KeyboardShortcutConfig, TouchConfig } from '../types'
|
||||
import './ControlsLayer.css'
|
||||
|
||||
const SettingsMenu = lazy(() => import('./menus/SettingsMenu').then(module => ({ default: module.SettingsMenu })))
|
||||
|
||||
interface ControlsLayerProps {
|
||||
keyboardShortcuts?: boolean
|
||||
keyboardShortcutConfig?: KeyboardShortcutConfig
|
||||
pictureInPicture?: boolean
|
||||
subtitles?: SubtitleTrack[]
|
||||
audioTracks?: AudioTrack[]
|
||||
qualities?: VideoQuality[]
|
||||
controlsAutoHideDelay?: number
|
||||
playbackRates?: number[]
|
||||
touchConfig?: TouchConfig
|
||||
controlsLeftExtra?: ReactNode
|
||||
controlsRightExtra?: ReactNode
|
||||
}
|
||||
|
||||
export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
keyboardShortcuts = true,
|
||||
keyboardShortcutConfig,
|
||||
pictureInPicture = true,
|
||||
subtitles = [],
|
||||
audioTracks = [],
|
||||
qualities = [],
|
||||
controlsAutoHideDelay = 3000,
|
||||
playbackRates,
|
||||
touchConfig,
|
||||
controlsLeftExtra,
|
||||
controlsRightExtra,
|
||||
}) => {
|
||||
const { videoState, uiState, togglePlay, toggleFullscreen, showControls, hideControls, translations } =
|
||||
usePlayerContext()
|
||||
@@ -61,8 +74,8 @@ export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
clearHideTimeout()
|
||||
hideTimeoutRef.current = window.setTimeout(() => {
|
||||
hideControls()
|
||||
}, 3000)
|
||||
}, [autoHideEnabled, clearHideTimeout, hideControls])
|
||||
}, controlsAutoHideDelay)
|
||||
}, [autoHideEnabled, clearHideTimeout, hideControls, controlsAutoHideDelay])
|
||||
|
||||
// Keep controls visible when not playing or when any menu is open
|
||||
useEffect(() => {
|
||||
@@ -143,10 +156,10 @@ export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
}, [autoHideEnabled, showControls])
|
||||
|
||||
// Keyboard shortcuts
|
||||
useKeyboardShortcuts(keyboardShortcuts)
|
||||
useKeyboardShortcuts(keyboardShortcuts, keyboardShortcutConfig)
|
||||
|
||||
// Touch gestures
|
||||
useTouchGestures(containerRef)
|
||||
useTouchGestures(containerRef, touchConfig)
|
||||
|
||||
// Handle click for play/pause and double-click for fullscreen
|
||||
const handleClick = useCallback(
|
||||
@@ -221,7 +234,7 @@ export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
<div className="controls-row">
|
||||
<div className="controls-left">
|
||||
<PlayPauseButton />
|
||||
<VolumeControl />
|
||||
{features.hasVolumeControl() && <VolumeControl />}
|
||||
{/* Time display - hidden for live broadcasts */}
|
||||
{!videoState.isLiveBroadcast && <TimeDisplay />}
|
||||
{/* Show "LIVE" badge for live broadcasts */}
|
||||
@@ -231,13 +244,20 @@ export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
<span className="live-text">{translations.live}</span>
|
||||
</div>
|
||||
)}
|
||||
{controlsLeftExtra}
|
||||
</div>
|
||||
|
||||
<div className="controls-right">
|
||||
{controlsRightExtra}
|
||||
<div style={{ position: 'relative' }}>
|
||||
<SettingsButton />
|
||||
<Suspense fallback={null}>
|
||||
<SettingsMenu subtitles={subtitles} audioTracks={audioTracks} qualities={qualities} />
|
||||
<SettingsMenu
|
||||
subtitles={subtitles}
|
||||
audioTracks={audioTracks}
|
||||
qualities={qualities}
|
||||
playbackRates={playbackRates}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
{pictureInPicture && <PIPButton />}
|
||||
|
||||
Reference in New Issue
Block a user