feat(rithmomachia): show real preview layout when dragging guide to dock
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 <noreply@anthropic.com>
This commit is contained in:
parent
34cd3247e5
commit
17d2460a87
|
|
@ -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 && (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
[dockPreview === 'left' ? 'left' : 'right']: 0,
|
||||
width: '35%',
|
||||
height: '100%',
|
||||
background: 'rgba(139, 92, 246, 0.15)',
|
||||
border: `3px dashed rgba(139, 92, 246, 0.5)`,
|
||||
borderRadius: 0,
|
||||
pointerEvents: 'none',
|
||||
zIndex: 9999,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '48px',
|
||||
color: 'rgba(139, 92, 246, 0.6)',
|
||||
fontWeight: 'bold',
|
||||
transition: 'none',
|
||||
}}
|
||||
>
|
||||
{dockPreview === 'left' ? '←' : '→'}
|
||||
</div>
|
||||
)}
|
||||
{modalContent}
|
||||
</>
|
||||
)
|
||||
// Otherwise, just render the modal (parent will handle preview rendering)
|
||||
return modalContent
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
<div
|
||||
className={css({
|
||||
|
|
@ -174,16 +181,17 @@ export function RithmomachiaGame() {
|
|||
overflow: 'hidden',
|
||||
})}
|
||||
>
|
||||
{guideDocked && isGuideOpen ? (
|
||||
{(guideDocked || dockPreviewSide) && isGuideOpen ? (
|
||||
<PanelGroup direction="horizontal" style={{ flex: 1 }}>
|
||||
{guideDockSide === 'left' && (
|
||||
{(guideDocked ? guideDockSide : dockPreviewSide) === 'left' && (
|
||||
<>
|
||||
<Panel defaultSize={35} minSize={20} maxSize={50}>
|
||||
<PlayingGuideModal
|
||||
isOpen={true}
|
||||
onClose={() => setIsGuideOpen(false)}
|
||||
docked={true}
|
||||
docked={guideDocked} // Only truly docked if guideDocked is true
|
||||
onUndock={handleUndock}
|
||||
onDockPreview={handleDockPreview}
|
||||
/>
|
||||
</Panel>
|
||||
<PanelResizeHandle
|
||||
|
|
@ -200,7 +208,7 @@ export function RithmomachiaGame() {
|
|||
<Panel minSize={50}>{gameContent}</Panel>
|
||||
</>
|
||||
)}
|
||||
{guideDockSide === 'right' && (
|
||||
{(guideDocked ? guideDockSide : dockPreviewSide) === 'right' && (
|
||||
<>
|
||||
<Panel minSize={50}>{gameContent}</Panel>
|
||||
<PanelResizeHandle
|
||||
|
|
@ -218,8 +226,9 @@ export function RithmomachiaGame() {
|
|||
<PlayingGuideModal
|
||||
isOpen={true}
|
||||
onClose={() => setIsGuideOpen(false)}
|
||||
docked={true}
|
||||
docked={guideDocked} // Only truly docked if guideDocked is true
|
||||
onUndock={handleUndock}
|
||||
onDockPreview={handleDockPreview}
|
||||
/>
|
||||
</Panel>
|
||||
</>
|
||||
|
|
@ -238,6 +247,7 @@ export function RithmomachiaGame() {
|
|||
onClose={() => setIsGuideOpen(false)}
|
||||
docked={false}
|
||||
onDock={handleDock}
|
||||
onDockPreview={handleDockPreview}
|
||||
/>
|
||||
)}
|
||||
</PageWithNav>
|
||||
|
|
|
|||
Loading…
Reference in New Issue