From 17d2460a8769a21d33fabc5f909cf5b939712d36 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Sun, 2 Nov 2025 11:02:44 -0600 Subject: [PATCH] feat(rithmomachia): show real preview layout when dragging guide to dock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace ghost panel overlay with actual docked layout preview: - Add onDockPreview callback to communicate preview state to parent - Parent renders real PanelGroup layout when dockPreviewSide is set - Guide appears in docked position showing real final layout - Board resizes automatically to show what docked state will look like - Dragging modal shown at 0.8 opacity during preview - Both guide (in panel) and dragging modal visible simultaneously - Clear preview when drag ends or moves away from edges This provides much better visual feedback - user sees exactly what the final docked layout will look like before releasing the drag. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/PlayingGuideModal.tsx | 53 +++++++------------ .../components/RithmomachiaGame.tsx | 20 +++++-- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx b/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx index d955ee89..ea974471 100644 --- a/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx +++ b/apps/web/src/arcade-games/rithmomachia/components/PlayingGuideModal.tsx @@ -19,6 +19,7 @@ interface PlayingGuideModalProps { docked?: boolean // True when docked to side onDock?: (side: 'left' | 'right') => void onUndock?: () => void + onDockPreview?: (side: 'left' | 'right' | null) => void // Preview docking without committing } type Section = 'overview' | 'pieces' | 'capture' | 'strategy' | 'harmony' | 'victory' @@ -30,6 +31,7 @@ export function PlayingGuideModal({ docked = false, onDock, onUndock, + onDockPreview, }: PlayingGuideModalProps) { const t = useTranslations('rithmomachia.guide') const { data: abacusSettings } = useAbacusSettings() @@ -124,14 +126,17 @@ export function PlayingGuideModal({ }) // Check if we're near edges for docking preview - if (onDock && !docked) { + if (onDock && onDockPreview && !docked) { const DOCK_THRESHOLD = 100 if (e.clientX < DOCK_THRESHOLD) { setDockPreview('left') + onDockPreview('left') } else if (e.clientX > window.innerWidth - DOCK_THRESHOLD) { setDockPreview('right') + onDockPreview('right') } else { setDockPreview(null) + onDockPreview(null) } } } else if (isResizing) { @@ -227,6 +232,9 @@ export function PlayingGuideModal({ setIsResizing(false) setResizeDirection('') setDockPreview(null) // Clear dock preview when drag ends + if (onDockPreview) { + onDockPreview(null) // Clear parent preview state + } } if (isDragging || isResizing) { @@ -409,8 +417,13 @@ export function PlayingGuideModal({ height: `${size.height}px`, zIndex: Z_INDEX.MODAL, }), - // 80% opacity on desktop when not hovered, full opacity otherwise - opacity: !standalone && !docked && window.innerWidth >= 768 && !isHovered ? 0.8 : 1, + // 80% opacity when showing dock preview or when not hovered on desktop + opacity: + dockPreview !== null + ? 0.8 + : !standalone && !docked && window.innerWidth >= 768 && !isHovered + ? 0.8 + : 1, transition: 'opacity 0.2s ease', }} onMouseEnter={() => setIsHovered(true)} @@ -696,36 +709,6 @@ export function PlayingGuideModal({ return modalContent } - // Otherwise, render the modal with optional dock preview - return ( - <> - {/* Dock preview ghost panel */} - {dockPreview && !docked && ( -
- {dockPreview === 'left' ? '←' : '→'} -
- )} - {modalContent} - - ) + // Otherwise, just render the modal (parent will handle preview rendering) + return modalContent } diff --git a/apps/web/src/arcade-games/rithmomachia/components/RithmomachiaGame.tsx b/apps/web/src/arcade-games/rithmomachia/components/RithmomachiaGame.tsx index 672beeda..89b54d55 100644 --- a/apps/web/src/arcade-games/rithmomachia/components/RithmomachiaGame.tsx +++ b/apps/web/src/arcade-games/rithmomachia/components/RithmomachiaGame.tsx @@ -41,6 +41,7 @@ export function RithmomachiaGame() { const [isGuideOpen, setIsGuideOpen] = useState(false) const [guideDocked, setGuideDocked] = useState(false) const [guideDockSide, setGuideDockSide] = useState<'left' | 'right'>('right') + const [dockPreviewSide, setDockPreviewSide] = useState<'left' | 'right' | null>(null) useEffect(() => { // Register this component's main div as the fullscreen element @@ -97,6 +98,7 @@ export function RithmomachiaGame() { console.log('[RithmomachiaGame] handleDock called', { side }) setGuideDockSide(side) setGuideDocked(true) + setDockPreviewSide(null) // Clear preview when committing to dock console.log('[RithmomachiaGame] Docked state updated', { guideDocked: true, guideDockSide: side, @@ -109,6 +111,11 @@ export function RithmomachiaGame() { console.log('[RithmomachiaGame] Undocked state updated', { guideDocked: false }) } + const handleDockPreview = (side: 'left' | 'right' | null) => { + console.log('[RithmomachiaGame] handleDockPreview called', { side }) + setDockPreviewSide(side) + } + const gameContent = (
- {guideDocked && isGuideOpen ? ( + {(guideDocked || dockPreviewSide) && isGuideOpen ? ( - {guideDockSide === 'left' && ( + {(guideDocked ? guideDockSide : dockPreviewSide) === 'left' && ( <> setIsGuideOpen(false)} - docked={true} + docked={guideDocked} // Only truly docked if guideDocked is true onUndock={handleUndock} + onDockPreview={handleDockPreview} /> {gameContent} )} - {guideDockSide === 'right' && ( + {(guideDocked ? guideDockSide : dockPreviewSide) === 'right' && ( <> {gameContent} setIsGuideOpen(false)} - docked={true} + docked={guideDocked} // Only truly docked if guideDocked is true onUndock={handleUndock} + onDockPreview={handleDockPreview} /> @@ -238,6 +247,7 @@ export function RithmomachiaGame() { onClose={() => setIsGuideOpen(false)} docked={false} onDock={handleDock} + onDockPreview={handleDockPreview} /> )}