메인 베너너

This commit is contained in:
mota
2025-10-13 18:06:46 +09:00
parent a1e4f76b0b
commit 329537e3ca
6 changed files with 175 additions and 45 deletions

View File

@@ -16,6 +16,7 @@ export function AppHeader() {
const navRefs = React.useRef<Record<string, HTMLAnchorElement | null>>({});
const panelRef = React.useRef<HTMLDivElement | null>(null);
const blockRefs = React.useRef<Record<string, HTMLDivElement | null>>({});
const navItemRefs = React.useRef<Record<string, HTMLDivElement | null>>({});
const [leftPositions, setLeftPositions] = React.useState<Record<string, number>>({});
const [panelHeight, setPanelHeight] = React.useState<number>(0);
const [blockWidths, setBlockWidths] = React.useState<Record<string, number>>({});
@@ -95,29 +96,15 @@ export function AppHeader() {
if (!container) return;
const containerRect = container.getBoundingClientRect();
const nextPositions: Record<string, number> = {};
const nextWidths: Record<string, number> = {};
categories.forEach((cat) => {
const a = navRefs.current[cat.slug];
if (a) {
const r = a.getBoundingClientRect();
nextPositions[cat.slug] = Math.max(0, r.left - containerRect.left);
}
const itemEl = navItemRefs.current[cat.slug];
if (!itemEl) return;
const r = itemEl.getBoundingClientRect();
nextPositions[cat.slug] = Math.max(0, r.left - containerRect.left);
nextWidths[cat.slug] = r.width; // 각 네비 항목의 실제 폭을 사용
});
setLeftPositions(nextPositions);
// 각 블록의 가로 폭 = 다음 항목의 left까지 남은 거리
// 마지막 항목은 컨테이너 끝까지 확장되지 않도록 width를 지정하지 않음
const nextWidths: Record<string, number> = {};
const ordered = categories
.filter((c) => typeof nextPositions[c.slug] === "number")
.sort((a, b) => (nextPositions[a.slug]! - nextPositions[b.slug]!));
ordered.forEach((cat, idx) => {
const currentLeft = nextPositions[cat.slug]!;
if (idx < ordered.length - 1) {
const nextLeft = nextPositions[ordered[idx + 1].slug]!;
const width = Math.max(0, nextLeft - currentLeft);
nextWidths[cat.slug] = width;
}
});
setBlockWidths(nextWidths);
// 패널 높이 = 블록들 중 최대 높이
@@ -166,11 +153,19 @@ export function AppHeader() {
}}
>
<div className="flex items-center gap-8">
{categories.map((cat) => (
<div key={cat.id} className="relative group min-w-[80px] text-center" onMouseEnter={() => setOpenSlug(cat.slug)}>
{categories.map((cat, idx) => (
<div
key={cat.id}
className="relative group min-w-[80px] text-center"
onMouseEnter={() => setOpenSlug(cat.slug)}
ref={(el) => {
navItemRefs.current[cat.slug] = el;
}}
style={{ minWidth: idx === categories.length - 1 ? 120 : undefined }}
>
<Link
href={cat.boards?.[0]?.id ? `/boards/${cat.boards[0].id}` : `/boards?category=${cat.slug}`}
className={`px-2 py-2 text-sm font-medium transition-colors duration-200 hover:text-neutral-900 ${
className={`px-2 py-2 text-sm font-medium transition-colors duration-200 hover:text-neutral-900 whitespace-nowrap ${
activeCategorySlug === cat.slug ? "text-neutral-900" : "text-neutral-700"
}`}
ref={(el) => {
@@ -197,7 +192,7 @@ export function AppHeader() {
onMouseEnter={() => { cancelClose(); setMegaOpen(true); }}
onMouseLeave={() => { scheduleClose(); }}
>
<div className="px-4 py-4 w-full max-w-7xl mx-auto overflow-x-hidden">
<div className="px-4 py-4 w-full mx-auto overflow-x-hidden">
<div ref={panelRef} className="relative">
{categories.map((cat) => (
<div
@@ -206,7 +201,7 @@ export function AppHeader() {
blockRefs.current[cat.slug] = el;
}}
className="absolute top-0"
style={{ left: (leftPositions[cat.slug] ?? 0) + 0, width: blockWidths[cat.slug] ?? undefined }}
style={{ left: (leftPositions[cat.slug] ?? 0), width: blockWidths[cat.slug] ?? undefined }}
>
{/* <div className="mb-2 font-semibold text-neutral-800">{cat.name}</div> */}
<div className="flex flex-col gap-3">
@@ -214,7 +209,7 @@ export function AppHeader() {
<Link
key={b.id}
href={`/boards/${b.id}`}
className={`rounded px-2 py-1 text-sm transition-colors duration-150 hover:bg-neutral-100 hover:text-neutral-900 text-center ${
className={`rounded px-2 py-1 text-sm transition-colors duration-150 hover:bg-neutral-100 hover:text-neutral-900 text-center whitespace-nowrap ${
activeBoardId === b.id ? "bg-neutral-100 text-neutral-900 font-medium" : "text-neutral-700"
}`}
aria-current={activeBoardId === b.id ? "page" : undefined}