mirror of
https://github.com/arkorty/Osborne.git
synced 2026-03-17 16:51:44 +00:00
ui fixes yo
This commit is contained in:
@@ -236,56 +236,70 @@ const Room = () => {
|
|||||||
return () => window.removeEventListener('resize', checkMobile);
|
return () => window.removeEventListener('resize', checkMobile);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Mobile swipe gesture handling
|
// Mobile swipe gesture handling & Escape key to close panels
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMobile) return;
|
// Swipe gesture (mobile only)
|
||||||
|
if (isMobile) {
|
||||||
|
let touchStartX = 0;
|
||||||
|
let touchStartY = 0;
|
||||||
|
let touchStartTime = 0;
|
||||||
|
|
||||||
let touchStartX = 0;
|
const handleTouchStart = (e: TouchEvent) => {
|
||||||
let touchStartY = 0;
|
const touch = e.touches[0];
|
||||||
let touchStartTime = 0;
|
touchStartX = touch.clientX;
|
||||||
|
touchStartY = touch.clientY;
|
||||||
|
touchStartTime = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
const handleTouchStart = (e: TouchEvent) => {
|
const handleTouchEnd = (e: TouchEvent) => {
|
||||||
const touch = e.touches[0];
|
const touch = e.changedTouches[0];
|
||||||
touchStartX = touch.clientX;
|
const touchEndX = touch.clientX;
|
||||||
touchStartY = touch.clientY;
|
const touchEndY = touch.clientY;
|
||||||
touchStartTime = Date.now();
|
const touchEndTime = Date.now();
|
||||||
};
|
|
||||||
|
|
||||||
const handleTouchEnd = (e: TouchEvent) => {
|
const deltaX = touchEndX - touchStartX;
|
||||||
const touch = e.changedTouches[0];
|
const deltaY = touchEndY - touchStartY;
|
||||||
const touchEndX = touch.clientX;
|
const deltaTime = touchEndTime - touchStartTime;
|
||||||
const touchEndY = touch.clientY;
|
|
||||||
const touchEndTime = Date.now();
|
|
||||||
|
|
||||||
const deltaX = touchEndX - touchStartX;
|
// Only consider it a swipe if:
|
||||||
const deltaY = touchEndY - touchStartY;
|
// 1. The gesture is fast enough (less than 500ms)
|
||||||
const deltaTime = touchEndTime - touchStartTime;
|
// 2. The horizontal distance is significant (at least 100px)
|
||||||
|
// 3. The vertical distance is less than horizontal (to avoid conflicting with scrolling)
|
||||||
// Only consider it a swipe if:
|
if (
|
||||||
// 1. The gesture is fast enough (less than 500ms)
|
deltaTime < 500 &&
|
||||||
// 2. The horizontal distance is significant (at least 100px)
|
Math.abs(deltaX) > 100 &&
|
||||||
// 3. The vertical distance is less than horizontal (to avoid conflicting with scrolling)
|
Math.abs(deltaX) > Math.abs(deltaY)
|
||||||
if (
|
) {
|
||||||
deltaTime < 500 &&
|
if (deltaX < 0 && leftPanelForced) {
|
||||||
Math.abs(deltaX) > 100 &&
|
// Swipe left - close left panel
|
||||||
Math.abs(deltaX) > Math.abs(deltaY)
|
setLeftPanelForced(false);
|
||||||
) {
|
} else if (deltaX > 0 && rightPanelForced) {
|
||||||
if (deltaX < 0 && leftPanelForced) {
|
// Swipe right - close right panel
|
||||||
// Swipe left - close left panel
|
setRightPanelForced(false);
|
||||||
setLeftPanelForced(false);
|
}
|
||||||
} else if (deltaX > 0 && rightPanelForced) {
|
|
||||||
// Swipe right - close right panel
|
|
||||||
setRightPanelForced(false);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('touchstart', handleTouchStart, { passive: true });
|
||||||
|
document.addEventListener('touchend', handleTouchEnd, { passive: true });
|
||||||
|
|
||||||
|
// Clean up swipe listeners
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('touchstart', handleTouchStart);
|
||||||
|
document.removeEventListener('touchend', handleTouchEnd);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape key closes panels (all devices)
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
if (leftPanelForced) setLeftPanelForced(false);
|
||||||
|
if (rightPanelForced) setRightPanelForced(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
document.addEventListener('touchstart', handleTouchStart, { passive: true });
|
|
||||||
document.addEventListener('touchend', handleTouchEnd, { passive: true });
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('touchstart', handleTouchStart);
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
document.removeEventListener('touchend', handleTouchEnd);
|
|
||||||
};
|
};
|
||||||
}, [isMobile, leftPanelForced, rightPanelForced]);
|
}, [isMobile, leftPanelForced, rightPanelForced]);
|
||||||
|
|
||||||
@@ -324,19 +338,19 @@ const Room = () => {
|
|||||||
|
|
||||||
// Calculate panel visibility based on window width
|
// Calculate panel visibility based on window width
|
||||||
// Minimum width needed: 320px (left) + 640px (main content) + 320px (right) = 1280px
|
// Minimum width needed: 320px (left) + 640px (main content) + 320px (right) = 1280px
|
||||||
const shouldShowPanels = windowWidth >= 1280;
|
const showSidePanels = windowWidth >= 1280;
|
||||||
|
|
||||||
// Auto-hide forced panels when screen size increases (do this before calculating visibility)
|
// Auto-hide forced panels when screen size increases (do this before calculating visibility)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shouldShowPanels) {
|
if (showSidePanels) {
|
||||||
setLeftPanelForced(false);
|
setLeftPanelForced(false);
|
||||||
setRightPanelForced(false);
|
setRightPanelForced(false);
|
||||||
}
|
}
|
||||||
}, [shouldShowPanels]);
|
}, [showSidePanels]);
|
||||||
|
|
||||||
// Calculate final panel visibility - when shouldShowPanels is true, always show panels regardless of forced state
|
// Calculate final panel visibility - when shouldShowPanels is true, always show panels regardless of forced state
|
||||||
const showLeftPanel = shouldShowPanels || (!shouldShowPanels && leftPanelForced);
|
const showLeftPanel = showSidePanels || (!showSidePanels && leftPanelForced);
|
||||||
const showRightPanel = shouldShowPanels || (!shouldShowPanels && rightPanelForced);
|
const showRightPanel = showSidePanels || (!showSidePanels && rightPanelForced);
|
||||||
|
|
||||||
// Initialize theme from cookie
|
// Initialize theme from cookie
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -809,7 +823,7 @@ const Room = () => {
|
|||||||
share
|
share
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-popover-foreground bg-popover text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-popover-foreground bg-popover text-xs border-foreground">
|
||||||
copy link to this page
|
copy link to this page
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
@@ -823,7 +837,7 @@ const Room = () => {
|
|||||||
purge
|
purge
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-popover-foreground bg-popover text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-popover-foreground bg-popover text-xs border-foreground">
|
||||||
permanently delete this room and all its contents
|
permanently delete this room and all its contents
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
@@ -837,7 +851,7 @@ const Room = () => {
|
|||||||
exit
|
exit
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-popover-foreground bg-popover text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-popover-foreground bg-popover text-xs border-foreground">
|
||||||
return to home
|
return to home
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
@@ -855,7 +869,7 @@ const Room = () => {
|
|||||||
upload
|
upload
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground">
|
||||||
upload files
|
upload files
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
@@ -873,13 +887,13 @@ const Room = () => {
|
|||||||
theme
|
theme
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground">
|
||||||
{getThemeById(currentThemeId)?.name || "Switch theme"}
|
{getThemeById(currentThemeId)?.name || "Switch theme"}
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
|
|
||||||
{/* Mobile Panel Controls */}
|
{/* Panel Controls for mobile and when panels are hidden due to width */}
|
||||||
{isMobile && (
|
{(isMobile || !showSidePanels) && (
|
||||||
<>
|
<>
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger>
|
<HoverCardTrigger>
|
||||||
@@ -890,7 +904,7 @@ const Room = () => {
|
|||||||
media
|
media
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground z-[999]">
|
||||||
toggle users & media panel
|
toggle users & media panel
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
@@ -903,7 +917,7 @@ const Room = () => {
|
|||||||
notes
|
notes
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground z-50">
|
<HoverCardContent className="py-1 px-2 w-auto text-xs border-foreground">
|
||||||
toggle comments panel
|
toggle comments panel
|
||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
@@ -983,7 +997,7 @@ const Room = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Overlay for mobile when panels are forced open */}
|
{/* Overlay for mobile when panels are forced open */}
|
||||||
{!shouldShowPanels && (leftPanelForced || rightPanelForced) && (
|
{!showSidePanels && (leftPanelForced || rightPanelForced) && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 bg-black/20 z-30"
|
className="fixed inset-0 bg-black/20 z-30"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export const CodeEditor = forwardRef<CodeEditorRef, CodeEditorProps>(({
|
|||||||
}, [themeConfig, editorReady]);
|
}, [themeConfig, editorReady]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`border border-border overflow-hidden ${className}`}>
|
<div className={`border border-border overflow-hidden ${className}`}>
|
||||||
<Editor
|
<Editor
|
||||||
height="100%"
|
height="100%"
|
||||||
language={language}
|
language={language}
|
||||||
|
|||||||
@@ -13,16 +13,18 @@ const HoverCardContent = React.forwardRef<
|
|||||||
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||||
<HoverCardPrimitive.Content
|
<HoverCardPrimitive.Portal>
|
||||||
ref={ref}
|
<HoverCardPrimitive.Content
|
||||||
align={align}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
align={align}
|
||||||
className={cn(
|
sideOffset={sideOffset}
|
||||||
"z-50 w-64 border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
className={cn(
|
||||||
className
|
"z-50 w-64 border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
)}
|
className
|
||||||
{...props}
|
)}
|
||||||
/>
|
{...props}
|
||||||
|
/>
|
||||||
|
</HoverCardPrimitive.Portal>
|
||||||
))
|
))
|
||||||
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
|
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user