Merge pull request #2 from MertUyanik/codex/fix-player-controls-visibility-in-fullscreen

Fix fullscreen control visibility and refresh lint tooling
This commit is contained in:
Mert Uyanık
2025-10-29 08:16:27 +03:00
committed by GitHub
5 changed files with 150 additions and 37 deletions
+51
View File
@@ -0,0 +1,51 @@
import js from '@eslint/js'
import tsParser from '@typescript-eslint/parser'
import tsPlugin from '@typescript-eslint/eslint-plugin'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import globals from 'globals'
export default [
{
ignores: ['dist/**', 'node_modules/**']
},
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
globals: {
...globals.browser,
...globals.es2021
}
},
plugins: {
'@typescript-eslint': tsPlugin,
'react-hooks': reactHooks,
'react-refresh': reactRefresh
},
rules: {
...tsPlugin.configs.recommended.rules,
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react-refresh/only-export-components': 'warn'
}
},
{
files: ['**/*.config.{js,ts}', '**/*.config.{cjs,mjs}', 'vite.config.*', 'eslint.config.js'],
languageOptions: {
globals: {
...globals.node
}
}
}
]
+8 -6
View File
@@ -22,21 +22,23 @@
"build": "tsc && vite build", "build": "tsc && vite build",
"build:lib": "tsc && vite build --config vite.config.lib.ts", "build:lib": "tsc && vite build --config vite.config.lib.ts",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" "lint": "eslint . --report-unused-disable-directives --max-warnings 0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^18.0.0", "react": "^18.0.0",
"react-dom": "^18.0.0" "react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.7.0",
"globals": "^15.6.0",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1", "@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^7.13.1", "@typescript-eslint/parser": "^8.5.0",
"@vitejs/plugin-react": "^4.3.1", "@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0", "eslint": "^9.7.0",
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-react-refresh": "^0.5.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"typescript": "^5.2.2", "typescript": "^5.2.2",
+2 -2
View File
@@ -42,7 +42,7 @@ export const VideoElement: React.FC<VideoElementProps> = ({
onSeeked, onSeeked,
onAudioTracksLoaded, onAudioTracksLoaded,
}) => { }) => {
const { videoRef, containerRef, setVideoState, toggleFullscreen, settings } = usePlayerContext() const { videoRef, setVideoState, toggleFullscreen, settings } = usePlayerContext()
const lastClickTimeRef = React.useRef<number>(0) const lastClickTimeRef = React.useRef<number>(0)
const [availableAudioTracks, setAvailableAudioTracks] = useState<AudioTrack[]>([]) const [availableAudioTracks, setAvailableAudioTracks] = useState<AudioTrack[]>([])
@@ -368,7 +368,7 @@ export const VideoElement: React.FC<VideoElementProps> = ({
}, [settings.audioTrack, availableAudioTracks, videoRef]) }, [settings.audioTrack, availableAudioTracks, videoRef])
return ( return (
<div ref={containerRef} className="video-container"> <div className="video-container">
<video <video
ref={videoRef} ref={videoRef}
className="video-element" className="video-element"
+88 -29
View File
@@ -1,5 +1,5 @@
import React, { useEffect, useState, useCallback } from 'react' import React, { useEffect, useState, useCallback } from 'react'
import { PlayerProvider } from '../contexts/PlayerContext' import { PlayerProvider, usePlayerContext } from '../contexts/PlayerContext'
import { VideoElement } from './VideoElement' import { VideoElement } from './VideoElement'
import { ControlsLayer } from './ControlsLayer' import { ControlsLayer } from './ControlsLayer'
import type { VideoPlayerProps, AudioTrack } from '../types' import type { VideoPlayerProps, AudioTrack } from '../types'
@@ -14,6 +14,69 @@ if (!polyfillsInitialized) {
polyfillsInitialized = true polyfillsInitialized = true
} }
const VideoPlayerContent: React.FC<
VideoPlayerProps & {
audioTracks: AudioTrack[]
onAudioTracksLoadedInternal: (tracks: AudioTrack[]) => void
}
> = ({
src,
poster,
autoplay = false,
loop = false,
muted = false,
controls = true,
subtitles = [],
keyboardShortcuts = true,
pictureInPicture = true,
className = '',
style,
onPlay,
onPause,
onEnded,
onTimeUpdate,
onVolumeChange,
onError,
onLoadedMetadata,
onSeeking,
onSeeked,
audioTracks,
onAudioTracksLoadedInternal,
}) => {
const { containerRef } = usePlayerContext()
return (
<div ref={containerRef} className={`video-player ${className}`} style={style}>
<VideoElement
src={src}
poster={poster}
autoplay={autoplay}
loop={loop}
muted={muted}
subtitles={subtitles}
onPlay={onPlay}
onPause={onPause}
onEnded={onEnded}
onTimeUpdate={onTimeUpdate}
onVolumeChange={onVolumeChange}
onError={onError}
onLoadedMetadata={onLoadedMetadata}
onSeeking={onSeeking}
onSeeked={onSeeked}
onAudioTracksLoaded={onAudioTracksLoadedInternal}
/>
{controls && (
<ControlsLayer
keyboardShortcuts={keyboardShortcuts}
pictureInPicture={pictureInPicture}
subtitles={subtitles}
audioTracks={audioTracks}
/>
)}
</div>
)
}
export const VideoPlayer: React.FC<VideoPlayerProps> = ({ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
src, src,
poster, poster,
@@ -56,34 +119,30 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
return ( return (
<PlayerProvider initialMuted={muted}> <PlayerProvider initialMuted={muted}>
<div className={`video-player ${className}`} style={style}> <VideoPlayerContent
<VideoElement src={src}
src={src} poster={poster}
poster={poster} autoplay={autoplay}
autoplay={autoplay} loop={loop}
loop={loop} muted={muted}
muted={muted} controls={controls}
subtitles={subtitles} subtitles={subtitles}
onPlay={onPlay} keyboardShortcuts={keyboardShortcuts}
onPause={onPause} pictureInPicture={pictureInPicture}
onEnded={onEnded} className={className}
onTimeUpdate={onTimeUpdate} style={style}
onVolumeChange={onVolumeChange} onPlay={onPlay}
onError={onError} onPause={onPause}
onLoadedMetadata={onLoadedMetadata} onEnded={onEnded}
onSeeking={onSeeking} onTimeUpdate={onTimeUpdate}
onSeeked={onSeeked} onVolumeChange={onVolumeChange}
onAudioTracksLoaded={handleAudioTracksLoaded} onError={onError}
/> onLoadedMetadata={onLoadedMetadata}
{controls && ( onSeeking={onSeeking}
<ControlsLayer onSeeked={onSeeked}
keyboardShortcuts={keyboardShortcuts} audioTracks={audioTracks}
pictureInPicture={pictureInPicture} onAudioTracksLoadedInternal={handleAudioTracksLoaded}
subtitles={subtitles} />
audioTracks={audioTracks}
/>
)}
</div>
</PlayerProvider> </PlayerProvider>
) )
} }
@@ -9,6 +9,7 @@ export const SettingsButton: React.FC = () => {
return ( return (
<button <button
className="control-button settings-button" className="control-button settings-button"
onMouseDown={(event) => event.stopPropagation()}
onClick={toggleSettings} onClick={toggleSettings}
aria-label="Settings" aria-label="Settings"
title="Settings" title="Settings"