This commit is contained in:
kuekhaoyang
2026-02-04 17:13:00 +08:00
parent 9e6275f68b
commit 787110bd40
5 changed files with 88 additions and 25 deletions
+1
View File
@@ -183,6 +183,7 @@ export function DesktopVideoPlayer({
data={data}
actions={actions}
showControls={data.showControls}
isRotated={shouldForceLandscape}
onTogglePlay={togglePlay}
onSkipForward={logic.skipForward}
onSkipBackward={logic.skipBackward}
+39 -12
View File
@@ -15,6 +15,7 @@ interface DesktopMoreMenuProps {
onMouseLeave: () => void;
onCopyLink: (type?: 'original' | 'proxy') => void;
containerRef: React.RefObject<HTMLDivElement | null>;
isRotated?: boolean;
}
export function DesktopMoreMenu({
@@ -24,7 +25,8 @@ export function DesktopMoreMenu({
onMouseEnter,
onMouseLeave,
onCopyLink,
containerRef
containerRef,
isRotated = false
}: DesktopMoreMenuProps) {
const {
autoNextEpisode,
@@ -119,6 +121,18 @@ export function DesktopMoreMenu({
}
}, [containerRef]);
// When rotated, use direct viewport positioning
const calculateRotatedPosition = React.useCallback(() => {
if (!buttonRef.current) return;
const buttonRect = buttonRef.current.getBoundingClientRect();
setMenuPosition({
top: buttonRect.bottom + 10,
left: buttonRect.left,
maxHeight: `${window.innerHeight * 0.6}px`,
openUpward: false
});
}, []);
// Auto-close menu on scroll
React.useEffect(() => {
if (!showMoreMenu) return;
@@ -131,19 +145,31 @@ export function DesktopMoreMenu({
return () => window.removeEventListener('scroll', handleScroll);
}, [showMoreMenu, onToggleMoreMenu]);
// Recalculate position when menu opens
React.useEffect(() => {
if (showMoreMenu) {
calculateMenuPosition();
// Recalculate after a small delay to get actual menu height
const timer = setTimeout(calculateMenuPosition, 50);
if (isRotated) {
calculateRotatedPosition();
} else {
calculateMenuPosition();
}
const timer = setTimeout(() => {
if (isRotated) {
calculateRotatedPosition();
} else {
calculateMenuPosition();
}
}, 50);
return () => clearTimeout(timer);
}
}, [showMoreMenu, calculateMenuPosition]);
}, [showMoreMenu, calculateMenuPosition, calculateRotatedPosition, isRotated]);
const handleToggle = () => {
if (!showMoreMenu) {
calculateMenuPosition();
if (isRotated) {
calculateRotatedPosition();
} else {
calculateMenuPosition();
}
}
onToggleMoreMenu();
};
@@ -151,16 +177,17 @@ export function DesktopMoreMenu({
const MenuContent = (
<div
ref={menuRef}
className={`absolute z-[9999] bg-[var(--glass-bg)] backdrop-blur-[25px] saturate-[180%] rounded-[var(--radius-2xl)] border border-[var(--glass-border)] shadow-[var(--shadow-md)] p-1.5 sm:p-2 w-fit min-w-[200px] sm:min-w-[240px] animate-in fade-in zoom-in-95 duration-200 overflow-y-auto`}
className={`${isRotated ? 'fixed' : 'absolute'} z-[2147483647] bg-[var(--glass-bg)] backdrop-blur-[25px] saturate-[180%] rounded-[var(--radius-2xl)] border border-[var(--glass-border)] shadow-[var(--shadow-md)] p-1.5 sm:p-2 w-fit min-w-[200px] sm:min-w-[240px] animate-in fade-in zoom-in-95 duration-200 overflow-y-auto`}
style={{
top: menuPosition.openUpward ? 'auto' : `${menuPosition.top}px`,
bottom: menuPosition.openUpward ? `calc(100% - ${menuPosition.top}px + 10px)` : 'auto',
top: isRotated ? `${menuPosition.top}px` : (menuPosition.openUpward ? 'auto' : `${menuPosition.top}px`),
bottom: (!isRotated && menuPosition.openUpward) ? `calc(100% - ${menuPosition.top}px + 10px)` : 'auto',
left: `${menuPosition.left}px`,
maxHeight: isFullscreen ? menuPosition.maxHeight : 'none',
maxHeight: menuPosition.maxHeight,
}}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
>
{/* Copy Link Options */}
{isProxied ? (
@@ -393,7 +420,7 @@ export function DesktopMoreMenu({
</button>
{/* More Menu Dropdown (Portal) */}
{showMoreMenu && typeof document !== 'undefined' && createPortal(MenuContent, containerRef.current || document.body)}
{showMoreMenu && typeof document !== 'undefined' && createPortal(MenuContent, isRotated ? document.body : (containerRef.current || document.body))}
</div>
);
}
@@ -35,6 +35,7 @@ interface DesktopOverlayProps {
onSpeedMenuMouseEnter: () => void;
onSpeedMenuMouseLeave: () => void;
containerRef: React.RefObject<HTMLDivElement | null>;
isRotated?: boolean;
}
export function DesktopOverlay({
@@ -67,6 +68,7 @@ export function DesktopOverlay({
onSpeedMenuMouseEnter,
onSpeedMenuMouseLeave,
containerRef,
isRotated = false,
}: DesktopOverlayProps) {
// Show navigation buttons when controls are visible or when paused (controls usually show when paused anyway)
const showNavButtons = showControls || !isPlaying;
@@ -83,6 +85,7 @@ export function DesktopOverlay({
onMouseLeave={onMoreMenuMouseLeave}
onCopyLink={onCopyLink}
containerRef={containerRef}
isRotated={isRotated}
/>
</div>
@@ -97,6 +100,7 @@ export function DesktopOverlay({
onMouseEnter={onSpeedMenuMouseEnter}
onMouseLeave={onSpeedMenuMouseLeave}
containerRef={containerRef}
isRotated={isRotated}
/>
</div>
@@ -6,6 +6,7 @@ interface DesktopOverlayWrapperProps {
data: ReturnType<typeof useDesktopPlayerState>['data'];
actions: ReturnType<typeof useDesktopPlayerState>['actions'];
showControls: boolean;
isRotated?: boolean;
onTogglePlay: () => void;
onSkipForward: () => void;
onSkipBackward: () => void;
@@ -31,6 +32,7 @@ export function DesktopOverlayWrapper({
data,
actions,
showControls,
isRotated = false,
onTogglePlay,
onSkipForward,
onSkipBackward,
@@ -94,6 +96,7 @@ export function DesktopOverlayWrapper({
onSpeedMenuMouseEnter={onSpeedMenuMouseEnter}
onSpeedMenuMouseLeave={onSpeedMenuMouseLeave}
containerRef={containerRef}
isRotated={isRotated}
/>
);
}
+41 -13
View File
@@ -10,6 +10,7 @@ interface DesktopSpeedMenuProps {
onMouseEnter: () => void;
onMouseLeave: () => void;
containerRef: React.RefObject<HTMLDivElement | null>;
isRotated?: boolean;
}
export function DesktopSpeedMenu({
@@ -20,7 +21,8 @@ export function DesktopSpeedMenu({
onToggleSpeedMenu,
onMouseEnter,
onMouseLeave,
containerRef
containerRef,
isRotated = false
}: DesktopSpeedMenuProps) {
const buttonRef = React.useRef<HTMLButtonElement>(null);
const menuRef = React.useRef<HTMLDivElement>(null);
@@ -86,6 +88,18 @@ export function DesktopSpeedMenu({
}
}, [containerRef]);
// When rotated, use direct viewport positioning
const calculateRotatedPosition = React.useCallback(() => {
if (!buttonRef.current) return;
const buttonRect = buttonRef.current.getBoundingClientRect();
setMenuPosition({
top: buttonRect.bottom + 10,
left: buttonRect.right,
maxHeight: `${window.innerHeight * 0.6}px`,
openUpward: false
});
}, []);
// Auto-close menu on scroll
React.useEffect(() => {
if (!showSpeedMenu) return;
@@ -98,19 +112,31 @@ export function DesktopSpeedMenu({
return () => window.removeEventListener('scroll', handleScroll);
}, [showSpeedMenu, onToggleSpeedMenu]);
// Recalculate position when menu opens
React.useEffect(() => {
if (showSpeedMenu) {
calculateMenuPosition();
// Recalculate after a small delay to get actual menu height
const timer = setTimeout(calculateMenuPosition, 50);
if (isRotated) {
calculateRotatedPosition();
} else {
calculateMenuPosition();
}
const timer = setTimeout(() => {
if (isRotated) {
calculateRotatedPosition();
} else {
calculateMenuPosition();
}
}, 50);
return () => clearTimeout(timer);
}
}, [showSpeedMenu, calculateMenuPosition]);
}, [showSpeedMenu, calculateMenuPosition, calculateRotatedPosition, isRotated]);
const handleToggle = () => {
if (!showSpeedMenu) {
calculateMenuPosition();
if (isRotated) {
calculateRotatedPosition();
} else {
calculateMenuPosition();
}
}
onToggleSpeedMenu();
};
@@ -118,16 +144,18 @@ export function DesktopSpeedMenu({
const MenuContent = (
<div
ref={menuRef}
className={`absolute z-[9999] bg-[var(--glass-bg)] backdrop-blur-[25px] saturate-[180%] rounded-[var(--radius-2xl)] border border-[var(--glass-border)] shadow-[var(--shadow-md)] p-1 sm:p-1.5 w-fit min-w-[3.5rem] sm:min-w-[4.5rem] animate-in fade-in zoom-in-95 duration-200 overflow-y-auto`}
className={`${isRotated ? 'fixed' : 'absolute'} z-[2147483647] bg-[var(--glass-bg)] backdrop-blur-[25px] saturate-[180%] rounded-[var(--radius-2xl)] border border-[var(--glass-border)] shadow-[var(--shadow-md)] p-1 sm:p-1.5 w-fit min-w-[3.5rem] sm:min-w-[4.5rem] animate-in fade-in zoom-in-95 duration-200 overflow-y-auto`}
style={{
top: menuPosition.openUpward ? 'auto' : `${menuPosition.top}px`,
bottom: menuPosition.openUpward ? `calc(100% - ${menuPosition.top}px + 10px)` : 'auto',
top: isRotated ? `${menuPosition.top}px` : (menuPosition.openUpward ? 'auto' : `${menuPosition.top}px`),
bottom: (!isRotated && menuPosition.openUpward) ? `calc(100% - ${menuPosition.top}px + 10px)` : 'auto',
left: `${menuPosition.left}px`,
transform: 'translateX(-100%)', // Align right edge
maxHeight: isFullscreen ? menuPosition.maxHeight : 'none',
transform: 'translateX(-100%)',
maxHeight: menuPosition.maxHeight,
}}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
>
{speeds.map((speed) => (
<button
@@ -158,7 +186,7 @@ export function DesktopSpeedMenu({
</button>
{/* Speed Menu (Portal) */}
{showSpeedMenu && typeof document !== 'undefined' && createPortal(MenuContent, containerRef.current || document.body)}
{showSpeedMenu && typeof document !== 'undefined' && createPortal(MenuContent, isRotated ? document.body : (containerRef.current || document.body))}
</div>
);
}