mirror of
https://github.com/Qortal/qapp-core.git
synced 2025-06-19 03:11:20 +00:00
change to popover
This commit is contained in:
parent
793449f486
commit
ad2b56d95a
@ -1,4 +1,16 @@
|
||||
import { Box, IconButton, Popper, Slider, Typography } from "@mui/material";
|
||||
import {
|
||||
alpha,
|
||||
Box,
|
||||
ButtonBase,
|
||||
Divider,
|
||||
Fade,
|
||||
IconButton,
|
||||
Popover,
|
||||
Popper,
|
||||
Slider,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
export const fontSizeExSmall = "60%";
|
||||
export const fontSizeSmall = "80%";
|
||||
import AspectRatioIcon from "@mui/icons-material/AspectRatio";
|
||||
@ -14,9 +26,12 @@ import {
|
||||
import { formatTime } from "../../utils/time.js";
|
||||
import { CustomFontTooltip } from "./CustomFontTooltip.js";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import SlowMotionVideoIcon from "@mui/icons-material/SlowMotionVideo";
|
||||
const buttonPaddingBig = "6px";
|
||||
const buttonPaddingSmall = "4px";
|
||||
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
|
||||
import ArrowBackIosIcon from "@mui/icons-material/ArrowBackIos";
|
||||
import CheckIcon from "@mui/icons-material/Check";
|
||||
|
||||
export const PlayButton = ({ togglePlay, isPlaying, isScreenSmall }: any) => {
|
||||
return (
|
||||
@ -55,7 +70,7 @@ export const ProgressSlider = ({progress, duration, playerRef}: any) => {
|
||||
|
||||
const [hoverX, setHoverX] = useState<number | null>(null);
|
||||
const [thumbnailUrl, setThumbnailUrl] = useState<string | null>(null);
|
||||
const [showDuration, setShowDuration] = useState(0)
|
||||
const [showDuration, setShowDuration] = useState(0);
|
||||
const onProgressChange = (_: any, value: number | number[]) => {
|
||||
if (!playerRef.current) return;
|
||||
|
||||
@ -69,8 +84,6 @@ export const ProgressSlider = ({progress, duration, playerRef}: any) => {
|
||||
const debounceTimeoutRef = useRef<any>(null);
|
||||
const previousBlobUrlRef = useRef<string | null>(null);
|
||||
|
||||
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent) => {
|
||||
const slider = sliderRef.current;
|
||||
if (!slider) return;
|
||||
@ -79,10 +92,10 @@ export const ProgressSlider = ({progress, duration, playerRef}: any) => {
|
||||
const x = e.clientX - rect.left;
|
||||
const percent = x / rect.width;
|
||||
const time = Math.min(Math.max(0, percent * duration), duration);
|
||||
console.log('hello100')
|
||||
console.log("hello100");
|
||||
setHoverX(e.clientX);
|
||||
|
||||
setShowDuration(time)
|
||||
setShowDuration(time);
|
||||
if (debounceTimeoutRef.current) clearTimeout(debounceTimeoutRef.current);
|
||||
|
||||
// debounceTimeoutRef.current = setTimeout(() => {
|
||||
@ -113,26 +126,28 @@ export const ProgressSlider = ({progress, duration, playerRef}: any) => {
|
||||
|
||||
const hoverAnchorRef = useRef<HTMLDivElement | null>(null);
|
||||
if (hoverX) {
|
||||
console.log('thumbnailUrl', thumbnailUrl, hoverX)
|
||||
|
||||
console.log("thumbnailUrl", thumbnailUrl, hoverX);
|
||||
}
|
||||
|
||||
console.log('duration', duration)
|
||||
console.log("duration", duration);
|
||||
|
||||
return (
|
||||
<Box position="relative" sx={{
|
||||
width: '100%',
|
||||
padding: '0px 10px'
|
||||
}}>
|
||||
<Box
|
||||
position="relative"
|
||||
sx={{
|
||||
width: "100%",
|
||||
padding: "0px 10px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
ref={hoverAnchorRef}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
left: hoverX ?? -9999,
|
||||
top: 0,
|
||||
width: '1px',
|
||||
height: '1px',
|
||||
pointerEvents: 'none',
|
||||
width: "1px",
|
||||
height: "1px",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
<Slider
|
||||
@ -145,36 +160,53 @@ console.log('thumbnailUrl', thumbnailUrl, hoverX)
|
||||
max={duration || 100}
|
||||
step={0.1}
|
||||
sx={{
|
||||
|
||||
color: "#00abff",
|
||||
padding: "0px",
|
||||
borderRadius: '0px',
|
||||
height: '0px',
|
||||
borderRadius: "0px",
|
||||
height: "0px",
|
||||
"@media (pointer: coarse)": { padding: "0px" },
|
||||
"& .MuiSlider-thumb": {
|
||||
backgroundColor: "red",
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
},
|
||||
"& .MuiSlider-thumb::after": { width: "14px", height: "14px", backgroundColor: 'red' },
|
||||
"& .MuiSlider-rail": { opacity: 0.5, height: "6px", backgroundColor: '#73859f80' },
|
||||
"& .MuiSlider-track": { height: "6px", border: "0px" , backgroundColor: 'red'},
|
||||
"& .MuiSlider-thumb::after": {
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: "red",
|
||||
},
|
||||
"& .MuiSlider-rail": {
|
||||
opacity: 0.5,
|
||||
height: "6px",
|
||||
backgroundColor: "#73859f80",
|
||||
},
|
||||
"& .MuiSlider-track": {
|
||||
height: "6px",
|
||||
border: "0px",
|
||||
backgroundColor: "red",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{hoverX !== null && (
|
||||
<Popper
|
||||
open
|
||||
anchorEl={hoverAnchorRef.current} placement="top"
|
||||
|
||||
|
||||
anchorEl={hoverAnchorRef.current}
|
||||
placement="top"
|
||||
disablePortal
|
||||
modifiers={[{ name: "offset", options: { offset: [-20, 0] } }]}
|
||||
modifiers={[{ name: "offset", options: { offset: [-10, 0] } }]}
|
||||
|
||||
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
bgcolor: alpha("#181818", 0.75),
|
||||
padding: '5px',
|
||||
borderRadius: '5px'
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
{/* <Box
|
||||
sx={{
|
||||
width: 250,
|
||||
@ -197,11 +229,15 @@ console.log('thumbnailUrl', thumbnailUrl, hoverX)
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover" }}
|
||||
/>
|
||||
</Box> */}
|
||||
<Typography sx={{
|
||||
fontSize: '0.8rom',
|
||||
textShadow: '0 0 5px rgba(0, 0, 0, 0.7)'
|
||||
|
||||
}}>{formatTime(showDuration)}</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "0.8rom",
|
||||
textShadow: "0 0 5px rgba(0, 0, 0, 0.7)",
|
||||
fontFamily: "sans-serif"
|
||||
}}
|
||||
>
|
||||
{formatTime(showDuration)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Popper>
|
||||
)}
|
||||
@ -210,8 +246,6 @@ console.log('thumbnailUrl', thumbnailUrl, hoverX)
|
||||
};
|
||||
|
||||
export const VideoTime = ({ progress, isScreenSmall, duration }: any) => {
|
||||
|
||||
|
||||
return (
|
||||
<CustomFontTooltip
|
||||
title="Seek video in 10% increments (0-9)"
|
||||
@ -221,14 +255,15 @@ export const VideoTime = ({progress, isScreenSmall, duration}: any) => {
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: isScreenSmall ? fontSizeExSmall : fontSizeSmall,
|
||||
color: 'white',
|
||||
visibility: typeof duration !== 'number' ? 'hidden' : 'visible',
|
||||
whiteSpace: 'nowrap',
|
||||
color: "white",
|
||||
visibility: typeof duration !== "number" ? "hidden" : "visible",
|
||||
whiteSpace: "nowrap",
|
||||
fontFamily: "sans-serif"
|
||||
}}
|
||||
>
|
||||
{typeof duration === 'number' ? formatTime(progress) : ''}
|
||||
{' / '}
|
||||
{typeof duration === 'number' ? formatTime(duration) : ''}
|
||||
{typeof duration === "number" ? formatTime(progress) : ""}
|
||||
{" / "}
|
||||
{typeof duration === "number" ? formatTime(duration) : ""}
|
||||
</Typography>
|
||||
</CustomFontTooltip>
|
||||
);
|
||||
@ -289,29 +324,169 @@ export const VolumeControl = ({ sliderWidth, onVolumeChange, volume }: any) => {
|
||||
sx={{ display: "flex", gap: "5px", alignItems: "center", width: "100%" }}
|
||||
>
|
||||
<VolumeButton />
|
||||
<VolumeSlider width={sliderWidth} onVolumeChange={onVolumeChange} volume={volume} />
|
||||
<VolumeSlider
|
||||
width={sliderWidth}
|
||||
onVolumeChange={onVolumeChange}
|
||||
volume={volume}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const PlaybackRate = ({playbackRate, increaseSpeed, isScreenSmall}: any) => {
|
||||
const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3];
|
||||
|
||||
export const PlaybackRate = ({
|
||||
playbackRate,
|
||||
increaseSpeed,
|
||||
isScreenSmall,
|
||||
onSelect,
|
||||
}: any) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const btnRef = useRef(null);
|
||||
const theme = useTheme();
|
||||
const onBack = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomFontTooltip
|
||||
title="Video Speed. Increase (+ or >), Decrease (- or <)"
|
||||
placement="bottom"
|
||||
arrow
|
||||
>
|
||||
<IconButton
|
||||
ref={btnRef}
|
||||
sx={{
|
||||
color: "white",
|
||||
fontSize: fontSizeSmall,
|
||||
padding: isScreenSmall ? buttonPaddingSmall : buttonPaddingBig,
|
||||
}}
|
||||
onClick={() => increaseSpeed()}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
{playbackRate}x
|
||||
<SlowMotionVideoIcon />
|
||||
</IconButton>
|
||||
</CustomFontTooltip>
|
||||
|
||||
<Popover
|
||||
open={isOpen}
|
||||
anchorEl={btnRef.current}
|
||||
onClose={() => setIsOpen(false)}
|
||||
slots={{
|
||||
transition: Fade,
|
||||
}}
|
||||
slotProps={{
|
||||
transition: {
|
||||
timeout: 200,
|
||||
},
|
||||
paper: {
|
||||
sx: {
|
||||
bgcolor: alpha("#181818", 0.98),
|
||||
color: "white",
|
||||
opacity: 0.9,
|
||||
borderRadius: 2,
|
||||
boxShadow: 5,
|
||||
p: 1,
|
||||
minWidth: 225,
|
||||
height: 300,
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
},
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px 0px 10px 0px",
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<ButtonBase onClick={onBack}>
|
||||
<ArrowBackIosIcon
|
||||
sx={{
|
||||
fontSize: "1.15em",
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
<ButtonBase>
|
||||
<Typography
|
||||
onClick={onBack}
|
||||
sx={{
|
||||
fontSize: "0.85rem",
|
||||
}}
|
||||
>
|
||||
Playback speed
|
||||
</Typography>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
overflow: "auto",
|
||||
"::-webkit-scrollbar-track": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
|
||||
"::-webkit-scrollbar": {
|
||||
width: "16px",
|
||||
height: "10px",
|
||||
},
|
||||
|
||||
"::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
borderRadius: "8px",
|
||||
backgroundClip: "content-box",
|
||||
border: "4px solid transparent",
|
||||
transition: "0.3s background-color",
|
||||
},
|
||||
|
||||
"::-webkit-scrollbar-thumb:hover": {
|
||||
backgroundColor: theme.palette.primary.dark,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{speeds?.map((speed) => {
|
||||
const isSelected = speed === playbackRate;
|
||||
return (
|
||||
<ButtonBase
|
||||
disabled={isSelected}
|
||||
key={speed}
|
||||
onClick={() => {
|
||||
onSelect(speed)
|
||||
setIsOpen(false)
|
||||
}}
|
||||
sx={{
|
||||
px: 2,
|
||||
py: 1,
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
},
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Typography>{speed}</Typography>
|
||||
{isSelected ? <CheckIcon /> : <ArrowForwardIosIcon />}
|
||||
</ButtonBase>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -331,8 +506,12 @@ export const ObjectFitButton = ({toggleObjectFit, isScreenSmall}: any) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const PictureInPictureButton = ({isFullscreen, toggleRef, togglePictureInPicture, isScreenSmall}: any) => {
|
||||
|
||||
export const PictureInPictureButton = ({
|
||||
isFullscreen,
|
||||
toggleRef,
|
||||
togglePictureInPicture,
|
||||
isScreenSmall,
|
||||
}: any) => {
|
||||
return (
|
||||
<>
|
||||
{!isFullscreen && (
|
||||
@ -358,7 +537,6 @@ export const PictureInPictureButton = ({isFullscreen, toggleRef, togglePictureIn
|
||||
};
|
||||
|
||||
export const FullscreenButton = ({ toggleFullscreen, isScreenSmall }: any) => {
|
||||
|
||||
return (
|
||||
<CustomFontTooltip title="Toggle Fullscreen (F)" placement="bottom" arrow>
|
||||
<IconButton
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
} from "./VideoControls";
|
||||
import { Ref } from "react";
|
||||
import SubtitlesIcon from '@mui/icons-material/Subtitles';
|
||||
import { CustomFontTooltip } from "./CustomFontTooltip";
|
||||
interface VideoControlsBarProps {
|
||||
canPlay: boolean
|
||||
isScreenSmall: boolean
|
||||
@ -36,9 +37,10 @@ interface VideoControlsBarProps {
|
||||
playbackRate: number
|
||||
openSubtitleManager: ()=> void
|
||||
subtitleBtnRef: any
|
||||
onSelectPlaybackRate: (rate: number)=> void;
|
||||
}
|
||||
|
||||
export const VideoControlsBar = ({subtitleBtnRef, showControls, playbackRate, increaseSpeed,decreaseSpeed, isFullScreen, showControlsFullScreen, reloadVideo, onVolumeChange, volume, isPlaying, canPlay, isScreenSmall, controlsHeight, playerRef, duration, progress, togglePlay, toggleFullscreen, extractFrames, openSubtitleManager}: VideoControlsBarProps) => {
|
||||
export const VideoControlsBar = ({subtitleBtnRef, showControls, playbackRate, increaseSpeed,decreaseSpeed, isFullScreen, showControlsFullScreen, reloadVideo, onVolumeChange, volume, isPlaying, canPlay, isScreenSmall, controlsHeight, playerRef, duration, progress, togglePlay, toggleFullscreen, extractFrames, openSubtitleManager, onSelectPlaybackRate}: VideoControlsBarProps) => {
|
||||
|
||||
const showMobileControls = isScreenSmall && canPlay;
|
||||
|
||||
@ -96,12 +98,18 @@ export const VideoControlsBar = ({subtitleBtnRef, showControls, playbackRate, in
|
||||
</Box>
|
||||
|
||||
<Box sx={{...controlGroupSX, marginLeft: 'auto'}}>
|
||||
<PlaybackRate playbackRate={playbackRate} increaseSpeed={increaseSpeed} decreaseSpeed={decreaseSpeed} />
|
||||
<ObjectFitButton />
|
||||
<PlaybackRate onSelect={onSelectPlaybackRate} playbackRate={playbackRate} increaseSpeed={increaseSpeed} decreaseSpeed={decreaseSpeed} />
|
||||
{/* <ObjectFitButton /> */}
|
||||
<CustomFontTooltip
|
||||
title="Subtitles"
|
||||
placement="bottom"
|
||||
arrow
|
||||
>
|
||||
<IconButton ref={subtitleBtnRef} onClick={openSubtitleManager}>
|
||||
<SubtitlesIcon />
|
||||
</IconButton>
|
||||
<PictureInPictureButton />
|
||||
</CustomFontTooltip>
|
||||
{/* <PictureInPictureButton /> */}
|
||||
<FullscreenButton toggleFullscreen={toggleFullscreen} />
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -206,6 +206,7 @@ export const VideoPlayer = ({
|
||||
status,
|
||||
percentLoaded,
|
||||
showControlsFullScreen,
|
||||
onSelectPlaybackRate
|
||||
} = useVideoPlayerController({
|
||||
autoPlay,
|
||||
playerRef,
|
||||
@ -214,6 +215,27 @@ export const VideoPlayer = ({
|
||||
isPlayerInitialized,
|
||||
});
|
||||
|
||||
console.log('isFullscreen', isFullscreen)
|
||||
|
||||
const enterFullscreen = useCallback(() => {
|
||||
const ref = containerRef?.current as any;
|
||||
if (!ref) return;
|
||||
|
||||
if (ref.requestFullscreen && !isFullscreen) {
|
||||
ref.requestFullscreen();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const exitFullscreen = useCallback(() => {
|
||||
document?.exitFullscreen();
|
||||
}, [isFullscreen]);
|
||||
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
console.log('togglefull', isFullscreen)
|
||||
isFullscreen ? exitFullscreen() : enterFullscreen();
|
||||
}, [isFullscreen]);
|
||||
|
||||
|
||||
const hotkeyHandlers = useMemo(
|
||||
() => ({
|
||||
reloadVideo,
|
||||
@ -227,6 +249,7 @@ export const VideoPlayer = ({
|
||||
toggleMute,
|
||||
setProgressAbsolute,
|
||||
setAlwaysShowControls,
|
||||
toggleFullscreen
|
||||
}),
|
||||
[
|
||||
reloadVideo,
|
||||
@ -240,6 +263,7 @@ export const VideoPlayer = ({
|
||||
toggleMute,
|
||||
setProgressAbsolute,
|
||||
setAlwaysShowControls,
|
||||
toggleFullscreen
|
||||
]
|
||||
);
|
||||
|
||||
@ -346,22 +370,6 @@ export const VideoPlayer = ({
|
||||
};
|
||||
}, [isPlayerInitialized]);
|
||||
|
||||
const enterFullscreen = () => {
|
||||
const ref = containerRef?.current as any;
|
||||
if (!ref) return;
|
||||
|
||||
if (ref.requestFullscreen && !isFullscreen) {
|
||||
ref.requestFullscreen();
|
||||
}
|
||||
};
|
||||
|
||||
const exitFullscreen = () => {
|
||||
if (isFullscreen) document.exitFullscreen();
|
||||
};
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
isFullscreen ? exitFullscreen() : enterFullscreen();
|
||||
};
|
||||
|
||||
const canvasRef = useRef(null);
|
||||
const videoRefForCanvas = useRef<any>(null);
|
||||
@ -744,6 +752,7 @@ export const VideoPlayer = ({
|
||||
duration={duration}
|
||||
progress={localProgress}
|
||||
openSubtitleManager={openSubtitleManager}
|
||||
onSelectPlaybackRate={onSelectPlaybackRate}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -280,6 +280,7 @@ const togglePlay = useCallback(async () => {
|
||||
}
|
||||
}, [togglePlay, isReady]);
|
||||
|
||||
|
||||
return {
|
||||
reloadVideo,
|
||||
togglePlay,
|
||||
@ -299,6 +300,6 @@ const togglePlay = useCallback(async () => {
|
||||
isReady,
|
||||
resourceUrl,
|
||||
startPlay,
|
||||
status, percentLoaded, showControlsFullScreen
|
||||
status, percentLoaded, showControlsFullScreen, onSelectPlaybackRate: updatePlaybackRate
|
||||
};
|
||||
};
|
||||
|
@ -12,6 +12,7 @@ interface UseVideoControls {
|
||||
changeVolume: (delta: number) => void;
|
||||
toggleMute: () => void;
|
||||
setProgressAbsolute: (percent: number) => void;
|
||||
toggleFullscreen: ()=> void;
|
||||
}
|
||||
|
||||
export const useVideoPlayerHotKeys = (props: UseVideoControls) => {
|
||||
@ -26,6 +27,7 @@ export const useVideoPlayerHotKeys = (props: UseVideoControls) => {
|
||||
changeVolume,
|
||||
toggleMute,
|
||||
setProgressAbsolute,
|
||||
toggleFullscreen
|
||||
} = props;
|
||||
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
@ -46,6 +48,9 @@ export const useVideoPlayerHotKeys = (props: UseVideoControls) => {
|
||||
case "o":
|
||||
toggleObjectFit();
|
||||
break;
|
||||
case "f":
|
||||
toggleFullscreen();
|
||||
break;
|
||||
case "c":
|
||||
toggleAlwaysShowControls();
|
||||
break;
|
||||
@ -127,6 +132,7 @@ export const useVideoPlayerHotKeys = (props: UseVideoControls) => {
|
||||
changeVolume,
|
||||
toggleMute,
|
||||
setProgressAbsolute,
|
||||
toggleFullscreen
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user