From ca76405986de60ff6b947eace3fc9f025cbc458b Mon Sep 17 00:00:00 2001 From: dnviti Date: Tue, 16 Dec 2025 19:09:53 +0100 Subject: [PATCH] feat: Refactor application layout for full-height content and implement resizable draft UI with card zoom and scaling. --- docs/development/CENTRAL.md | 3 + .../2025-12-16-195000_draft_ui_polish.md | 23 ++ .../2025-12-16-200500_resizable_draft_ui.md | 34 +++ .../devlog/2025-12-16-201500_card_zoom.md | 13 ++ .../devlog/2025-12-16-203000_zoom_zone.md | 15 ++ src/client/src/App.tsx | 6 +- src/client/src/modules/cube/CubeManager.tsx | 2 +- src/client/src/modules/draft/DraftView.tsx | 199 +++++++++++++++--- src/client/src/modules/lobby/GameRoom.tsx | 2 +- src/client/src/modules/lobby/LobbyManager.tsx | 2 +- src/client/src/modules/tester/DeckTester.tsx | 2 +- .../modules/tournament/TournamentManager.tsx | 2 +- 12 files changed, 261 insertions(+), 42 deletions(-) create mode 100644 docs/development/devlog/2025-12-16-195000_draft_ui_polish.md create mode 100644 docs/development/devlog/2025-12-16-200500_resizable_draft_ui.md create mode 100644 docs/development/devlog/2025-12-16-201500_card_zoom.md create mode 100644 docs/development/devlog/2025-12-16-203000_zoom_zone.md diff --git a/docs/development/CENTRAL.md b/docs/development/CENTRAL.md index 1ba6be1..687bd52 100644 --- a/docs/development/CENTRAL.md +++ b/docs/development/CENTRAL.md @@ -13,3 +13,6 @@ - [Draft Rules & Pick Logic](./devlog/2025-12-16-180000_draft_rules_implementation.md): Completed. Enforced 4-player minimum and "Pick 2" rule for 4-player drafts. - [Fix Pack Duplication](./devlog/2025-12-16-184500_fix_pack_duplication.md): Completed. Enforced deep cloning and unique IDs for all draft packs to prevent opening identical packs. - [Reconnection & Auto-Pick](./devlog/2025-12-16-191500_reconnection_and_autopick.md): Completed. Implemented session persistence, seamless reconnection, and 30s auto-pick on disconnect. +- [Draft Interface UI Polish](./devlog/2025-12-16-195000_draft_ui_polish.md): Completed. Redesigned the draft view for a cleaner, immersive, game-like experience with no unnecessary scrolls. +- [Resizable Draft Interface](./devlog/2025-12-16-200500_resizable_draft_ui.md): Completed. Implemented user-resizable pool panel and card sizes with persistence. +- [Card Zoom (Dedicated Zone)](./devlog/2025-12-16-203000_zoom_zone.md): Completed. Refactored layout to show zoomed card in a dedicated side panel. diff --git a/docs/development/devlog/2025-12-16-195000_draft_ui_polish.md b/docs/development/devlog/2025-12-16-195000_draft_ui_polish.md new file mode 100644 index 0000000..2749ec7 --- /dev/null +++ b/docs/development/devlog/2025-12-16-195000_draft_ui_polish.md @@ -0,0 +1,23 @@ +# Draft Interface UI Polish + +## Status +- [x] Analyze current UI issues (bottom border, scrolling). +- [x] Remove global padding from `App.tsx`. +- [x] Refactor `DraftView.tsx` for a cleaner, game-like experience. +- [x] Implement immersive 3D effects and tray-style pool view. + +## Context +The user requested to improve the draft card pick interface. Specifically to remove the ugly bottom border, avoid page scrolls, and make it feel more like a game. + +## Implementation Details + +### `src/client/src/App.tsx` +- Removed `pb-20` from the main container to allow full-screen layouts without forced scrolling at the bottom. + +### `src/client/src/modules/draft/DraftView.tsx` +- **Layout**: Changed to relative positioning with `overflow-hidden` to contain all elements within the viewport. +- **Visuals**: + - Added a radial gradient background overlay. + - Redesigned the "Current Pack" area with `[perspective:1000px]` and 3D hover transforms. + - Redesigned the "Your Pool" bottom area to be a "tray" with `backdrop-blur`, gradient background, and removed the boxy border. +- **Scrollbars**: Hidden scrollbars in the main pack view for a cleaner look (`[&::-webkit-scrollbar]:hidden`). diff --git a/docs/development/devlog/2025-12-16-200500_resizable_draft_ui.md b/docs/development/devlog/2025-12-16-200500_resizable_draft_ui.md new file mode 100644 index 0000000..40efe19 --- /dev/null +++ b/docs/development/devlog/2025-12-16-200500_resizable_draft_ui.md @@ -0,0 +1,34 @@ +# Resizable Draft Interface + +## Status +- [x] Implement resizable bottom "Pool" panel. +- [x] Implement resizable card size slider. +- [x] Persist settings to `localStorage`. + +## Technical Plan + +### `src/client/src/modules/draft/DraftView.tsx` + +1. **State Initialization**: + - `poolHeight`: number (default ~220). Load from `localStorage.getItem('draft_poolHeight')`. + - `cardScale`: number (default 1 or specific width like 224px). Load from `localStorage.getItem('draft_cardScale')`. + +2. **Resize Handle**: + - Insert a `div` cursor-row-resize between the Main Area and the Bottom Area. + - Implement `onMouseDown` handler to start dragging. + - Implement `onMouseMove` and `onMouseUp` on the window/document to handle the resize logic. + +3. **Card Size Control**: + - Add a slider (``) in the Top Header area to adjust `cardScale`. + - Apply this scale to the card images/containers in the Main Area. + +4. **Persistence**: + - `useEffect` hooks to save state changes to `localStorage`. + +5. **Refactoring Styling**: + - Change `h-[220px]` class on the bottom panel to `style={{ height: poolHeight }}`. + - Update card width class `w-56` to dynamic style or class based on scale. + +## UX Improvements +- Add limit constraints (min height for pool, max height for pool). +- Add limit constraints for card size (min visible, max huge). diff --git a/docs/development/devlog/2025-12-16-201500_card_zoom.md b/docs/development/devlog/2025-12-16-201500_card_zoom.md new file mode 100644 index 0000000..087aeaf --- /dev/null +++ b/docs/development/devlog/2025-12-16-201500_card_zoom.md @@ -0,0 +1,13 @@ +# Card Zoom on Hover + +## Status +- [x] Add `hoveredCard` state to `DraftView`. +- [x] Implement `onMouseEnter`/`onMouseLeave` handlers for cards in both Pick and Pool areas. +- [x] rendering a fixed, high z-index preview of the hovered card. +- [x] Disable right-click context menu on Draft interface. + +## Implementation Details +- **File**: `src/client/src/modules/draft/DraftView.tsx` +- **Zoom Component**: A fixed `div` containing the large card image. +- **Position**: Fixed to the left or right side of the screen (e.g., `left-10 top-1/2 -translate-y-1/2`) to avoid covering the grid being interacted with (which is usually centered). +- **Styling**: Large size (e.g., `w-80` or `h-[500px]`), shadow, border, rounded corners. diff --git a/docs/development/devlog/2025-12-16-203000_zoom_zone.md b/docs/development/devlog/2025-12-16-203000_zoom_zone.md new file mode 100644 index 0000000..424e34c --- /dev/null +++ b/docs/development/devlog/2025-12-16-203000_zoom_zone.md @@ -0,0 +1,15 @@ +# Card Zoom on Hover - Dedicated Zone + +## Status +- [x] Add `hoveredCard` state to `DraftView` (Already done). +- [x] Implement `onMouseEnter`/`onMouseLeave` handlers (Already done). +- [x] Refactor `DraftView` layout to include a dedicated sidebar for card preview. +- [x] Move the zoomed card image into this dedicated zone instead of a fixed overlay. + +## Implementation Details +- **File**: `src/client/src/modules/draft/DraftView.tsx` +- **Layout Change**: + - Wrap the central card selection area in a `flex-row` container. + - Add a Left Sidebar (e.g., `w-80`) reserved for the zoomed card. + - Ensure the main card grid takes up the remaining space (`flex-1`). +- **Behavior**: When no card is hovered, the sidebar can show a placeholder or remain empty/decorative. diff --git a/src/client/src/App.tsx b/src/client/src/App.tsx index c23efd4..4827ddc 100644 --- a/src/client/src/App.tsx +++ b/src/client/src/App.tsx @@ -35,8 +35,8 @@ export const App: React.FC = () => { }, [generatedPacks]); return ( -
-
+
+
@@ -75,7 +75,7 @@ export const App: React.FC = () => {
-
+
{activeTab === 'draft' && ( = ({ packs, setPacks, onGoT }; return ( -
+
{/* --- LEFT COLUMN: CONTROLS --- */}
diff --git a/src/client/src/modules/draft/DraftView.tsx b/src/client/src/modules/draft/DraftView.tsx index 26eb8d8..4623beb 100644 --- a/src/client/src/modules/draft/DraftView.tsx +++ b/src/client/src/modules/draft/DraftView.tsx @@ -18,6 +18,59 @@ export const DraftView: React.FC = ({ draftState, roomId, curren return () => clearInterval(interval); }, []); // Reset timer on new pack? Simplified for now. + // --- UI State & Persistence --- + const [poolHeight, setPoolHeight] = useState(() => { + const saved = localStorage.getItem('draft_poolHeight'); + return saved ? parseInt(saved, 10) : 220; + }); + + const [cardScale, setCardScale] = useState(() => { + const saved = localStorage.getItem('draft_cardScale'); + return saved ? parseFloat(saved) : 0.7; + }); + + const [isResizing, setIsResizing] = useState(false); + + // Persist settings + useEffect(() => { + localStorage.setItem('draft_poolHeight', poolHeight.toString()); + }, [poolHeight]); + + useEffect(() => { + localStorage.setItem('draft_cardScale', cardScale.toString()); + }, [cardScale]); + + // Resize Handlers + const startResizing = (e: React.MouseEvent) => { + setIsResizing(true); + e.preventDefault(); + }; + + useEffect(() => { + const stopResizing = () => setIsResizing(false); + const resize = (e: MouseEvent) => { + if (isResizing) { + const newHeight = window.innerHeight - e.clientY; + // Limits: Min 100px, Max 60% of screen + const maxHeight = window.innerHeight * 0.6; + if (newHeight >= 100 && newHeight <= maxHeight) { + setPoolHeight(newHeight); + } + } + }; + + if (isResizing) { + window.addEventListener('mousemove', resize); + window.addEventListener('mouseup', stopResizing); + } + return () => { + window.removeEventListener('mousemove', resize); + window.removeEventListener('mouseup', stopResizing); + }; + }, [isResizing]); + + const [hoveredCard, setHoveredCard] = useState(null); + const activePack = draftState.players[currentPlayerId]?.activePack; const pickedCards = draftState.players[currentPlayerId]?.pool || []; @@ -35,54 +88,132 @@ export const DraftView: React.FC = ({ draftState, roomId, curren } return ( -
+
e.preventDefault()}> +
+ {/* Top Header: Timer & Pack Info */} -
-
-

- Pack {draftState.packNumber} -

- Pick {pickedCards.length % 15 + 1} -
-
- 00:{timer < 10 ? `0${timer}` : timer} +
+
+
+
+

+ Pack {draftState.packNumber} +

+ Pick {pickedCards.length % 15 + 1} +
+ + {/* Card Scalar */} +
+ + setCardScale(parseFloat(e.target.value))} + className="w-full h-1 bg-slate-700 rounded-lg appearance-none cursor-pointer accent-emerald-500" + /> +
+
+ +
+ 00:{timer < 10 ? `0${timer}` : timer} +
- {/* Main Area: Current Pack */} -
-

Select a Card

-
- {activePack.cards.map((card: any) => ( + {/* Middle Content: Zoom Sidebar + Pack Grid */} +
+ + {/* Dedicated Zoom Zone (Left Sidebar) */} +
+ {hoveredCard ? ( +
+ {hoveredCard.name} +
+

{hoveredCard.name}

+

{hoveredCard.type_line}

+
+
+ ) : ( +
+
+ Hover Card +
+

Hover over a card to view clear details.

+
+ )} +
+ + {/* Main Area: Current Pack */} +
+
+

Select a Card

+
+ {activePack.cards.map((card: any) => ( +
handlePick(card.id)} + onMouseEnter={() => setHoveredCard(card)} + onMouseLeave={() => setHoveredCard(null)} + > +
+ {card.name} +
+ ))} +
+
+
+ +
+ + {/* Resize Handle */} +
+
+
+ + {/* Bottom Area: Drafted Pool Preview */} +
+
+

+ + Your Pool ({pickedCards.length}) +

+
+
+ {pickedCards.map((card: any, idx: number) => (
handlePick(card.id)} + key={`${card.id}-${idx}`} + className="relative group shrink-0 transition-all hover:-translate-y-10 h-full flex items-center" + onMouseEnter={() => setHoveredCard(card)} + onMouseLeave={() => setHoveredCard(null)} > {card.name}
))}
- - {/* Bottom Area: Drafted Pool Preview */} -
-

Your Pool ({pickedCards.length})

-
- {pickedCards.map((card: any, idx: number) => ( - {card.name} - ))} -
-
); }; diff --git a/src/client/src/modules/lobby/GameRoom.tsx b/src/client/src/modules/lobby/GameRoom.tsx index 2a7f248..0504b26 100644 --- a/src/client/src/modules/lobby/GameRoom.tsx +++ b/src/client/src/modules/lobby/GameRoom.tsx @@ -247,7 +247,7 @@ export const GameRoom: React.FC = ({ room: initialRoom, currentPl }; return ( -
+
{renderContent()} {/* Sidebar: Players & Chat */} diff --git a/src/client/src/modules/lobby/LobbyManager.tsx b/src/client/src/modules/lobby/LobbyManager.tsx index 64c9c10..0fd6951 100644 --- a/src/client/src/modules/lobby/LobbyManager.tsx +++ b/src/client/src/modules/lobby/LobbyManager.tsx @@ -188,7 +188,7 @@ export const LobbyManager: React.FC = ({ generatedPacks }) => } return ( -
+

Multiplayer Lobby diff --git a/src/client/src/modules/tester/DeckTester.tsx b/src/client/src/modules/tester/DeckTester.tsx index 59215b6..fadef03 100644 --- a/src/client/src/modules/tester/DeckTester.tsx +++ b/src/client/src/modules/tester/DeckTester.tsx @@ -120,7 +120,7 @@ export const DeckTester: React.FC = () => { } return ( -
+

Deck Tester diff --git a/src/client/src/modules/tournament/TournamentManager.tsx b/src/client/src/modules/tournament/TournamentManager.tsx index df20566..0baa59c 100644 --- a/src/client/src/modules/tournament/TournamentManager.tsx +++ b/src/client/src/modules/tournament/TournamentManager.tsx @@ -48,7 +48,7 @@ export const TournamentManager: React.FC = () => { }; return ( -
+

Players