diff --git a/App.tsx b/App.tsx index 3f4ab6f..b8cbc43 100644 --- a/App.tsx +++ b/App.tsx @@ -32,6 +32,13 @@ const App: React.FC = () => { const [capturedByWhite, setCapturedByWhite] = useState([]); // Pieces Black lost to White const [capturedByBlack, setCapturedByBlack] = useState([]); // Pieces White lost to Black + // Mobile Overlay State + const [isInfoOverlayVisible, setIsInfoOverlayVisible] = useState(false); + + const toggleInfoOverlay = useCallback(() => { + setIsInfoOverlayVisible(prev => !prev); + }, []); + // Helper function to get legal moves (filters out moves that leave king in check) const getLegalMoves = useCallback(( currentBoard: BoardState, @@ -293,13 +300,24 @@ const App: React.FC = () => { return (

React Chess

-
{ } mainContent={ -
+
void; + moveHistory: string[]; + gameStatusMessage: string; + onResetGame: () => void; + isCheckmate: boolean; + isStalemate: boolean; + isCheck: boolean; + // Props for mobile captured pieces + capturedByWhiteData: Piece[]; + capturedByBlackData: Piece[]; } export const AppLayout: React.FC = ({ @@ -12,6 +27,16 @@ export const AppLayout: React.FC = ({ leftSidebarContent, mainContent, rightSidebarContent, + isInfoOverlayVisible, + toggleInfoOverlay, + moveHistory, + gameStatusMessage, + onResetGame, + isCheckmate, + isStalemate, + isCheck, + capturedByWhiteData, + capturedByBlackData, }) => { return (
= ({
-
- {headerContent} +
+ {/* Mobile Info Toggle Button - aligned to the left or start */} + + + {/* Header Content (Title and Desktop Status) - takes remaining space and centers its content */} +
+ {headerContent} +
+ + {/* Spacer to balance the mobile button, hidden on lg screens */} +
- {/* Left Side */} -
- {leftSidebarContent} + {/* Mobile: Captured Black Pieces (Top) - order-1 ensures it's first in flex-col */} +
+
- {/* Center - Chess Board */} + {/* Main Content (Board) - Order changes for mobile vs desktop */} + {/* On mobile, order-2. On desktop, it's part of lg:flex-row, effectively order-2 by source order among visible lg items */}
- {mainContent} + {mainContent} {/* This is the BoardComponent */} +
+ + {/* Mobile: Captured White Pieces (Bottom) - order-3 ensures it's last in flex-col before desktop sidebars */} +
+ +
+ + {/* Desktop Left Sidebar (contains both captured displays from App.tsx) */} + {/* order-1 for lg screens, hidden on smaller screens */} + - {/* Right Side */} + {/* Desktop Right Sidebar (Game Info, Moves, Reset from App.tsx) */} + {/* order-3 for lg screens, hidden on smaller screens */}
+ + {isInfoOverlayVisible && ( + + )}
); }; diff --git a/components/CapturedPiecesDisplay.tsx b/components/CapturedPiecesDisplay.tsx index f3df579..c3dfe4e 100644 --- a/components/CapturedPiecesDisplay.tsx +++ b/components/CapturedPiecesDisplay.tsx @@ -9,31 +9,31 @@ export interface CapturedPiecesDisplayProps { export const CapturedPiecesDisplay: React.FC = ({ pieces, title }) => (

{title}

{pieces.length === 0 && ( - No pieces captured + No pieces captured )} {pieces.map((p, i) => (
{/* This usage expects PIECE_UNICODE to be { [color]: { [type]: char } } */} diff --git a/components/MobileInfoOverlay.tsx b/components/MobileInfoOverlay.tsx new file mode 100644 index 0000000..14d5aed --- /dev/null +++ b/components/MobileInfoOverlay.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import NotationDisplay from './NotationDisplay'; // Assuming NotationDisplay is in the same components folder + +interface MobileInfoOverlayProps { + isOpen: boolean; + onClose: () => void; + moveHistory: string[]; + gameStatusMessage: string; + onResetGame: () => void; + isCheckmate: boolean; // Added to determine button text + isStalemate: boolean; // Added to determine button text + isCheck: boolean; // Added for status message styling +} + +export const MobileInfoOverlay: React.FC = ({ + isOpen, + onClose, + moveHistory, + gameStatusMessage, + onResetGame, + isCheckmate, + isStalemate, + isCheck, +}) => { + if (!isOpen) { + return null; + } + + // Determine status message styling based on game state + // Similar to App.tsx, but adapted for the overlay + let statusMessageClasses = 'px-3 py-2 rounded-md text-sm font-medium text-center '; + if (isCheckmate) { + statusMessageClasses += 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/40'; + } else if (isStalemate) { + statusMessageClasses += 'bg-yellow-500/20 text-yellow-300 border border-yellow-500/40'; + } else if (isCheck) { + statusMessageClasses += 'bg-red-500/20 text-red-300 border border-red-500/40 animate-pulse'; + } else { + statusMessageClasses += 'bg-slate-700/60 text-slate-300 border border-slate-600/40'; + } + + + return ( +
+
e.stopPropagation()} // Prevent closing when clicking inside panel + > + {/* Close Button */} + + + {/* Game Status Message */} +
+ {gameStatusMessage} +
+ + {/* Move History */} +
{/* Ensure NotationDisplay can shrink and scroll */} + +
+ + {/* Reset Game Button */} + +
+
+ ); +};