Files
player/src/components/menus/SettingsMenu.tsx
T
hibna b57b24d051 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.
2025-10-29 07:49:06 +03:00

202 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useRef, useState } from 'react'
import { usePlayerContext } from '../../contexts/PlayerContext'
import { SpeedIcon, SubtitlesIcon, CheckIcon, AudioIcon } from '../../icons'
import type { AudioTrack } from '../../types'
import './SettingsMenu.css'
interface SettingsMenuProps {
subtitles?: Array<{ src: string; lang: string; label: string }>
audioTracks?: AudioTrack[]
}
type MenuView = 'main' | 'speed' | 'subtitles' | 'audio'
export const SettingsMenu: React.FC<SettingsMenuProps> = ({ subtitles = [], audioTracks = [] }) => {
const { uiState, videoState, settings, setPlaybackRate, setSubtitle, setAudioTrack, toggleSettings } = usePlayerContext()
const menuRef = useRef<HTMLDivElement>(null)
const [currentView, setCurrentView] = useState<MenuView>('main')
const playbackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]
// Close menu when clicking outside
useEffect(() => {
if (!uiState.settingsOpen) return
const handleClickOutside = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
toggleSettings()
setCurrentView('main')
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [uiState.settingsOpen, toggleSettings])
// Reset to main view when menu closes
useEffect(() => {
if (!uiState.settingsOpen) {
setCurrentView('main')
}
}, [uiState.settingsOpen])
const goBack = () => {
setCurrentView('main')
}
if (!uiState.settingsOpen) return null
return (
<div ref={menuRef} className="settings-menu">
{/* Main Menu */}
{currentView === 'main' && (
<>
<div className="settings-menu-header">
<h3>Ayarlar</h3>
</div>
<div className="settings-main-options">
<button className="settings-main-option" onClick={() => setCurrentView('speed')}>
<div className="settings-main-option-icon">
<SpeedIcon size={20} color="var(--player-text)" />
</div>
<div className="settings-main-option-content">
<span className="settings-main-option-label">Hız</span>
<span className="settings-main-option-value">
{videoState.playbackRate === 1 ? 'Normal' : `${videoState.playbackRate}x`}
</span>
</div>
<div className="settings-main-option-arrow"></div>
</button>
<button className="settings-main-option" onClick={() => setCurrentView('subtitles')}>
<div className="settings-main-option-icon">
<SubtitlesIcon size={20} color="var(--player-text)" />
</div>
<div className="settings-main-option-content">
<span className="settings-main-option-label">Altyazı</span>
<span className="settings-main-option-value">
{settings.subtitle ? settings.subtitle.label : 'Kapalı'}
</span>
</div>
<div className="settings-main-option-arrow"></div>
</button>
{audioTracks.length > 0 && (
<button className="settings-main-option" onClick={() => setCurrentView('audio')}>
<div className="settings-main-option-icon">
<AudioIcon size={20} color="var(--player-text)" />
</div>
<div className="settings-main-option-content">
<span className="settings-main-option-label">Ses</span>
<span className="settings-main-option-value">
{settings.audioTrack ? settings.audioTrack.name : 'Varsayılan'}
</span>
</div>
<div className="settings-main-option-arrow"></div>
</button>
)}
</div>
</>
)}
{/* Speed Submenu */}
{currentView === 'speed' && (
<>
<div className="settings-menu-header">
<button className="settings-back-button" onClick={goBack}>
</button>
<h3>Oynatma Hızı</h3>
</div>
<div className="settings-options">
{playbackRates.map((rate) => (
<button
key={rate}
className={`settings-option ${videoState.playbackRate === rate ? 'active' : ''}`}
onClick={() => {
setPlaybackRate(rate)
setTimeout(() => goBack(), 150)
}}
>
<span>{rate === 1 ? 'Normal' : `${rate}x`}</span>
{videoState.playbackRate === rate && <CheckIcon size={16} color="var(--player-primary)" />}
</button>
))}
</div>
</>
)}
{/* Subtitles Submenu */}
{currentView === 'subtitles' && (
<>
<div className="settings-menu-header">
<button className="settings-back-button" onClick={goBack}>
</button>
<h3>Altyazı</h3>
</div>
<div className="settings-options">
<button
className={`settings-option ${!settings.subtitle ? 'active' : ''}`}
onClick={() => {
setSubtitle(null)
setTimeout(() => goBack(), 150)
}}
>
<span>Kapalı</span>
{!settings.subtitle && <CheckIcon size={16} color="var(--player-primary)" />}
</button>
{subtitles.length > 0 ? (
subtitles.map((subtitle) => (
<button
key={subtitle.lang}
className={`settings-option ${settings.subtitle?.lang === subtitle.lang ? 'active' : ''}`}
onClick={() => {
setSubtitle(subtitle)
setTimeout(() => goBack(), 150)
}}
>
<span>{subtitle.label}</span>
{settings.subtitle?.lang === subtitle.lang && <CheckIcon size={16} color="var(--player-primary)" />}
</button>
))
) : (
<div className="settings-empty-state">
<span>Altyazı mevcut değil</span>
</div>
)}
</div>
</>
)}
{/* Audio Submenu */}
{currentView === 'audio' && (
<>
<div className="settings-menu-header">
<button className="settings-back-button" onClick={goBack}>
</button>
<h3>Ses</h3>
</div>
<div className="settings-options">
{audioTracks.map((track) => (
<button
key={track.language}
className={`settings-option ${settings.audioTrack?.language === track.language ? 'active' : ''}`}
onClick={() => {
setAudioTrack(track)
setTimeout(() => goBack(), 150)
}}
>
<span>{track.name}</span>
{settings.audioTrack?.language === track.language && <CheckIcon size={16} color="var(--player-primary)" />}
</button>
))}
</div>
</>
)}
</div>
)
}