- Added AvailableParticipantsSection for participant search and filtering. - Created DraftGridSection for displaying the draft grid with team timers and context menu. - Developed QueueSection for managing participant queue and autodraft settings. - Introduced RecentPicksSection to show recent draft picks. - Implemented TeamsDraftedGrid to visualize drafted participants by team and sport. - Added collapsible UI components for better layout management. - Established tabbed navigation for draft board, recent picks, and teams drafted. - Enhanced responsiveness for mobile and desktop views. - Updated styles for a cohesive design across components.
173 lines
5.3 KiB
TypeScript
173 lines
5.3 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import type { ReactNode } from "react";
|
|
import { ChevronLeft, ChevronRight, ChevronDown, ChevronUp } from "lucide-react";
|
|
import { Button } from "~/components/ui/button";
|
|
import { cn } from "~/lib/utils";
|
|
|
|
interface DraftSidebarProps {
|
|
collapsed: boolean;
|
|
onCollapsedChange: (collapsed: boolean) => void;
|
|
queueSection: ReactNode;
|
|
participantsSection: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function DraftSidebar({
|
|
collapsed,
|
|
onCollapsedChange,
|
|
queueSection,
|
|
participantsSection,
|
|
className,
|
|
}: DraftSidebarProps) {
|
|
// Track which sections are expanded
|
|
// At least one must be expanded at all times
|
|
const [queueExpanded, setQueueExpanded] = useState(() => {
|
|
if (typeof window === "undefined") return true;
|
|
const stored = localStorage.getItem("draftSidebarQueueExpanded");
|
|
return stored !== null ? JSON.parse(stored) : true;
|
|
});
|
|
|
|
const [participantsExpanded, setParticipantsExpanded] = useState(() => {
|
|
if (typeof window === "undefined") return true;
|
|
const stored = localStorage.getItem("draftSidebarParticipantsExpanded");
|
|
return stored !== null ? JSON.parse(stored) : true;
|
|
});
|
|
|
|
// Persist section states to localStorage
|
|
useEffect(() => {
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem(
|
|
"draftSidebarQueueExpanded",
|
|
JSON.stringify(queueExpanded)
|
|
);
|
|
}
|
|
}, [queueExpanded]);
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem(
|
|
"draftSidebarParticipantsExpanded",
|
|
JSON.stringify(participantsExpanded)
|
|
);
|
|
}
|
|
}, [participantsExpanded]);
|
|
|
|
const toggleQueueSection = () => {
|
|
// Prevent collapsing if it's the only expanded section
|
|
if (queueExpanded && !participantsExpanded) {
|
|
return;
|
|
}
|
|
setQueueExpanded(!queueExpanded);
|
|
};
|
|
|
|
const toggleParticipantsSection = () => {
|
|
// Prevent collapsing if it's the only expanded section
|
|
if (participantsExpanded && !queueExpanded) {
|
|
return;
|
|
}
|
|
setParticipantsExpanded(!participantsExpanded);
|
|
};
|
|
|
|
// Calculate flex sizes for sections
|
|
const queueFlexSize = queueExpanded && participantsExpanded ? "1" : queueExpanded ? "1" : "0";
|
|
const participantsFlexSize = queueExpanded && participantsExpanded ? "1" : participantsExpanded ? "1" : "0";
|
|
|
|
if (collapsed) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative flex-shrink-0 bg-card border-r border-border transition-all duration-300",
|
|
"w-12 hidden lg:block", // Hide completely on mobile when collapsed
|
|
className
|
|
)}
|
|
>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => onCollapsedChange(false)}
|
|
className="absolute top-4 left-1/2 -translate-x-1/2"
|
|
aria-label="Expand sidebar"
|
|
>
|
|
<ChevronRight className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{/* Mobile backdrop */}
|
|
<div
|
|
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
|
|
onClick={() => onCollapsedChange(true)}
|
|
aria-label="Close sidebar"
|
|
/>
|
|
|
|
<div
|
|
className={cn(
|
|
"relative flex-shrink-0 bg-card border-r border-border transition-all duration-300 flex flex-col",
|
|
"w-[350px]",
|
|
// Mobile: fixed overlay, Desktop: normal sidebar
|
|
"fixed inset-y-0 left-0 z-50 lg:relative lg:z-auto",
|
|
className
|
|
)}
|
|
>
|
|
{/* Collapse button */}
|
|
<div className="absolute top-4 right-2 z-20">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => onCollapsedChange(true)}
|
|
aria-label="Collapse sidebar"
|
|
>
|
|
<ChevronLeft className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Queue Section */}
|
|
<div
|
|
className="flex flex-col border-b border-border overflow-hidden"
|
|
style={{ flex: queueFlexSize }}
|
|
>
|
|
<button
|
|
onClick={toggleQueueSection}
|
|
className="flex items-center justify-between px-4 py-3 pr-12 bg-muted/50 hover:bg-muted transition-colors border-b border-border"
|
|
disabled={queueExpanded && !participantsExpanded}
|
|
>
|
|
<h2 className="font-semibold text-sm">My Queue</h2>
|
|
{queueExpanded ? (
|
|
<ChevronUp className="h-4 w-4" />
|
|
) : (
|
|
<ChevronDown className="h-4 w-4" />
|
|
)}
|
|
</button>
|
|
{queueExpanded && (
|
|
<div className="flex-1 overflow-y-auto">{queueSection}</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Available Participants Section */}
|
|
<div
|
|
className="flex flex-col overflow-hidden"
|
|
style={{ flex: participantsFlexSize }}
|
|
>
|
|
<button
|
|
onClick={toggleParticipantsSection}
|
|
className="flex items-center justify-between px-4 py-3 bg-muted/50 hover:bg-muted transition-colors border-b border-border"
|
|
disabled={participantsExpanded && !queueExpanded}
|
|
>
|
|
<h2 className="font-semibold text-sm">Available Participants</h2>
|
|
{participantsExpanded ? (
|
|
<ChevronUp className="h-4 w-4" />
|
|
) : (
|
|
<ChevronDown className="h-4 w-4" />
|
|
)}
|
|
</button>
|
|
{participantsExpanded && (
|
|
<div className="flex-1 overflow-y-auto">{participantsSection}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|