feat: implement customizable vertical and horizontal deck builder layouts with a new layout switcher and associated rendering refactors.
This commit is contained in:
@@ -84,3 +84,4 @@
|
||||
- [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.
|
||||
- [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.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Customizable Deck Builder Layout
|
||||
|
||||
## Request
|
||||
The user wants to customize the Deck Builder interface with the following features:
|
||||
1. **Layout Modes**:
|
||||
* **Vertical View (Default)**: The current 3-column layout ([Zoom] | [Pool] | [Deck + Lands]).
|
||||
* **Horizontal View**: A new layout where Pool is above the Deck. Land Station should be to the left of the Card Pool in this mode.
|
||||
2. **Land Station Updates**:
|
||||
* Remove "(Unlimited)" text.
|
||||
* Increase container height.
|
||||
* Integrate proper Land Advisor into the Land Station container to save space.
|
||||
|
||||
## Design
|
||||
### New Layout State
|
||||
- State: `layout: 'vertical' | 'horizontal'`
|
||||
- Toggle: A button group or switch to change layouts.
|
||||
|
||||
### Component Structure
|
||||
I will extract the core sections into render functions or variables to move them around easily.
|
||||
- `renderZoomSidebar()`
|
||||
- `renderPool()`
|
||||
- `renderDeck()`
|
||||
- `renderLandStation()` (This will now include the Land Advisor inside it)
|
||||
|
||||
### Horizontal Layout Grid
|
||||
Structure:
|
||||
```
|
||||
[Zoom Sidebar (Fixed Left)] | [Main Content (Flex Column)]
|
||||
|
|
||||
|-- [Top Row (Flex Row)]
|
||||
| |-- [Land Station (width fixed or flex)]
|
||||
| |-- [Pool (Flex 1)]
|
||||
|
|
||||
|-- [Bottom Row (Flex 1)]
|
||||
|-- [Deck]
|
||||
```
|
||||
|
||||
### Vertical Layout Grid (Current)
|
||||
Structure:
|
||||
```
|
||||
[Zoom Sidebar] | [Pool] | [Deck + Land Station]
|
||||
```
|
||||
*Note: In current layout, Land Station is stacked vertically with Deck in the 3rd column. User is fine with "exactly how is it now" for vertical.*
|
||||
|
||||
### Land Station Refactoring
|
||||
- Combine `Advice Panel` and `Land Station` div.
|
||||
- Remove `(Unlimited)`.
|
||||
- Increase height (e.g., `h-64` or `min-h-[200px]`).
|
||||
|
||||
## Implementation Steps
|
||||
1. Read `DeckBuilderView.tsx` (already read).
|
||||
2. Refactor to extract render helper functions for clear modularity.
|
||||
3. Add `layout` state and toggle UI.
|
||||
4. Implement CSS grids/flex layouts for both modes.
|
||||
5. Modify Land Station to include Advisor and styling updates.
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { socketService } from '../../services/SocketService';
|
||||
import { Save, Layers, Clock } from 'lucide-react';
|
||||
import { Save, Layers, Clock, Columns, LayoutTemplate } from 'lucide-react';
|
||||
|
||||
interface DeckBuilderViewProps {
|
||||
roomId: string;
|
||||
@@ -13,21 +12,12 @@ interface DeckBuilderViewProps {
|
||||
export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, availableBasicLands = [] }) => {
|
||||
// Unlimited Timer (Static for now)
|
||||
const [timer] = useState<string>("Unlimited");
|
||||
const [layout, setLayout] = useState<'vertical' | 'horizontal'>('vertical');
|
||||
const [pool, setPool] = useState<any[]>(initialPool);
|
||||
const [deck, setDeck] = useState<any[]>([]);
|
||||
const [lands, setLands] = useState({ Plains: 0, Island: 0, Swamp: 0, Mountain: 0, Forest: 0 });
|
||||
const [hoveredCard, setHoveredCard] = useState<any>(null);
|
||||
|
||||
/*
|
||||
// Disable timer countdown
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setTimer(t => t > 0 ? t - 1 : 0);
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
*/
|
||||
|
||||
// --- Land Advice Logic ---
|
||||
const landSuggestion = React.useMemo(() => {
|
||||
const targetLands = 17;
|
||||
@@ -56,8 +46,6 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
||||
|
||||
totalPips = Object.values(pips).reduce((a, b) => a + b, 0);
|
||||
|
||||
// If no colored pips (artifacts only?), suggest even split or just return 0s?
|
||||
// Let's assume proportional to 1 if 0 to avoid div by zero.
|
||||
if (totalPips === 0) return null;
|
||||
|
||||
// Distribute
|
||||
@@ -181,9 +169,190 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
||||
return [...(availableBasicLands || [])].sort((a, b) => a.name.localeCompare(b.name));
|
||||
}, [availableBasicLands]);
|
||||
|
||||
// --- Sub Actions ---
|
||||
const renderAdvisorContent = () => {
|
||||
if (!landSuggestion) return <span className="text-xs text-slate-500 italic">Add colored spells to get advice.</span>;
|
||||
|
||||
return (
|
||||
<div className="flex-1 w-full flex h-full bg-slate-900 text-white">
|
||||
{/* Column 1: Zoom Sidebar */}
|
||||
<div className="flex items-center gap-4 flex-wrap">
|
||||
<div className="flex gap-2">
|
||||
{(Object.entries(landSuggestion) as [string, number][]).map(([type, count]) => {
|
||||
if (count === 0) return null;
|
||||
let colorClass = "text-slate-300";
|
||||
if (type === 'Plains') colorClass = "text-amber-200";
|
||||
if (type === 'Island') colorClass = "text-blue-200";
|
||||
if (type === 'Swamp') colorClass = "text-purple-200";
|
||||
if (type === 'Mountain') colorClass = "text-red-200";
|
||||
if (type === 'Forest') colorClass = "text-emerald-200";
|
||||
|
||||
return (
|
||||
<div key={type} className={`font-bold ${colorClass} text-xs flex items-center gap-1`}>
|
||||
<span>{type.substring(0, 1)}:</span>
|
||||
<span>{count}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
onClick={applySuggestion}
|
||||
className="bg-emerald-700 hover:bg-emerald-600 text-white text-[10px] px-2 py-1 rounded shadow transition-colors font-bold uppercase tracking-wide"
|
||||
>
|
||||
Auto-Fill
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// --- Render Sections ---
|
||||
const renderLandStation = () => (
|
||||
<div className={`bg-slate-800 rounded-lg border border-slate-700 flex flex-col ${layout === 'horizontal' ? 'h-full' : 'h-72'} transition-all`}>
|
||||
<div className="p-3 border-b border-slate-700 flex flex-col gap-2 shrink-0 bg-slate-900/30">
|
||||
<div className="flex justify-between items-center">
|
||||
<h3 className="text-sm font-bold text-slate-400 uppercase">Land Station</h3>
|
||||
</div>
|
||||
|
||||
{/* Integrated Advisor */}
|
||||
<div className="bg-slate-950/50 rounded border border-white/5 p-2 flex flex-col gap-1">
|
||||
<span className="text-[10px] text-emerald-400 font-bold uppercase flex items-center gap-1">
|
||||
<Layers className="w-3 h-3" /> Land Advisor (Target: 17)
|
||||
</span>
|
||||
{renderAdvisorContent()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto custom-scrollbar p-2 bg-slate-900/50 rounded-b-lg">
|
||||
{availableBasicLands && availableBasicLands.length > 0 ? (
|
||||
<div className={`grid ${layout === 'horizontal' ? 'grid-cols-2' : 'grid-flow-col auto-cols-max'} gap-2 content-start`}>
|
||||
{/* Note: horizontal layout gets grid-cols-2 for vertical scrolling list feeling, vertical layout gets side-scrolling or wrapped */}
|
||||
<div className="flex flex-wrap gap-2 justify-center">
|
||||
{sortedLands.map((land) => (
|
||||
<div
|
||||
key={land.scryfallId}
|
||||
className="relative group cursor-pointer"
|
||||
onClick={() => addLandToDeck(land)}
|
||||
onMouseEnter={() => setHoveredCard(land)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
>
|
||||
<img
|
||||
src={land.image || land.image_uris?.normal}
|
||||
className="w-20 hover:scale-105 transition-transform rounded shadow-lg"
|
||||
alt={land.name}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 bg-black/40 rounded transition-opacity">
|
||||
<span className="text-white font-bold text-xs bg-black/50 px-1 rounded">+</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
// Fallback counter UI
|
||||
<div className="flex flex-col gap-2 p-2">
|
||||
{Object.keys(lands).map(type => (
|
||||
<div key={type} className="flex items-center justify-between bg-slate-800 p-2 rounded">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`w-6 h-6 rounded-full flex items-center justify-center font-bold text-xs border
|
||||
${type === 'Plains' ? 'bg-amber-900/50 border-amber-500 text-amber-200' : ''}
|
||||
${type === 'Island' ? 'bg-blue-900/50 border-blue-500 text-blue-200' : ''}
|
||||
${type === 'Swamp' ? 'bg-purple-900/50 border-purple-500 text-purple-200' : ''}
|
||||
${type === 'Mountain' ? 'bg-red-900/50 border-red-500 text-red-200' : ''}
|
||||
${type === 'Forest' ? 'bg-green-900/50 border-green-500 text-green-200' : ''}
|
||||
`}>
|
||||
{type[0]}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={() => handleLandChange(type, -1)} className="w-6 h-6 bg-slate-700 hover:bg-slate-600 rounded text-slate-300 font-bold">-</button>
|
||||
<span className="w-6 text-center text-sm font-bold">{lands[type as keyof typeof lands]}</span>
|
||||
<button onClick={() => handleLandChange(type, 1)} className="w-6 h-6 bg-slate-700 hover:bg-slate-600 rounded text-slate-300 font-bold">+</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderPool = () => (
|
||||
<>
|
||||
<div className="flex justify-between items-center mb-4 shrink-0">
|
||||
<h2 className="text-xl font-bold flex items-center gap-2"><Layers /> Card Pool ({pool.length})</h2>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-2 bg-slate-950/50 rounded-lg custom-scrollbar">
|
||||
<div className="flex flex-wrap gap-2 justify-center content-start">
|
||||
{pool.map((card) => (
|
||||
<img
|
||||
key={card.id}
|
||||
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
||||
className="w-28 hover:scale-110 transition-transform cursor-pointer rounded shadow-md"
|
||||
onClick={() => addToDeck(card)}
|
||||
onMouseEnter={() => setHoveredCard(card)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
title={card.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const renderDeck = () => (
|
||||
<>
|
||||
<div className="flex justify-between items-center mb-4 shrink-0">
|
||||
<h2 className="text-xl font-bold">Your Deck ({deck.length + Object.values(lands).reduce((a, b) => a + b, 0)})</h2>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-amber-400 font-mono text-xl font-bold bg-slate-800 px-3 py-1 rounded border border-amber-500/30">
|
||||
<Clock className="w-5 h-5" /> {formatTime(timer)}
|
||||
</div>
|
||||
<button
|
||||
onClick={submitDeck}
|
||||
className="bg-emerald-600 hover:bg-emerald-500 text-white px-6 py-2 rounded-lg font-bold shadow-lg flex items-center gap-2 transition-transform hover:scale-105"
|
||||
>
|
||||
<Save className="w-4 h-4" /> Submit Deck
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto p-2 bg-slate-950/50 rounded-lg custom-scrollbar">
|
||||
<div className="flex flex-wrap gap-2 justify-center content-start">
|
||||
{deck.map((card) => (
|
||||
<img
|
||||
key={card.id}
|
||||
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
||||
className="w-28 hover:scale-110 transition-transform cursor-pointer rounded shadow-md"
|
||||
onClick={() => removeFromDeck(card)}
|
||||
onMouseEnter={() => setHoveredCard(card)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
title={card.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex-1 w-full flex h-full bg-slate-900 text-white overflow-hidden relative">
|
||||
{/* View Switcher - Absolute Positioned */}
|
||||
<div className="absolute bottom-4 left-84 z-20 flex bg-slate-800/80 backdrop-blur rounded-lg p-1 border border-slate-700 shadow-xl gap-1" style={{ left: '330px' }}>
|
||||
<button
|
||||
onClick={() => setLayout('vertical')}
|
||||
className={`p-2 rounded flex items-center gap-2 text-xs font-bold transition-colors ${layout === 'vertical' ? 'bg-slate-600 text-white shadow' : 'text-slate-400 hover:text-white'}`}
|
||||
title="Cards Side-by-Side"
|
||||
>
|
||||
<Columns className="w-4 h-4" /> Vertical
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setLayout('horizontal')}
|
||||
className={`p-2 rounded flex items-center gap-2 text-xs font-bold transition-colors ${layout === 'horizontal' ? 'bg-slate-600 text-white shadow' : 'text-slate-400 hover:text-white'}`}
|
||||
title="Pool Above Deck"
|
||||
>
|
||||
<LayoutTemplate className="w-4 h-4" /> Horizontal
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Column 1: Zoom Sidebar (Always visible) */}
|
||||
<div className="hidden xl:flex w-80 shrink-0 flex-col items-center justify-start pt-8 border-r border-slate-800 bg-slate-950/50 z-10 p-4">
|
||||
{hoveredCard ? (
|
||||
<div className="animate-in fade-in slide-in-from-left-4 duration-200 sticky top-4 w-full">
|
||||
@@ -212,156 +381,41 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Column 2: Pool */}
|
||||
{/* Main Content Area */}
|
||||
{layout === 'vertical' ? (
|
||||
<>
|
||||
{/* Vertical: Column 2 (Pool) */}
|
||||
<div className="flex-1 p-4 flex flex-col border-r border-slate-700 min-w-0">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-xl font-bold flex items-center gap-2"><Layers /> Card Pool ({pool.length})</h2>
|
||||
{renderPool()}
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-2 bg-slate-950/50 rounded-lg custom-scrollbar">
|
||||
<div className="flex flex-wrap gap-2 justify-center content-start">
|
||||
{pool.map((card) => (
|
||||
<img
|
||||
key={card.id}
|
||||
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
||||
className="w-28 hover:scale-110 transition-transform cursor-pointer rounded shadow-md"
|
||||
onClick={() => addToDeck(card)}
|
||||
onMouseEnter={() => setHoveredCard(card)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
title={card.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Column 3: Deck & Lands */}
|
||||
{/* Vertical: Column 3 (Deck & Lands) */}
|
||||
<div className="flex-1 p-4 flex flex-col min-w-0">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-xl font-bold">Your Deck ({deck.length + Object.values(lands).reduce((a, b) => a + b, 0)})</h2>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-amber-400 font-mono text-xl font-bold bg-slate-800 px-3 py-1 rounded border border-amber-500/30">
|
||||
<Clock className="w-5 h-5" /> {formatTime(timer)}
|
||||
</div>
|
||||
<button
|
||||
onClick={submitDeck}
|
||||
className="bg-emerald-600 hover:bg-emerald-500 text-white px-6 py-2 rounded-lg font-bold shadow-lg flex items-center gap-2 transition-transform hover:scale-105"
|
||||
>
|
||||
<Save className="w-4 h-4" /> Submit Deck
|
||||
</button>
|
||||
{renderDeck()}
|
||||
<div className="mt-4">
|
||||
{renderLandStation()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Deck View */}
|
||||
<div className="flex-1 overflow-y-auto p-2 bg-slate-950/50 rounded-lg mb-4 custom-scrollbar">
|
||||
<div className="flex flex-wrap gap-2 justify-center content-start">
|
||||
{deck.map((card) => (
|
||||
<img
|
||||
key={card.id}
|
||||
src={card.image || card.image_uris?.normal || card.card_faces?.[0]?.image_uris?.normal}
|
||||
className="w-28 hover:scale-110 transition-transform cursor-pointer rounded shadow-md"
|
||||
onClick={() => removeFromDeck(card)}
|
||||
onMouseEnter={() => setHoveredCard(card)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
title={card.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
|
||||
{/* Advice Panel */}
|
||||
<div className="bg-slate-800 rounded-lg p-3 border border-slate-700 flex justify-between items-center">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs text-slate-400 font-bold uppercase flex items-center gap-2">
|
||||
<Layers className="w-3 h-3 text-emerald-400" /> Land Advisor (Target: 17)
|
||||
</span>
|
||||
<div className="text-xs text-slate-500 mt-1">
|
||||
Based on your deck's mana symbols.
|
||||
</div>
|
||||
</div>
|
||||
{landSuggestion ? (
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex gap-2">
|
||||
{(Object.entries(landSuggestion) as [string, number][]).map(([type, count]) => {
|
||||
if (count === 0) return null;
|
||||
let colorClass = "text-slate-300";
|
||||
if (type === 'Plains') colorClass = "text-amber-200";
|
||||
if (type === 'Island') colorClass = "text-blue-200";
|
||||
if (type === 'Swamp') colorClass = "text-purple-200";
|
||||
if (type === 'Mountain') colorClass = "text-red-200";
|
||||
if (type === 'Forest') colorClass = "text-emerald-200";
|
||||
|
||||
return (
|
||||
<div key={type} className={`font-bold ${colorClass} text-sm flex items-center gap-1`}>
|
||||
<span>{type.substring(0, 1)}:</span>
|
||||
<span>{count}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
onClick={applySuggestion}
|
||||
className="bg-emerald-700 hover:bg-emerald-600 text-white text-xs px-3 py-1 rounded shadow transition-colors font-bold"
|
||||
>
|
||||
Auto-Fill
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<span className="text-xs text-slate-500 italic">Add colored spells to get advice.</span>
|
||||
)}
|
||||
/* Horizontal Layout */
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
{/* Top Row: Lands + Pool */}
|
||||
<div className="flex-1 flex min-h-0 border-b border-slate-700">
|
||||
{/* Land Station (Left of Pool) */}
|
||||
<div className="w-[300px] p-4 border-r border-slate-700 flex flex-col">
|
||||
{renderLandStation()}
|
||||
</div>
|
||||
|
||||
{/* Land Station */}
|
||||
<div className="h-48 bg-slate-800 rounded-lg p-4 border border-slate-700 flex flex-col">
|
||||
<h3 className="text-sm font-bold text-slate-400 uppercase mb-2">Land Station (Unlimited)</h3>
|
||||
|
||||
{availableBasicLands && availableBasicLands.length > 0 ? (
|
||||
<div className="flex-1 overflow-x-auto flex items-center gap-3 custom-scrollbar p-2 bg-slate-900/50 rounded-lg">
|
||||
{sortedLands.map((land) => (
|
||||
<div
|
||||
key={land.scryfallId}
|
||||
className="flex-shrink-0 relative group cursor-pointer"
|
||||
onClick={() => addLandToDeck(land)}
|
||||
onMouseEnter={() => setHoveredCard(land)}
|
||||
onMouseLeave={() => setHoveredCard(null)}
|
||||
>
|
||||
<img
|
||||
src={land.image || land.image_uris?.normal}
|
||||
className="w-24 rounded shadow-lg group-hover:scale-110 transition-transform"
|
||||
alt={land.name}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 bg-black/40 rounded transition-opacity">
|
||||
<span className="text-white font-bold text-xs bg-black/50 px-2 py-1 rounded">+ Add</span>
|
||||
{/* Pool */}
|
||||
<div className="flex-1 p-4 flex flex-col min-w-0">
|
||||
{renderPool()}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Bottom Row: Deck */}
|
||||
<div className="h-[40%] p-4 flex flex-col bg-slate-900/50">
|
||||
{renderDeck()}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-around items-center h-full">
|
||||
{Object.keys(lands).map(type => (
|
||||
<div key={type} className="flex flex-col items-center gap-1">
|
||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center font-bold text-xs border-2
|
||||
${type === 'Plains' ? 'bg-amber-100 border-amber-300 text-amber-900' : ''}
|
||||
${type === 'Island' ? 'bg-blue-100 border-blue-300 text-blue-900' : ''}
|
||||
${type === 'Swamp' ? 'bg-purple-100 border-purple-300 text-purple-900' : ''}
|
||||
${type === 'Mountain' ? 'bg-red-100 border-red-300 text-red-900' : ''}
|
||||
${type === 'Forest' ? 'bg-green-100 border-green-300 text-green-900' : ''}
|
||||
`}>
|
||||
{type[0]}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={() => handleLandChange(type, -1)} className="w-8 h-8 bg-slate-700 rounded hover:bg-slate-600 flex items-center justify-center text-lg font-bold text-slate-300">-</button>
|
||||
<span className="w-8 text-center font-bold text-lg">{lands[type as keyof typeof lands]}</span>
|
||||
<button onClick={() => handleLandChange(type, 1)} className="w-8 h-8 bg-slate-700 rounded hover:bg-slate-600 flex items-center justify-center text-lg font-bold text-slate-300">+</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user