import classnames from 'classnames'; import type { CSSProperties, MouseEvent as ReactMouseEvent, ReactNode } from 'react'; import React, { memo, useCallback, useMemo, useRef, useState } from 'react'; import { useWindowSize } from 'react-use'; import { useSidebarDisplay } from '../hooks/useSidebarDisplay'; import { Sidebar } from './Sidebar'; import { WorkspaceHeader } from './WorkspaceHeader'; import RequestResponse from './RequestResponse'; const side = { gridArea: 'side' }; const head = { gridArea: 'head' }; const body = { gridArea: 'body' }; const FLOATING_SIDEBAR_BREAKPOINT = 960; export default function Workspace() { const windowSize = useWindowSize(); const styles = useMemo( () => ({ gridTemplate: ` ' ${head.gridArea} ${head.gridArea}' auto ' ${side.gridArea} ${body.gridArea}' minmax(0,1fr) / auto 1fr `, }), [], ); return (
); } const HeaderContainer = memo(function HeaderContainer({ children }: { children: ReactNode }) { return (
{children}
); }); interface SidebarContainerProps { children: ReactNode; style: CSSProperties; floating?: boolean; } const SidebarContainer = memo(function SidebarContainer({ children, style, floating, }: SidebarContainerProps) { const sidebar = useSidebarDisplay(); const [isResizing, setIsResizing] = useState(false); const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void } | null>( null, ); const unsub = () => { if (moveState.current !== null) { document.documentElement.removeEventListener('mousemove', moveState.current.move); document.documentElement.removeEventListener('mouseup', moveState.current.up); } }; const handleResizeStart = useCallback( (e: ReactMouseEvent) => { if (sidebar.width === undefined) return; unsub(); const mouseStartX = e.clientX; const startWidth = sidebar.width; moveState.current = { move: (e: MouseEvent) => { e.preventDefault(); // Prevent text selection and things sidebar.set(startWidth + (e.clientX - mouseStartX)); }, up: (e: MouseEvent) => { e.preventDefault(); unsub(); setIsResizing(false); }, }; document.documentElement.addEventListener('mousemove', moveState.current.move); document.documentElement.addEventListener('mouseup', moveState.current.up); setIsResizing(true); }, [sidebar.width], ); const sidebarStyles = useMemo( () => ({ width: sidebar.hidden ? 0 : sidebar.width, // No width when hidden borderWidth: sidebar.hidden ? 0 : undefined, // No border when hidden }), [sidebar.width, sidebar.hidden, style], ); const commonClassname = classnames('overflow-hidden bg-gray-100 border-highlight'); if (floating) { return (
{children}
); } return (
{children}
); }); interface ResizeBarProps { className?: string; barClassName?: string; isResizing: boolean; onResizeStart: (e: ReactMouseEvent) => void; onReset?: () => void; side: 'left' | 'right' | 'top'; justify: 'center' | 'end' | 'start'; } export function ResizeBar({ justify, className, onResizeStart, onReset, isResizing, side, }: ResizeBarProps) { const vertical = side === 'top'; return (
{/* Show global overlay with cursor style to ensure cursor remains the same when moving quickly */} {isResizing && (
)}
); }