From bd33f6be243747001dbd14b75bb531806d084008 Mon Sep 17 00:00:00 2001 From: dnviti Date: Thu, 18 Dec 2025 02:21:18 +0100 Subject: [PATCH] feat: Persist DeckBuilder UI settings and library height to local storage, and fix sort dropdown positioning. --- .../src/modules/draft/DeckBuilderView.tsx | 152 ++++++++++++------ src/client/src/modules/draft/DraftView.tsx | 2 +- 2 files changed, 101 insertions(+), 53 deletions(-) diff --git a/src/client/src/modules/draft/DeckBuilderView.tsx b/src/client/src/modules/draft/DeckBuilderView.tsx index eabe227..3ec1c3a 100644 --- a/src/client/src/modules/draft/DeckBuilderView.tsx +++ b/src/client/src/modules/draft/DeckBuilderView.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useEffect } from 'react'; import { socketService } from '../../services/SocketService'; -import { Save, Layers, Clock, Columns, LayoutTemplate, List, LayoutGrid, ChevronDown, Check, GripVertical } from 'lucide-react'; +import { Save, Layers, Clock, Columns, LayoutTemplate, List, LayoutGrid, ChevronDown, Check } from 'lucide-react'; import { StackView } from '../../components/StackView'; import { FoilOverlay } from '../../components/CardPreview'; import { DraftCard } from '../../services/PackGeneratorService'; @@ -220,10 +220,22 @@ const CardsDisplay: React.FC<{ export const DeckBuilderView: React.FC = ({ initialPool, availableBasicLands = [] }) => { // Unlimited Timer (Static for now) const [timer] = useState("Unlimited"); - const [layout, setLayout] = useState<'vertical' | 'horizontal'>('vertical'); - 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 [groupBy, setGroupBy] = useState<'type' | 'color' | 'cmc' | 'rarity'>('color'); - const [cardWidth, setCardWidth] = useState(60); + const [layout, setLayout] = useState<'vertical' | 'horizontal'>(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem('deck_layout') : null; + return (saved as 'vertical' | 'horizontal') || 'vertical'; + }); + const [viewMode, setViewMode] = useState<'list' | 'grid' | 'stack'>(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem('deck_viewMode') : null; + return (saved as 'list' | 'grid' | 'stack') || 'stack'; + }); + const [groupBy, setGroupBy] = useState<'type' | 'color' | 'cmc' | 'rarity'>(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem('deck_groupBy') : null; + return (saved as 'type' | 'color' | 'cmc' | 'rarity') || 'color'; + }); + const [cardWidth, setCardWidth] = useState(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem('deck_cardWidth') : null; + return saved ? parseInt(saved, 10) : 60; + }); // Local state for smooth slider const [localCardWidth, setLocalCardWidth] = useState(cardWidth); const containerRef = React.useRef(null); @@ -240,28 +252,29 @@ export const DeckBuilderView: React.FC = ({ initialPool, a // --- Resize State --- const [sidebarWidth, setSidebarWidth] = useState(() => { - const saved = localStorage.getItem('deck_sidebarWidth'); + const saved = typeof window !== 'undefined' ? localStorage.getItem('deck_sidebarWidth') : null; return saved ? parseInt(saved, 10) : 320; }); - const [poolHeightPercent, setPoolHeightPercent] = useState(() => { - const saved = localStorage.getItem('deck_poolHeightPercent'); - return saved ? parseFloat(saved) : 60; + // We now control the Library (Bottom) height in pixels, matching DraftView consistency + const [libraryHeight, setLibraryHeight] = useState(() => { + const saved = typeof window !== 'undefined' ? localStorage.getItem('deck_libraryHeight') : null; + return saved ? parseInt(saved, 10) : 300; }); const sidebarRef = React.useRef(null); - const poolRef = React.useRef(null); + const libraryRef = React.useRef(null); const resizingState = React.useRef<{ startX: number, startY: number, startWidth: number, - startHeightPercent: number, - active: 'sidebar' | 'pool' | null - }>({ startX: 0, startY: 0, startWidth: 0, startHeightPercent: 0, active: null }); + startHeight: number, + active: 'sidebar' | 'library' | null + }>({ startX: 0, startY: 0, startWidth: 0, startHeight: 0, active: null }); // Initial visual set React.useEffect(() => { if (sidebarRef.current) sidebarRef.current.style.width = `${sidebarWidth}px`; - if (poolRef.current) poolRef.current.style.height = `${poolHeightPercent}%`; + if (libraryRef.current) libraryRef.current.style.height = `${libraryHeight}px`; }, []); // Persist Resize @@ -270,8 +283,14 @@ export const DeckBuilderView: React.FC = ({ initialPool, a }, [sidebarWidth]); useEffect(() => { - localStorage.setItem('deck_poolHeightPercent', poolHeightPercent.toString()); - }, [poolHeightPercent]); + localStorage.setItem('deck_libraryHeight', libraryHeight.toString()); + }, [libraryHeight]); + + // Persist Settings + useEffect(() => localStorage.setItem('deck_layout', layout), [layout]); + useEffect(() => localStorage.setItem('deck_viewMode', viewMode), [viewMode]); + useEffect(() => localStorage.setItem('deck_groupBy', groupBy), [groupBy]); + useEffect(() => localStorage.setItem('deck_cardWidth', cardWidth.toString()), [cardWidth]); const [pool, setPool] = useState(initialPool); const [deck, setDeck] = useState([]); @@ -451,22 +470,18 @@ export const DeckBuilderView: React.FC = ({ initialPool, a // --- Resize Handlers --- // --- Resize Handlers --- - const handleResizeStart = (type: 'sidebar' | 'pool', e: React.MouseEvent | React.TouchEvent) => { + const handleResizeStart = (type: 'sidebar' | 'library', e: React.MouseEvent | React.TouchEvent) => { // Prevent default to avoid scrolling/selection if (e.cancelable) e.preventDefault(); const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; - const containerTop = 56; - const containerHeight = window.innerHeight - containerTop; - const currentPoolHeight = poolRef.current?.getBoundingClientRect().height || (containerHeight * 0.6); - resizingState.current = { startX: clientX, startY: clientY, startWidth: sidebarRef.current?.getBoundingClientRect().width || 320, - startHeightPercent: poolHeightPercent, + startHeight: libraryRef.current?.getBoundingClientRect().height || 300, active: type }; @@ -492,13 +507,11 @@ export const DeckBuilderView: React.FC = ({ initialPool, a sidebarRef.current.style.width = `${newWidth}px`; } - if (resizingState.current.active === 'pool' && poolRef.current) { - const containerTop = 56; - const containerHeight = window.innerHeight - containerTop; - const relativeY = clientY - containerTop; - const percentage = (relativeY / containerHeight) * 100; - const clamped = Math.max(20, Math.min(80, percentage)); - poolRef.current.style.height = `${clamped}%`; + if (resizingState.current.active === 'library' && libraryRef.current) { + // Dragging UP increases height of bottom panel + const delta = resizingState.current.startY - clientY; + const newHeight = Math.max(100, Math.min(window.innerHeight * 0.8, resizingState.current.startHeight + delta)); + libraryRef.current.style.height = `${newHeight}px`; } }); }, []); @@ -507,11 +520,8 @@ export const DeckBuilderView: React.FC = ({ initialPool, a if (resizingState.current.active === 'sidebar' && sidebarRef.current) { setSidebarWidth(parseInt(sidebarRef.current.style.width)); } - if (resizingState.current.active === 'pool' && poolRef.current) { - const hStyle = poolRef.current.style.height; - if (hStyle.includes('%')) { - setPoolHeightPercent(parseFloat(hStyle)); - } + if (resizingState.current.active === 'library' && libraryRef.current) { + setLibraryHeight(parseInt(libraryRef.current.style.height)); } resizingState.current.active = null; @@ -609,7 +619,14 @@ export const DeckBuilderView: React.FC = ({ initialPool, a {viewMode === 'stack' && (
))}
@@ -794,11 +837,10 @@ export const DeckBuilderView: React.FC = ({ initialPool, a ) : (
{/* Top: Pool + Land Station */} - {/* Top: Pool + Land Station */} -
+
Card Pool ({pool.length}) @@ -808,20 +850,26 @@ export const DeckBuilderView: React.FC = ({ initialPool, a
+
- {/* Resizer Handle */} -
handleResizeStart('pool', e)} - onTouchStart={(e) => handleResizeStart('pool', e)} - > -
-
+ {/* Resizer Handle */} +
handleResizeStart('library', e)} + onTouchStart={(e) => handleResizeStart('library', e)} + > +
+
- {/* Bottom: Deck */} + {/* Bottom: Library */} +
Library ({deck.length}) diff --git a/src/client/src/modules/draft/DraftView.tsx b/src/client/src/modules/draft/DraftView.tsx index 5f02d03..7a5b00b 100644 --- a/src/client/src/modules/draft/DraftView.tsx +++ b/src/client/src/modules/draft/DraftView.tsx @@ -534,7 +534,7 @@ export const DraftView: React.FC = ({ draftState, currentPlayerI ); }; -const DraftCardItem = ({ rawCard, cardScale, handlePick, setHoveredCard }: any) => { +const DraftCardItem = ({ rawCard, handlePick, setHoveredCard }: any) => { const card = normalizeCard(rawCard); const isFoil = card.finish === 'foil'; const { onTouchStart, onTouchEnd, onTouchMove, onClick } = useCardTouch(setHoveredCard, () => {