Files
TougliGui/src/components/Sidebar.tsx
2026-04-24 20:34:50 +02:00

164 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useState } from "react";
import { useStore } from "../store";
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handler = () => setWidth(window.innerWidth);
window.addEventListener("resize", handler);
return () => window.removeEventListener("resize", handler);
}, []);
return width;
}
export default function Sidebar() {
const { guides, openGuide, activeGuideGid, view, sidebarCollapsed, setSidebarCollapsed } = useStore();
const [search, setSearch] = useState("");
const collapsed = sidebarCollapsed;
const windowWidth = useWindowWidth();
const isOverlay = collapsed || windowWidth < 500;
const filtered = guides.filter(g =>
g.name.toLowerCase().includes(search.toLowerCase())
);
return (
<aside style={{
position: isOverlay ? "absolute" : "relative",
left: 0,
top: 0,
bottom: 0,
zIndex: 10,
width: collapsed ? "36px" : "190px",
flexShrink: 0,
background: collapsed ? "transparent" : "#161b22",
borderRight: collapsed ? "none" : "1px solid #2d3748",
display: "flex",
flexDirection: "column",
overflow: "hidden",
transition: "width 0.2s ease, background 0.2s ease",
}}>
{/* Toggle button */}
<button
onClick={() => setSidebarCollapsed(!sidebarCollapsed)} title={collapsed ? "Ouvrir le menu" : "Réduire le menu"}
style={{
width: "100%",
height: "36px",
flexShrink: 0,
background: collapsed ? "rgba(22,27,34,0.9)" : "transparent",
border: collapsed ? "1px solid #2d3748" : "none",
borderLeft: "none",
borderBottom: collapsed ? "1px solid #2d3748" : "1px solid #2d3748",
borderRadius: collapsed ? "0 6px 6px 0" : "0",
color: "#4a5568",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: collapsed ? "center" : "flex-end",
padding: "0 10px",
marginTop: collapsed ? "8px" : "0",
transition: "all 0.15s",
}}
onMouseEnter={e => (e.currentTarget.style.color = "#f0c040")}
onMouseLeave={e => (e.currentTarget.style.color = "#4a5568")}
>
<span style={{
fontSize: "12px",
transform: collapsed ? "rotate(180deg)" : "rotate(0deg)",
transition: "transform 0.2s ease",
display: "inline-block",
}}>
</span>
</button>
{!collapsed && (
<>
{/* Search */}
<div style={{ padding: "10px 12px", borderBottom: "1px solid #2d3748" }}>
<input
value={search}
onChange={e => setSearch(e.target.value)}
placeholder="Rechercher un Dofus…"
style={{
width: "100%", background: "#0d1117", border: "1px solid #2d3748",
borderRadius: "6px", padding: "6px 10px", color: "#e2e8f0",
fontSize: "12px", outline: "none", boxSizing: "border-box",
}}
onFocus={e => (e.target.style.borderColor = "#f0c040")}
onBlur={e => (e.target.style.borderColor = "#2d3748")}
/>
</div>
{/* Guide list */}
<div style={{ flex: 1, overflowY: "auto", padding: "8px 0", scrollbarWidth: "none" }}>
{filtered.length === 0 ? (
<div style={{ padding: "16px 12px", color: "#4a5568", fontSize: "12px", textAlign: "center" }}>
Aucun guide synchronisé
</div>
) : (
filtered.map(guide => {
const pct = guide.total_quests > 0
? Math.round((guide.completed_quests / guide.total_quests) * 100)
: 0;
const isActive = guide.gid === activeGuideGid && view === "guide";
return (
<button
key={guide.gid}
onClick={() => openGuide(guide.gid)}
style={{
width: "100%", textAlign: "left",
background: isActive ? "rgba(240,192,64,0.08)" : "transparent",
border: "none", borderLeft: isActive ? "2px solid #f0c040" : "2px solid transparent",
padding: "8px 12px", cursor: "pointer", transition: "all 0.12s",
}}
onMouseEnter={e => {
if (!isActive) (e.currentTarget as HTMLElement).style.background = "rgba(255,255,255,0.03)";
}}
onMouseLeave={e => {
if (!isActive) (e.currentTarget as HTMLElement).style.background = "transparent";
}}
>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: "4px" }}>
<span style={{
fontSize: "12px", fontWeight: isActive ? 600 : 400,
color: isActive ? "#f0c040" : "#e2e8f0",
display: "-webkit-box",
WebkitLineClamp: 2,
WebkitBoxOrient: "vertical",
overflow: "hidden",
wordBreak: "break-word",
lineHeight: 1.3,
flex: 1,
} as React.CSSProperties}>
{guide.name}
</span>
<span style={{
fontSize: "10px", color: pct === 100 ? "#4ade80" : "#94a3b8",
fontWeight: 600, flexShrink: 0,
}}>
{pct}%
</span>
</div>
<div style={{
marginTop: "4px", height: "2px", background: "#2d3748",
borderRadius: "1px", overflow: "hidden",
}}>
<div style={{
height: "100%", width: `${pct}%`,
background: pct === 100 ? "#4ade80" : pct > 50 ? "#f0c040" : "#4a9eff",
transition: "width 0.3s ease",
}} />
</div>
</button>
);
})
)}
</div>
</>
)}
</aside>
);
}