feat: Refactor application layout for full-height content and implement resizable draft UI with card zoom and scaling.
This commit is contained in:
@@ -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.
|
- [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.
|
- [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.
|
- [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.
|
||||||
|
|||||||
23
docs/development/devlog/2025-12-16-195000_draft_ui_polish.md
Normal file
23
docs/development/devlog/2025-12-16-195000_draft_ui_polish.md
Normal file
@@ -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`).
|
||||||
@@ -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 (`<input type="range" />`) 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).
|
||||||
13
docs/development/devlog/2025-12-16-201500_card_zoom.md
Normal file
13
docs/development/devlog/2025-12-16-201500_card_zoom.md
Normal file
@@ -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.
|
||||||
15
docs/development/devlog/2025-12-16-203000_zoom_zone.md
Normal file
15
docs/development/devlog/2025-12-16-203000_zoom_zone.md
Normal file
@@ -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.
|
||||||
@@ -35,8 +35,8 @@ export const App: React.FC = () => {
|
|||||||
}, [generatedPacks]);
|
}, [generatedPacks]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-slate-900 text-slate-100 font-sans pb-20">
|
<div className="h-screen flex flex-col bg-slate-900 text-slate-100 font-sans overflow-hidden">
|
||||||
<header className="bg-slate-800 border-b border-slate-700 p-4 sticky top-0 z-50 shadow-lg">
|
<header className="bg-slate-800 border-b border-slate-700 p-4 shrink-0 z-50 shadow-lg">
|
||||||
<div className="max-w-7xl mx-auto flex flex-col md:flex-row justify-between items-center gap-4">
|
<div className="max-w-7xl mx-auto flex flex-col md:flex-row justify-between items-center gap-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="bg-purple-600 p-2 rounded-lg"><Layers className="w-6 h-6 text-white" /></div>
|
<div className="bg-purple-600 p-2 rounded-lg"><Layers className="w-6 h-6 text-white" /></div>
|
||||||
@@ -75,7 +75,7 @@ export const App: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main className="flex-1 overflow-hidden relative">
|
||||||
{activeTab === 'draft' && (
|
{activeTab === 'draft' && (
|
||||||
<CubeManager
|
<CubeManager
|
||||||
packs={generatedPacks}
|
packs={generatedPacks}
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, onGoT
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-7xl mx-auto grid grid-cols-1 lg:grid-cols-12 gap-8 p-4 md:p-6">
|
<div className="h-full overflow-y-auto max-w-7xl mx-auto grid grid-cols-1 lg:grid-cols-12 gap-8 p-4 md:p-6">
|
||||||
|
|
||||||
{/* --- LEFT COLUMN: CONTROLS --- */}
|
{/* --- LEFT COLUMN: CONTROLS --- */}
|
||||||
<div className="lg:col-span-4 flex flex-col gap-4">
|
<div className="lg:col-span-4 flex flex-col gap-4">
|
||||||
|
|||||||
@@ -18,6 +18,59 @@ export const DraftView: React.FC<DraftViewProps> = ({ draftState, roomId, curren
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []); // Reset timer on new pack? Simplified for now.
|
}, []); // Reset timer on new pack? Simplified for now.
|
||||||
|
|
||||||
|
// --- UI State & Persistence ---
|
||||||
|
const [poolHeight, setPoolHeight] = useState<number>(() => {
|
||||||
|
const saved = localStorage.getItem('draft_poolHeight');
|
||||||
|
return saved ? parseInt(saved, 10) : 220;
|
||||||
|
});
|
||||||
|
|
||||||
|
const [cardScale, setCardScale] = useState<number>(() => {
|
||||||
|
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<any>(null);
|
||||||
|
|
||||||
const activePack = draftState.players[currentPlayerId]?.activePack;
|
const activePack = draftState.players[currentPlayerId]?.activePack;
|
||||||
const pickedCards = draftState.players[currentPlayerId]?.pool || [];
|
const pickedCards = draftState.players[currentPlayerId]?.pool || [];
|
||||||
|
|
||||||
@@ -35,54 +88,132 @@ export const DraftView: React.FC<DraftViewProps> = ({ draftState, roomId, curren
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-slate-950 text-white p-4 gap-4">
|
<div className="flex flex-col h-full bg-slate-950 text-white overflow-hidden relative select-none" onContextMenu={(e) => e.preventDefault()}>
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-slate-900 via-slate-950 to-black opacity-50 pointer-events-none"></div>
|
||||||
|
|
||||||
{/* Top Header: Timer & Pack Info */}
|
{/* Top Header: Timer & Pack Info */}
|
||||||
<div className="flex justify-between items-center bg-slate-900 p-4 rounded-lg border border-slate-800">
|
<div className="shrink-0 p-4 z-10">
|
||||||
<div>
|
<div className="flex justify-between items-center bg-slate-900/80 backdrop-blur border border-slate-800 p-4 rounded-lg shadow-lg">
|
||||||
<h2 className="text-xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-amber-400 to-orange-500">
|
<div className="flex items-center gap-8">
|
||||||
Pack {draftState.packNumber}
|
<div>
|
||||||
</h2>
|
<h2 className="text-xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-amber-400 to-orange-500 shadow-amber-500/20 drop-shadow-sm">
|
||||||
<span className="text-sm text-slate-400">Pick {pickedCards.length % 15 + 1}</span>
|
Pack {draftState.packNumber}
|
||||||
</div>
|
</h2>
|
||||||
<div className="text-3xl font-mono text-emerald-400 font-bold">
|
<span className="text-sm text-slate-400 font-medium">Pick {pickedCards.length % 15 + 1}</span>
|
||||||
00:{timer < 10 ? `0${timer}` : timer}
|
</div>
|
||||||
|
|
||||||
|
{/* Card Scalar */}
|
||||||
|
<div className="hidden md:flex flex-col gap-1 w-32">
|
||||||
|
<label className="text-[10px] text-slate-500 uppercase font-bold tracking-wider">Card Size</label>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="0.5"
|
||||||
|
max="1.5"
|
||||||
|
step="0.1"
|
||||||
|
value={cardScale}
|
||||||
|
onChange={(e) => setCardScale(parseFloat(e.target.value))}
|
||||||
|
className="w-full h-1 bg-slate-700 rounded-lg appearance-none cursor-pointer accent-emerald-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-4xl font-mono text-emerald-400 font-bold drop-shadow-[0_0_10px_rgba(52,211,153,0.5)]">
|
||||||
|
00:{timer < 10 ? `0${timer}` : timer}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Area: Current Pack */}
|
{/* Middle Content: Zoom Sidebar + Pack Grid */}
|
||||||
<div className="flex-1 bg-slate-900/50 p-6 rounded-xl border border-slate-800 overflow-y-auto">
|
<div className="flex-1 flex overflow-hidden">
|
||||||
<h3 className="text-center text-slate-400 uppercase tracking-widest text-sm font-bold mb-6">Select a Card</h3>
|
|
||||||
<div className="flex flex-wrap justify-center gap-4">
|
{/* Dedicated Zoom Zone (Left Sidebar) */}
|
||||||
{activePack.cards.map((card: any) => (
|
<div className="hidden lg:flex w-80 shrink-0 flex-col items-center justify-start pt-8 border-r border-slate-800/50 bg-slate-900/20 backdrop-blur-sm z-10">
|
||||||
|
{hoveredCard ? (
|
||||||
|
<div className="animate-in fade-in slide-in-from-left-4 duration-300 p-4 sticky top-4">
|
||||||
|
<img
|
||||||
|
src={hoveredCard.image || hoveredCard.image_uris?.normal || hoveredCard.card_faces?.[0]?.image_uris?.normal}
|
||||||
|
alt={hoveredCard.name}
|
||||||
|
className="w-full rounded-xl shadow-2xl shadow-black ring-1 ring-white/10"
|
||||||
|
/>
|
||||||
|
<div className="mt-4 text-center">
|
||||||
|
<h3 className="text-lg font-bold text-slate-200">{hoveredCard.name}</h3>
|
||||||
|
<p className="text-xs text-slate-400 uppercase tracking-wider mt-1">{hoveredCard.type_line}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center justify-center h-full text-slate-600 p-8 text-center opacity-50">
|
||||||
|
<div className="w-48 h-64 border-2 border-dashed border-slate-700 rounded-xl mb-4 flex items-center justify-center">
|
||||||
|
<span className="text-xs uppercase font-bold tracking-widest">Hover Card</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm">Hover over a card to view clear details.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Area: Current Pack */}
|
||||||
|
<div className="flex-1 overflow-y-auto p-4 z-0 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:'none'] [scrollbar-width:'none']">
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-full pb-10">
|
||||||
|
<h3 className="text-center text-slate-500 uppercase tracking-[0.2em] text-xs font-bold mb-8">Select a Card</h3>
|
||||||
|
<div className="flex flex-wrap justify-center gap-6 [perspective:1000px]">
|
||||||
|
{activePack.cards.map((card: any) => (
|
||||||
|
<div
|
||||||
|
key={card.id}
|
||||||
|
className="group relative transition-all duration-300 hover:scale-110 hover:-translate-y-4 hover:z-50 cursor-pointer"
|
||||||
|
style={{ width: `${14 * cardScale}rem` }}
|
||||||
|
onClick={() => handlePick(card.id)}
|
||||||
|
onMouseEnter={() => setHoveredCard(card)}
|
||||||
|
onMouseLeave={() => setHoveredCard(null)}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 rounded-xl bg-emerald-500 blur-xl opacity-0 group-hover:opacity-40 transition-opacity duration-300"></div>
|
||||||
|
<img
|
||||||
|
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
||||||
|
alt={card.name}
|
||||||
|
className="w-full rounded-xl shadow-2xl shadow-black group-hover:ring-2 ring-emerald-400/50 relative z-10"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Resize Handle */}
|
||||||
|
<div
|
||||||
|
className="h-1 bg-slate-800 hover:bg-emerald-500 cursor-row-resize z-30 transition-colors w-full flex items-center justify-center shrink-0"
|
||||||
|
onMouseDown={startResizing}
|
||||||
|
>
|
||||||
|
<div className="w-16 h-1 bg-slate-600 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Area: Drafted Pool Preview */}
|
||||||
|
<div
|
||||||
|
className="shrink-0 bg-gradient-to-t from-slate-950 to-slate-900/90 backdrop-blur-md flex flex-col z-20 shadow-[0_-10px_40px_rgba(0,0,0,0.5)] transition-all ease-out duration-75"
|
||||||
|
style={{ height: `${poolHeight}px` }}
|
||||||
|
>
|
||||||
|
<div className="px-6 py-2 flex items-center justify-between shrink-0">
|
||||||
|
<h3 className="text-xs font-bold text-slate-400 uppercase tracking-wider flex items-center gap-2">
|
||||||
|
<span className="w-2 h-2 rounded-full bg-emerald-500"></span>
|
||||||
|
Your Pool ({pickedCards.length})
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 overflow-x-auto flex items-center gap-2 px-6 pb-4 custom-scrollbar">
|
||||||
|
{pickedCards.map((card: any, idx: number) => (
|
||||||
<div
|
<div
|
||||||
key={card.id}
|
key={`${card.id}-${idx}`}
|
||||||
className="group relative transition-all hover:scale-110 hover:z-10 cursor-pointer"
|
className="relative group shrink-0 transition-all hover:-translate-y-10 h-full flex items-center"
|
||||||
onClick={() => handlePick(card.id)}
|
onMouseEnter={() => setHoveredCard(card)}
|
||||||
|
onMouseLeave={() => setHoveredCard(null)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
||||||
alt={card.name}
|
alt={card.name}
|
||||||
className="w-48 rounded-lg shadow-xl shadow-black/50 group-hover:shadow-emerald-500/50 group-hover:ring-2 ring-emerald-400"
|
className="h-[90%] w-auto rounded-lg shadow-lg border border-slate-700/50 group-hover:border-emerald-500/50 group-hover:shadow-emerald-500/20 transition-all object-contain"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Area: Drafted Pool Preview */}
|
|
||||||
<div className="h-48 bg-slate-900 p-4 rounded-lg border border-slate-800 flex flex-col">
|
|
||||||
<h3 className="text-xs font-bold text-slate-500 uppercase mb-2">Your Pool ({pickedCards.length})</h3>
|
|
||||||
<div className="flex-1 overflow-x-auto flex items-center gap-1 pb-2">
|
|
||||||
{pickedCards.map((card: any, idx: number) => (
|
|
||||||
<img
|
|
||||||
key={`${card.id}-${idx}`}
|
|
||||||
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
|
||||||
alt={card.name}
|
|
||||||
className="h-full rounded shadow-md"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ export const GameRoom: React.FC<GameRoomProps> = ({ room: initialRoom, currentPl
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-100px)] gap-4">
|
<div className="flex h-full gap-4">
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
|
|
||||||
{/* Sidebar: Players & Chat */}
|
{/* Sidebar: Players & Chat */}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export const LobbyManager: React.FC<LobbyManagerProps> = ({ generatedPacks }) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto p-4 md:p-10">
|
<div className="h-full overflow-y-auto max-w-4xl mx-auto p-4 md:p-10">
|
||||||
<div className="bg-slate-800 rounded-2xl p-8 border border-slate-700 shadow-2xl">
|
<div className="bg-slate-800 rounded-2xl p-8 border border-slate-700 shadow-2xl">
|
||||||
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
|
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
|
||||||
<Users className="w-8 h-8 text-purple-500" /> Multiplayer Lobby
|
<Users className="w-8 h-8 text-purple-500" /> Multiplayer Lobby
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export const DeckTester: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto p-4 md:p-8">
|
<div className="h-full overflow-y-auto max-w-4xl mx-auto p-4 md:p-8">
|
||||||
<div className="bg-slate-800 rounded-2xl p-8 border border-slate-700 shadow-2xl">
|
<div className="bg-slate-800 rounded-2xl p-8 border border-slate-700 shadow-2xl">
|
||||||
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
|
<h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
|
||||||
<Play className="w-8 h-8 text-emerald-500" /> Deck Tester
|
<Play className="w-8 h-8 text-emerald-500" /> Deck Tester
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const TournamentManager: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto p-4 md:p-6">
|
<div className="h-full overflow-y-auto max-w-4xl mx-auto p-4 md:p-6">
|
||||||
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700 shadow-xl mb-8">
|
<div className="bg-slate-800 rounded-xl p-6 border border-slate-700 shadow-xl mb-8">
|
||||||
<h2 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
<h2 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
|
||||||
<Users className="w-5 h-5 text-blue-400" /> Players
|
<Users className="w-5 h-5 text-blue-400" /> Players
|
||||||
|
|||||||
Reference in New Issue
Block a user