From 3936260861fbcd876b21bbd62270ff9289209fa2 Mon Sep 17 00:00:00 2001 From: dnviti Date: Wed, 17 Dec 2025 18:35:57 +0100 Subject: [PATCH] feat: Implement 3D flip card preview with foil effects in Draft View and add hover preview control to StackView. --- docs/development/CENTRAL.md | 3 + .../2025-12-17-183000_restore_stack_hover.md | 16 ++ .../2025-12-17-183500_draft_ui_upgrade.md | 16 ++ .../2025-12-17-184000_fix_draft_pool_ui.md | 13 ++ src/client/src/components/StackView.tsx | 33 +++-- .../src/modules/draft/DeckBuilderView.tsx | 1 + src/client/src/modules/draft/DraftView.tsx | 140 +++++++++++++----- 7 files changed, 167 insertions(+), 55 deletions(-) create mode 100644 docs/development/devlog/2025-12-17-183000_restore_stack_hover.md create mode 100644 docs/development/devlog/2025-12-17-183500_draft_ui_upgrade.md create mode 100644 docs/development/devlog/2025-12-17-184000_fix_draft_pool_ui.md diff --git a/docs/development/CENTRAL.md b/docs/development/CENTRAL.md index 5140340..1d91ef3 100644 --- a/docs/development/CENTRAL.md +++ b/docs/development/CENTRAL.md @@ -84,4 +84,7 @@ - [Gameplay Magnified View & Timeout](./devlog/2025-12-17-161500_gameplay_magnified_view_and_timeout.md): Completed. Added magnified view with full card details (Oracle text, type, mana) to gameplay and disabled timeout. - [Test Deck Feature](./devlog/2025-12-17-162500_test_deck_feature.md): Completed. Implemented "Test Solo" button in Cube Manager to instantly start a solo game with a randomized deck from generated packs. - [Update Deck Auto-Fill](./devlog/2025-12-17-165500_update_deck_autofill.md): Completed. Updated deck builder "Auto-Fill" to add lands as individual cards to the deck list for easier management. +- [2025-12-17-183000_restore_stack_hover](./devlog/2025-12-17-183000_restore_stack_hover.md): Completed. Restored hover magnified card for Stack View in Pack Generation. +- [2025-12-17-183500_draft_ui_upgrade](./devlog/2025-12-17-183500_draft_ui_upgrade.md): Completed. Implemented 3D flip preview and consistent foil rendering in Draft View. +- [2025-12-17-184000_fix_draft_pool_ui](./devlog/2025-12-17-184000_fix_draft_pool_ui.md): Completed. Fixed "Your Pool" resizing bugs and removed unwanted hover animation. - [Customizable Deck Builder Layout](./devlog/2025-12-17-170000_customizable_deck_builder.md): Completed. Implemented switchable Vertical (Side-by-Side) and Horizontal (Top-Bottom) layouts, with an integrated, improved Land Station. diff --git a/docs/development/devlog/2025-12-17-183000_restore_stack_hover.md b/docs/development/devlog/2025-12-17-183000_restore_stack_hover.md new file mode 100644 index 0000000..40c6dac --- /dev/null +++ b/docs/development/devlog/2025-12-17-183000_restore_stack_hover.md @@ -0,0 +1,16 @@ +# Restore Hover Magnified Card for Stack View + +## Task +Restore the hover magnified card functionality for the stacked view in the pack generation UI, while ensuring it remains disabled for the deck building UI. + +## Changes +- Modified `src/client/src/components/StackView.tsx`: + - Imported `CardHoverWrapper`. + - Added `disableHoverPreview` prop (default `false`). + - Wrapped card elements with `CardHoverWrapper`, passing `preventPreview` based on the new prop and card width. +- Modified `src/client/src/modules/draft/DeckBuilderView.tsx`: + - Passed `disableHoverPreview={true}` to `StackView` to maintain existing behavior for the deck builder (which uses a dedicated sidebar preview). + +## Outcome +- Pack Generation UI (Cube Manager) now shows floating previews for cards in Stack View. +- Deck Builder UI remains unchanged (no double previews). diff --git a/docs/development/devlog/2025-12-17-183500_draft_ui_upgrade.md b/docs/development/devlog/2025-12-17-183500_draft_ui_upgrade.md new file mode 100644 index 0000000..542c29a --- /dev/null +++ b/docs/development/devlog/2025-12-17-183500_draft_ui_upgrade.md @@ -0,0 +1,16 @@ +# Draft Cards Picker UI Update + +## Task +Update the `DraftView` to implement a 3D-flipping card preview sidebar (consistent with the Deck Builder) and ensure foil cards are rendered correctly in both the preview and the main selection grid. + +## Changes +- Modified `src/client/src/modules/draft/DraftView.tsx`: + - Imported `FoilOverlay` component. + - Defined `normalizeCard` helper to standardise card object structure (handling images and finish). + - Added `displayCard` state to persist card details during the flip animation. + - Replaced the previous fade-in sidebar with the 3D perspective flip container from `DeckBuilderView`. + - Updated the card rendering loop in `activePack` to use `normalizeCard` and conditionally render `FoilOverlay` and foil-specific styles (purple glow, badges). + +## Outcome +- **Sidebar**: Now features a persistent 3D flip animation. When hovering a card, it flips to show the front; when not hovering, it shows the card back (`/images/back.jpg`). +- **Foil Support**: Start using `FoilOverlay` for both the main draft grid and the sidebar preview, providing visual consistency for premium cards. diff --git a/docs/development/devlog/2025-12-17-184000_fix_draft_pool_ui.md b/docs/development/devlog/2025-12-17-184000_fix_draft_pool_ui.md new file mode 100644 index 0000000..33887c0 --- /dev/null +++ b/docs/development/devlog/2025-12-17-184000_fix_draft_pool_ui.md @@ -0,0 +1,13 @@ +# Fix Draft UI Issues + +## Task +Fix bugs in the Draft Pick UI related to the "Your Pool" section. + +## Changes +- Modified `src/client/src/modules/draft/DraftView.tsx`: + - **Resize Handle Fix**: Updated the resize event listeners (`mousemove`, `mouseup`) to be attached to `document` instead of `window`. This ensures the resize action continues smoothly even if the mouse leaves the browser window or moves rapidly over iframes/other elements that might swallow events. + - **Remove Hover Effect**: Removed the `hover:-translate-y-10` class from the drafted card items in the bottom pool view. The cards will now remain stationary on hover, as requested. + +## Outcome +- The pool resizing experience is now consistent and does not lose focus when dragging quickly. +- Cards in the "Your Pool" strip no longer jump up when hovered, providing a stable viewing experience. diff --git a/src/client/src/components/StackView.tsx b/src/client/src/components/StackView.tsx index 8a2160d..1072d5e 100644 --- a/src/client/src/components/StackView.tsx +++ b/src/client/src/components/StackView.tsx @@ -1,12 +1,13 @@ import React, { useMemo } from 'react'; import { DraftCard } from '../services/PackGeneratorService'; -import { FoilOverlay } from './CardPreview'; +import { FoilOverlay, CardHoverWrapper } from './CardPreview'; interface StackViewProps { cards: DraftCard[]; cardWidth?: number; onCardClick?: (card: DraftCard) => void; onHover?: (card: DraftCard | null) => void; + disableHoverPreview?: boolean; } const CATEGORY_ORDER = [ @@ -21,7 +22,7 @@ const CATEGORY_ORDER = [ 'Other' ]; -export const StackView: React.FC = ({ cards, cardWidth = 150, onCardClick, onHover }) => { +export const StackView: React.FC = ({ cards, cardWidth = 150, onCardClick, onHover, disableHoverPreview = false }) => { const categorizedCards = useMemo(() => { const categories: Record = {}; @@ -86,19 +87,21 @@ export const StackView: React.FC = ({ cards, cardWidth = 150, on onMouseLeave={() => onHover && onHover(null)} onClick={() => onCardClick && onCardClick(card)} > -
- {card.name} - {/* Optional: Shine effect for foils if visible? */} - {card.finish === 'foil' && } -
+ = 200}> +
+ {card.name} + {/* Optional: Shine effect for foils if visible? */} + {card.finish === 'foil' && } +
+
) })} diff --git a/src/client/src/modules/draft/DeckBuilderView.tsx b/src/client/src/modules/draft/DeckBuilderView.tsx index b267046..22b5744 100644 --- a/src/client/src/modules/draft/DeckBuilderView.tsx +++ b/src/client/src/modules/draft/DeckBuilderView.tsx @@ -92,6 +92,7 @@ const CardsDisplay: React.FC<{ cardWidth={cardWidth} onCardClick={(c) => onCardClick(c)} onHover={(c) => onHover(c)} + disableHoverPreview={true} /> ) diff --git a/src/client/src/modules/draft/DraftView.tsx b/src/client/src/modules/draft/DraftView.tsx index 5be6c10..e99613d 100644 --- a/src/client/src/modules/draft/DraftView.tsx +++ b/src/client/src/modules/draft/DraftView.tsx @@ -3,6 +3,14 @@ import React, { useState, useEffect } from 'react'; import { socketService } from '../../services/SocketService'; import { LogOut } from 'lucide-react'; import { Modal } from '../../components/Modal'; +import { FoilOverlay } from '../../components/CardPreview'; + +// Helper to normalize card data for visuals +const normalizeCard = (c: any) => ({ + ...c, + finish: c.finish || 'nonfoil', + image: c.image || c.image_uris?.normal || c.card_faces?.[0]?.image_uris?.normal +}); interface DraftViewProps { draftState: any; @@ -76,16 +84,23 @@ export const DraftView: React.FC = ({ draftState, currentPlayerI }; if (isResizing) { - window.addEventListener('mousemove', resize); - window.addEventListener('mouseup', stopResizing); + document.addEventListener('mousemove', resize); + document.addEventListener('mouseup', stopResizing); } return () => { - window.removeEventListener('mousemove', resize); - window.removeEventListener('mouseup', stopResizing); + document.removeEventListener('mousemove', resize); + document.removeEventListener('mouseup', stopResizing); }; }, [isResizing]); const [hoveredCard, setHoveredCard] = useState(null); + const [displayCard, setDisplayCard] = useState(null); + + useEffect(() => { + if (hoveredCard) { + setDisplayCard(normalizeCard(hoveredCard)); + } + }, [hoveredCard]); const activePack = draftState.players[currentPlayerId]?.activePack; const pickedCards = draftState.players[currentPlayerId]?.pool || []; @@ -152,27 +167,61 @@ export const DraftView: React.FC = ({ draftState, currentPlayerI
{/* Dedicated Zoom Zone (Left Sidebar) */} -
- {hoveredCard ? ( -
- {hoveredCard.name} -
-

{hoveredCard.name}

-

{hoveredCard.type_line}

+
+
+
+ {/* Front Face (Hovered Card) */} +
+ {(hoveredCard || displayCard) && ( +
+ {(hoveredCard + {/* Foil Overlay for Preview */} + {((hoveredCard || displayCard).finish === 'foil') && } + +
+

{(hoveredCard || displayCard).name}

+

{(hoveredCard || displayCard).type_line}

+
+
+ )} +
+ + {/* Back Face (Card Back) */} +
+ Card Back
- ) : ( -
-
- Hover Card + + {/* Oracle Text Box Below Card */} + {(hoveredCard || displayCard)?.oracle_text && ( +
+ {(hoveredCard || displayCard).oracle_text.split('\n').map((line: string, i: number) =>

{line}

)}
-

Hover over a card to view clear details.

-
- )} + )} +
{/* Main Area: Current Pack OR Waiting State */} @@ -198,23 +247,34 @@ export const DraftView: React.FC = ({ draftState, currentPlayerI

Select a Card

- {activePack.cards.map((card: any) => ( -
handlePick(card.id)} - onMouseEnter={() => setHoveredCard(card)} - onMouseLeave={() => setHoveredCard(null)} - > -
- {card.name} -
- ))} + {activePack.cards.map((rawCard: any) => { + const card = normalizeCard(rawCard); + const isFoil = card.finish === 'foil'; + + return ( +
handlePick(card.id)} + onMouseEnter={() => setHoveredCard(card)} + onMouseLeave={() => setHoveredCard(null)} + > + {/* Foil Glow Effect */} + {isFoil &&
} + +
+ {card.name} + {isFoil && } + {isFoil &&
FOIL
} +
+
+ ); + })}
)} @@ -245,7 +305,7 @@ export const DraftView: React.FC = ({ draftState, currentPlayerI {pickedCards.map((card: any, idx: number) => (
setHoveredCard(card)} onMouseLeave={() => setHoveredCard(null)} >