Initial commit: modern React video player library
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.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import React, { useState, useRef, useCallback } from 'react'
|
||||
import { usePlayerContext } from '../../contexts/PlayerContext'
|
||||
import { VolumeUpIcon, VolumeDownIcon, VolumeMuteIcon } from '../../icons'
|
||||
import './VolumeControl.css'
|
||||
|
||||
export const VolumeControl: React.FC = () => {
|
||||
const { videoState, setVolume, toggleMute } = usePlayerContext()
|
||||
const [showSlider, setShowSlider] = useState(false)
|
||||
const timeoutRef = useRef<number>()
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
if (timeoutRef.current) {
|
||||
window.clearTimeout(timeoutRef.current)
|
||||
}
|
||||
setShowSlider(true)
|
||||
}, [])
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
setShowSlider(false)
|
||||
}, 300)
|
||||
}, [])
|
||||
|
||||
const handleSliderChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const volume = parseFloat(e.target.value)
|
||||
setVolume(volume)
|
||||
},
|
||||
[setVolume]
|
||||
)
|
||||
|
||||
const VolumeIcon = videoState.muted ? VolumeMuteIcon : videoState.volume > 0.5 ? VolumeUpIcon : VolumeDownIcon
|
||||
|
||||
return (
|
||||
<div
|
||||
className="volume-control"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<button
|
||||
className="control-button volume-button"
|
||||
onClick={toggleMute}
|
||||
aria-label={videoState.muted ? 'Unmute' : 'Mute'}
|
||||
title={videoState.muted ? 'Unmute (M)' : 'Mute (M)'}
|
||||
>
|
||||
<VolumeIcon size={24} color="var(--player-text)" />
|
||||
</button>
|
||||
|
||||
<div className={`volume-slider-container ${showSlider ? 'visible' : ''}`}>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={videoState.muted ? 0 : videoState.volume}
|
||||
onChange={handleSliderChange}
|
||||
className="volume-slider"
|
||||
aria-label="Volume"
|
||||
/>
|
||||
<div
|
||||
className="volume-slider-fill"
|
||||
style={{ width: `${(videoState.muted ? 0 : videoState.volume) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user