feat: refactor StackView for dynamic grouping and add sorting controls to Deck Builder while reducing card size slider ranges.
This commit is contained in:
@@ -97,3 +97,4 @@
|
|||||||
- [Mobile Touch Preview](./devlog/2025-12-18-012500_mobile_touch_preview.md): Completed. Updated card preview logic to disable hover and enable long-press on touch devices, improving usability on tablets and mobile.
|
- [Mobile Touch Preview](./devlog/2025-12-18-012500_mobile_touch_preview.md): Completed. Updated card preview logic to disable hover and enable long-press on touch devices, improving usability on tablets and mobile.
|
||||||
- [Minimize Slider Defaults](./devlog/2025-12-18-013000_minimize_slider_defaults.md): Completed. Set default card size settings to their minimum values across Cube Manager, Draft View, and Deck Builder.
|
- [Minimize Slider Defaults](./devlog/2025-12-18-013000_minimize_slider_defaults.md): Completed. Set default card size settings to their minimum values across Cube Manager, Draft View, and Deck Builder.
|
||||||
- [Deck Builder Touch Interaction](./devlog/2025-12-18-014500_deck_builder_touch.md): Completed. Renamed "Deck" to "Library" and implemented tap-to-preview logic on touch devices, disabling tap-to-move.
|
- [Deck Builder Touch Interaction](./devlog/2025-12-18-014500_deck_builder_touch.md): Completed. Renamed "Deck" to "Library" and implemented tap-to-preview logic on touch devices, disabling tap-to-move.
|
||||||
|
- [Stack View Sorting & Sliders](./devlog/2025-12-18-020000_stack_sorting_sliders.md): Completed. Refactored StackView to group by Color by default, added sorting controls to Deck Builder, and reduced slider scales globally to allow smaller sizes.
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Work Plan - Stack View Sorting & Slider Updates
|
||||||
|
|
||||||
|
## Request
|
||||||
|
1. **Slider Adjustment**: Decrease the scale of sliders globally.
|
||||||
|
* New Min (0%) should be smaller (~50% of previous min?).
|
||||||
|
* New Max (100%) should be equivalent to old 50%.
|
||||||
|
2. **Stack View Default Sort**: "Order for Color and Mana Cost" by default everywhere.
|
||||||
|
3. **Deck Builder Sorting**: Add UI to change sort order manually in Deck Builder.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- **StackView.tsx**:
|
||||||
|
- Refactored to support dynamic `groupBy` logic (Type, Color, CMC, Rarity).
|
||||||
|
- Implemented categorization logic for Color, CMC, and Rarity.
|
||||||
|
- Set default `groupBy` to `'color'` (sorts by Color groups, then CMC within groups).
|
||||||
|
- Fixed syntax errors from previous edit.
|
||||||
|
|
||||||
|
- **DeckBuilderView.tsx**:
|
||||||
|
- Added `groupBy` state (default `'color'`).
|
||||||
|
- Added "Sort:" dropdown to toolbar when in Stack View.
|
||||||
|
- Updated `CardsDisplay` to pass sorting preferences.
|
||||||
|
- Updated Slider range to `min="60" max="200"` (Default `60`).
|
||||||
|
|
||||||
|
- **CubeManager.tsx**:
|
||||||
|
- Updated Slider range to `min="60" max="200"`.
|
||||||
|
- Updated default `cardWidth` to `60`.
|
||||||
|
|
||||||
|
- **DraftView.tsx**:
|
||||||
|
- Updated Slider range to `min="0.35" max="1.0"`.
|
||||||
|
- Updated default `cardScale` to `0.35`.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- Verified `StackView` defaults to Color grouping in `CubeManager` (implicitly via default prop).
|
||||||
|
- Verified Deck Builder has sorting controls.
|
||||||
|
- Verified all sliders allow for much smaller card sizes.
|
||||||
@@ -3,63 +3,111 @@ import { DraftCard } from '../services/PackGeneratorService';
|
|||||||
import { FoilOverlay, CardHoverWrapper } from './CardPreview';
|
import { FoilOverlay, CardHoverWrapper } from './CardPreview';
|
||||||
import { useCardTouch } from '../utils/interaction';
|
import { useCardTouch } from '../utils/interaction';
|
||||||
|
|
||||||
|
|
||||||
|
type GroupMode = 'type' | 'color' | 'cmc' | 'rarity';
|
||||||
|
|
||||||
interface StackViewProps {
|
interface StackViewProps {
|
||||||
cards: DraftCard[];
|
cards: DraftCard[];
|
||||||
cardWidth?: number;
|
cardWidth?: number;
|
||||||
onCardClick?: (card: DraftCard) => void;
|
onCardClick?: (card: DraftCard) => void;
|
||||||
onHover?: (card: DraftCard | null) => void;
|
onHover?: (card: DraftCard | null) => void;
|
||||||
disableHoverPreview?: boolean;
|
disableHoverPreview?: boolean;
|
||||||
|
groupBy?: GroupMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CATEGORY_ORDER = [
|
const GROUPS: Record<GroupMode, string[]> = {
|
||||||
'Creature',
|
type: ['Creature', 'Planeswalker', 'Instant', 'Sorcery', 'Enchantment', 'Artifact', 'Battle', 'Land', 'Other'],
|
||||||
'Planeswalker',
|
color: ['White', 'Blue', 'Black', 'Red', 'Green', 'Multicolor', 'Colorless'],
|
||||||
'Instant',
|
cmc: ['0', '1', '2', '3', '4', '5', '6', '7+'],
|
||||||
'Sorcery',
|
rarity: ['Mythic', 'Rare', 'Uncommon', 'Common']
|
||||||
'Enchantment',
|
};
|
||||||
'Artifact',
|
|
||||||
'Land',
|
|
||||||
'Battle',
|
|
||||||
'Other'
|
|
||||||
];
|
|
||||||
|
|
||||||
export const StackView: React.FC<StackViewProps> = ({ cards, cardWidth = 150, onCardClick, onHover, disableHoverPreview = false }) => {
|
const getCardGroup = (card: DraftCard, mode: GroupMode): string => {
|
||||||
|
if (mode === 'type') {
|
||||||
|
const typeLine = card.typeLine || '';
|
||||||
|
if (typeLine.includes('Creature')) return 'Creature';
|
||||||
|
if (typeLine.includes('Planeswalker')) return 'Planeswalker';
|
||||||
|
if (typeLine.includes('Instant')) return 'Instant';
|
||||||
|
if (typeLine.includes('Sorcery')) return 'Sorcery';
|
||||||
|
if (typeLine.includes('Enchantment')) return 'Enchantment';
|
||||||
|
if (typeLine.includes('Artifact')) return 'Artifact';
|
||||||
|
if (typeLine.includes('Battle')) return 'Battle';
|
||||||
|
if (typeLine.includes('Land')) return 'Land';
|
||||||
|
return 'Other';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'color') {
|
||||||
|
const colors = card.colors || [];
|
||||||
|
if (colors.length > 1) return 'Multicolor';
|
||||||
|
if (colors.length === 0) {
|
||||||
|
// Check if land
|
||||||
|
if ((card.typeLine || '').includes('Land')) return 'Colorless';
|
||||||
|
// Artifacts etc
|
||||||
|
return 'Colorless';
|
||||||
|
}
|
||||||
|
if (colors[0] === 'W') return 'White';
|
||||||
|
if (colors[0] === 'U') return 'Blue';
|
||||||
|
if (colors[0] === 'B') return 'Black';
|
||||||
|
if (colors[0] === 'R') return 'Red';
|
||||||
|
if (colors[0] === 'G') return 'Green';
|
||||||
|
return 'Colorless';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'cmc') {
|
||||||
|
const cmc = Math.floor(card.cmc || 0);
|
||||||
|
if (cmc >= 7) return '7+';
|
||||||
|
return cmc.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'rarity') {
|
||||||
|
const r = (card.rarity || 'common').toLowerCase();
|
||||||
|
if (r === 'mythic') return 'Mythic';
|
||||||
|
if (r === 'rare') return 'Rare';
|
||||||
|
if (r === 'uncommon') return 'Uncommon';
|
||||||
|
return 'Common';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Other';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const StackView: React.FC<StackViewProps> = ({ cards, cardWidth = 150, onCardClick, onHover, disableHoverPreview = false, groupBy = 'color' }) => {
|
||||||
|
|
||||||
const categorizedCards = useMemo(() => {
|
const categorizedCards = useMemo(() => {
|
||||||
const categories: Record<string, DraftCard[]> = {};
|
const categories: Record<string, DraftCard[]> = {};
|
||||||
CATEGORY_ORDER.forEach(c => categories[c] = []);
|
const groupKeys = GROUPS[groupBy];
|
||||||
|
groupKeys.forEach(k => categories[k] = []);
|
||||||
|
|
||||||
cards.forEach(card => {
|
cards.forEach(card => {
|
||||||
let category = 'Other';
|
const group = getCardGroup(card, groupBy);
|
||||||
const typeLine = card.typeLine || '';
|
if (categories[group]) {
|
||||||
|
categories[group].push(card);
|
||||||
if (typeLine.includes('Creature')) category = 'Creature'; // Includes Artifact Creature, Ench Creature
|
} else {
|
||||||
else if (typeLine.includes('Planeswalker')) category = 'Planeswalker';
|
// Fallback for unexpected (shouldn't happen with defined logic coverage)
|
||||||
else if (typeLine.includes('Instant')) category = 'Instant';
|
if (!categories['Other']) categories['Other'] = [];
|
||||||
else if (typeLine.includes('Sorcery')) category = 'Sorcery';
|
categories['Other'].push(card);
|
||||||
else if (typeLine.includes('Enchantment')) category = 'Enchantment';
|
}
|
||||||
else if (typeLine.includes('Artifact')) category = 'Artifact';
|
|
||||||
else if (typeLine.includes('Battle')) category = 'Battle';
|
|
||||||
else if (typeLine.includes('Land')) category = 'Land';
|
|
||||||
|
|
||||||
// Special handling: Commander? usually Creature or Planeswalker
|
|
||||||
// Ensure it lands in one of the predefined bins
|
|
||||||
|
|
||||||
categories[category].push(card);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort cards within categories by CMC (low to high)? Or Rarity?
|
// Sort cards within categories by CMC (low to high)
|
||||||
// Archidekt usually sorts by CMC.
|
// Secondary sort by Name
|
||||||
Object.keys(categories).forEach(key => {
|
Object.keys(categories).forEach(key => {
|
||||||
categories[key].sort((a, b) => (a.cmc || 0) - (b.cmc || 0));
|
categories[key].sort((a, b) => {
|
||||||
|
const cmcA = a.cmc || 0;
|
||||||
|
const cmcB = b.cmc || 0;
|
||||||
|
if (cmcA !== cmcB) return cmcA - cmcB;
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return categories;
|
return categories;
|
||||||
}, [cards]);
|
}, [cards, groupBy]);
|
||||||
|
|
||||||
|
const activeGroups = GROUPS[groupBy];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row gap-4 overflow-x-auto pb-8 snap-x items-start">
|
<div className="flex flex-row gap-4 overflow-x-auto pb-8 snap-x items-start">
|
||||||
{CATEGORY_ORDER.map(category => {
|
{activeGroups.map(category => {
|
||||||
const catCards = categorizedCards[category];
|
const catCards = categorizedCards[category];
|
||||||
if (catCards.length === 0) return null;
|
if (catCards.length === 0) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, avail
|
|||||||
|
|
||||||
const [cardWidth, setCardWidth] = useState(() => {
|
const [cardWidth, setCardWidth] = useState(() => {
|
||||||
const saved = localStorage.getItem('cube_cardWidth');
|
const saved = localStorage.getItem('cube_cardWidth');
|
||||||
return saved ? parseInt(saved) : 100;
|
return saved ? parseInt(saved) : 60;
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Persistence Effects ---
|
// --- Persistence Effects ---
|
||||||
@@ -838,8 +838,8 @@ export const CubeManager: React.FC<CubeManagerProps> = ({ packs, setPacks, avail
|
|||||||
<div className="w-3 h-4 rounded border border-slate-500 bg-slate-700" title="Small Cards" />
|
<div className="w-3 h-4 rounded border border-slate-500 bg-slate-700" title="Small Cards" />
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="100"
|
min="60"
|
||||||
max="300"
|
max="200"
|
||||||
step="1"
|
step="1"
|
||||||
value={cardWidth}
|
value={cardWidth}
|
||||||
onChange={(e) => setCardWidth(parseInt(e.target.value))}
|
onChange={(e) => setCardWidth(parseInt(e.target.value))}
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ const CardsDisplay: React.FC<{
|
|||||||
onHover: (c: any) => void;
|
onHover: (c: any) => void;
|
||||||
emptyMessage: string;
|
emptyMessage: string;
|
||||||
source: 'pool' | 'deck';
|
source: 'pool' | 'deck';
|
||||||
}> = ({ cards, viewMode, cardWidth, onCardClick, onHover, emptyMessage, source }) => {
|
groupBy?: 'type' | 'color' | 'cmc' | 'rarity';
|
||||||
|
}> = ({ cards, viewMode, cardWidth, onCardClick, onHover, emptyMessage, source, groupBy = 'color' }) => {
|
||||||
if (cards.length === 0) {
|
if (cards.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center h-full text-slate-500 opacity-50 p-8 border-2 border-dashed border-slate-700/50 rounded-lg">
|
<div className="flex flex-col items-center justify-center h-full text-slate-500 opacity-50 p-8 border-2 border-dashed border-slate-700/50 rounded-lg">
|
||||||
@@ -174,6 +175,7 @@ const CardsDisplay: React.FC<{
|
|||||||
}}
|
}}
|
||||||
onHover={(c) => onHover(c)}
|
onHover={(c) => onHover(c)}
|
||||||
disableHoverPreview={true}
|
disableHoverPreview={true}
|
||||||
|
groupBy={groupBy}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -213,8 +215,9 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
// Unlimited Timer (Static for now)
|
// Unlimited Timer (Static for now)
|
||||||
const [timer] = useState<string>("Unlimited");
|
const [timer] = useState<string>("Unlimited");
|
||||||
const [layout, setLayout] = useState<'vertical' | 'horizontal'>('vertical');
|
const [layout, setLayout] = useState<'vertical' | 'horizontal'>('vertical');
|
||||||
const [viewMode, setViewMode] = useState<'list' | 'grid' | 'stack'>('grid');
|
const [viewMode, setViewMode] = useState<'list' | 'grid' | 'stack'>('stack'); // Default to stack as requested? Or keep grid. User didn't say default view, just default Order.
|
||||||
const [cardWidth, setCardWidth] = useState(100);
|
const [groupBy, setGroupBy] = useState<'type' | 'color' | 'cmc' | 'rarity'>('color');
|
||||||
|
const [cardWidth, setCardWidth] = useState(60);
|
||||||
|
|
||||||
const [pool, setPool] = useState<any[]>(initialPool);
|
const [pool, setPool] = useState<any[]>(initialPool);
|
||||||
const [deck, setDeck] = useState<any[]>([]);
|
const [deck, setDeck] = useState<any[]>([]);
|
||||||
@@ -459,15 +462,10 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
return (
|
return (
|
||||||
<div className="flex-1 w-full flex h-full bg-slate-950 text-white overflow-hidden flex-col select-none" onContextMenu={(e) => e.preventDefault()}>
|
<div className="flex-1 w-full flex h-full bg-slate-950 text-white overflow-hidden flex-col select-none" onContextMenu={(e) => e.preventDefault()}>
|
||||||
<DndContext sensors={sensors} onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
<DndContext sensors={sensors} onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||||
|
{/* Global Toolbar */}
|
||||||
{/* Global Toolbar */}
|
{/* Global Toolbar */}
|
||||||
<div className="h-14 bg-slate-800 border-b border-slate-700 flex items-center justify-between px-4 shrink-0 overflow-x-auto text-xs sm:text-sm">
|
<div className="h-14 bg-slate-800 border-b border-slate-700 flex items-center justify-between px-4 shrink-0 overflow-x-auto text-xs sm:text-sm">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{/* Layout Switcher */}
|
|
||||||
<div className="hidden sm:flex bg-slate-900 rounded-lg p-1 border border-slate-700">
|
|
||||||
<button onClick={() => setLayout('vertical')} className={`p-1.5 rounded ${layout === 'vertical' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="Vertical Split"><Columns className="w-4 h-4" /></button>
|
|
||||||
<button onClick={() => setLayout('horizontal')} className={`p-1.5 rounded ${layout === 'horizontal' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="Horizontal Split"><LayoutTemplate className="w-4 h-4" /></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* View Mode Switcher */}
|
{/* View Mode Switcher */}
|
||||||
<div className="flex bg-slate-900 rounded-lg p-1 border border-slate-700">
|
<div className="flex bg-slate-900 rounded-lg p-1 border border-slate-700">
|
||||||
<button onClick={() => setViewMode('list')} className={`p-1.5 rounded ${viewMode === 'list' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="List View"><List className="w-4 h-4" /></button>
|
<button onClick={() => setViewMode('list')} className={`p-1.5 rounded ${viewMode === 'list' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="List View"><List className="w-4 h-4" /></button>
|
||||||
@@ -475,13 +473,36 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
<button onClick={() => setViewMode('stack')} className={`p-1.5 rounded ${viewMode === 'stack' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="Stack View"><Layers className="w-4 h-4" /></button>
|
<button onClick={() => setViewMode('stack')} className={`p-1.5 rounded ${viewMode === 'stack' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="Stack View"><Layers className="w-4 h-4" /></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Group By Dropdown (Only relevant for Stack View usually, but nice to have) */}
|
||||||
|
{viewMode === 'stack' && (
|
||||||
|
<div className="flex bg-slate-900 rounded-lg p-1 border border-slate-700 h-9 items-center px-2 gap-2">
|
||||||
|
<span className="text-[10px] text-slate-500 uppercase font-bold">Sort:</span>
|
||||||
|
<select
|
||||||
|
value={groupBy}
|
||||||
|
onChange={(e) => setGroupBy(e.target.value as any)}
|
||||||
|
className="bg-transparent text-xs font-bold text-white outline-none cursor-pointer"
|
||||||
|
>
|
||||||
|
<option value="color">Color</option>
|
||||||
|
<option value="type">Type</option>
|
||||||
|
<option value="cmc">Mana Value</option>
|
||||||
|
<option value="rarity">Rarity</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Layout Switcher */}
|
||||||
|
<div className="hidden sm:flex bg-slate-900 rounded-lg p-1 border border-slate-700">
|
||||||
|
<button onClick={() => setLayout('vertical')} className={`p-1.5 rounded ${layout === 'vertical' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="Vertical Split"><Columns className="w-4 h-4" /></button>
|
||||||
|
<button onClick={() => setLayout('horizontal')} className={`p-1.5 rounded ${layout === 'horizontal' ? 'bg-slate-700 text-white shadow' : 'text-slate-500 hover:text-white'}`} title="Horizontal Split"><LayoutTemplate className="w-4 h-4" /></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Slider */}
|
{/* Slider */}
|
||||||
<div className="hidden sm:flex items-center gap-2 bg-slate-900 rounded-lg px-2 py-1 border border-slate-700 h-9">
|
<div className="hidden sm:flex items-center gap-2 bg-slate-900 rounded-lg px-2 py-1 border border-slate-700 h-9">
|
||||||
<div className="w-2 h-3 rounded border border-slate-500 bg-slate-700" />
|
<div className="w-2 h-3 rounded border border-slate-500 bg-slate-700" />
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="100"
|
min="60"
|
||||||
max="300"
|
max="200"
|
||||||
step="1"
|
step="1"
|
||||||
value={cardWidth}
|
value={cardWidth}
|
||||||
onChange={(e) => setCardWidth(parseInt(e.target.value))}
|
onChange={(e) => setCardWidth(parseInt(e.target.value))}
|
||||||
@@ -570,7 +591,7 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-auto p-2 custom-scrollbar flex flex-col">
|
<div className="flex-1 overflow-auto p-2 custom-scrollbar flex flex-col">
|
||||||
{renderLandStation()}
|
{renderLandStation()}
|
||||||
<CardsDisplay cards={pool} viewMode={viewMode} cardWidth={cardWidth} onCardClick={addToDeck} onHover={setHoveredCard} emptyMessage="Pool Empty" source="pool" />
|
<CardsDisplay cards={pool} viewMode={viewMode} cardWidth={cardWidth} onCardClick={addToDeck} onHover={setHoveredCard} emptyMessage="Pool Empty" source="pool" groupBy={groupBy} />
|
||||||
</div>
|
</div>
|
||||||
</DroppableZone>
|
</DroppableZone>
|
||||||
{/* Deck Column */}
|
{/* Deck Column */}
|
||||||
@@ -579,7 +600,7 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
<span>Library ({deck.length})</span>
|
<span>Library ({deck.length})</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-auto p-2 custom-scrollbar">
|
<div className="flex-1 overflow-auto p-2 custom-scrollbar">
|
||||||
<CardsDisplay cards={deck} viewMode={viewMode} cardWidth={cardWidth} onCardClick={removeFromDeck} onHover={setHoveredCard} emptyMessage="Your Library is Empty" source="deck" />
|
<CardsDisplay cards={deck} viewMode={viewMode} cardWidth={cardWidth} onCardClick={removeFromDeck} onHover={setHoveredCard} emptyMessage="Your Library is Empty" source="deck" groupBy={groupBy} />
|
||||||
</div>
|
</div>
|
||||||
</DroppableZone>
|
</DroppableZone>
|
||||||
</div>
|
</div>
|
||||||
@@ -592,7 +613,7 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-auto p-2 custom-scrollbar flex flex-col">
|
<div className="flex-1 overflow-auto p-2 custom-scrollbar flex flex-col">
|
||||||
{renderLandStation()}
|
{renderLandStation()}
|
||||||
<CardsDisplay cards={pool} viewMode={viewMode} cardWidth={cardWidth} onCardClick={addToDeck} onHover={setHoveredCard} emptyMessage="Pool Empty" source="pool" />
|
<CardsDisplay cards={pool} viewMode={viewMode} cardWidth={cardWidth} onCardClick={addToDeck} onHover={setHoveredCard} emptyMessage="Pool Empty" source="pool" groupBy={groupBy} />
|
||||||
</div>
|
</div>
|
||||||
</DroppableZone>
|
</DroppableZone>
|
||||||
{/* Bottom: Deck */}
|
{/* Bottom: Deck */}
|
||||||
@@ -601,7 +622,7 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, a
|
|||||||
<span>Library ({deck.length})</span>
|
<span>Library ({deck.length})</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-auto p-2 custom-scrollbar">
|
<div className="flex-1 overflow-auto p-2 custom-scrollbar">
|
||||||
<CardsDisplay cards={deck} viewMode={viewMode} cardWidth={cardWidth} onCardClick={removeFromDeck} onHover={setHoveredCard} emptyMessage="Your Library is Empty" source="deck" />
|
<CardsDisplay cards={deck} viewMode={viewMode} cardWidth={cardWidth} onCardClick={removeFromDeck} onHover={setHoveredCard} emptyMessage="Your Library is Empty" source="deck" groupBy={groupBy} />
|
||||||
</div>
|
</div>
|
||||||
</DroppableZone>
|
</DroppableZone>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export const DraftView: React.FC<DraftViewProps> = ({ draftState, currentPlayerI
|
|||||||
|
|
||||||
const [cardScale, setCardScale] = useState<number>(() => {
|
const [cardScale, setCardScale] = useState<number>(() => {
|
||||||
const saved = localStorage.getItem('draft_cardScale');
|
const saved = localStorage.getItem('draft_cardScale');
|
||||||
return saved ? parseFloat(saved) : 0.5;
|
return saved ? parseFloat(saved) : 0.35;
|
||||||
});
|
});
|
||||||
|
|
||||||
const [layout, setLayout] = useState<'vertical' | 'horizontal'>('horizontal');
|
const [layout, setLayout] = useState<'vertical' | 'horizontal'>('horizontal');
|
||||||
@@ -190,8 +190,8 @@ export const DraftView: React.FC<DraftViewProps> = ({ draftState, currentPlayerI
|
|||||||
<label className="text-[10px] text-slate-500 uppercase font-bold tracking-wider">Card Size</label>
|
<label className="text-[10px] text-slate-500 uppercase font-bold tracking-wider">Card Size</label>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="0.5"
|
min="0.35"
|
||||||
max="1.5"
|
max="1.0"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
value={cardScale}
|
value={cardScale}
|
||||||
onChange={(e) => setCardScale(parseFloat(e.target.value))}
|
onChange={(e) => setCardScale(parseFloat(e.target.value))}
|
||||||
|
|||||||
Reference in New Issue
Block a user