brackt/app/components/DraftSidebar.tsx
Chris Parsons 2cd4096e70 feat: Implement draft room redesign with collapsible sidebar and tabbed content
- 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.
2025-10-25 03:23:41 -07:00

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>
</>
);
}