메가메뉴 닫힘 딜레이이
This commit is contained in:
@@ -5,7 +5,7 @@ const prisma = new PrismaClient();
|
|||||||
async function upsertCategories() {
|
async function upsertCategories() {
|
||||||
// 카테고리 트리 (projectmemo 기준 상위 그룹)
|
// 카테고리 트리 (projectmemo 기준 상위 그룹)
|
||||||
const categories = [
|
const categories = [
|
||||||
{ name: "암실소문 (메인)", slug: "main", sortOrder: 1, status: "active" },
|
{ name: "암실소문", slug: "main", sortOrder: 1, status: "active" },
|
||||||
{ name: "명예의 전당", slug: "hall-of-fame", sortOrder: 2, status: "active" },
|
{ name: "명예의 전당", slug: "hall-of-fame", sortOrder: 2, status: "active" },
|
||||||
{ name: "주변 제휴업체", slug: "nearby-partners", sortOrder: 3, status: "active" },
|
{ name: "주변 제휴업체", slug: "nearby-partners", sortOrder: 3, status: "active" },
|
||||||
{ name: "제휴업소 정보", slug: "partner-info", sortOrder: 4, status: "active" },
|
{ name: "제휴업소 정보", slug: "partner-info", sortOrder: 4, status: "active" },
|
||||||
@@ -128,8 +128,11 @@ async function upsertBoards(admin, categoryMap) {
|
|||||||
{ name: "회원랭킹", slug: "ranking", description: "랭킹", type: "special", sortOrder: 14 },
|
{ name: "회원랭킹", slug: "ranking", description: "랭킹", type: "special", sortOrder: 14 },
|
||||||
{ name: "무료쿠폰", slug: "free-coupons", description: "쿠폰", type: "special", sortOrder: 15 },
|
{ name: "무료쿠폰", slug: "free-coupons", description: "쿠폰", type: "special", sortOrder: 15 },
|
||||||
{ name: "월간집계", slug: "monthly-stats", description: "월간 통계", type: "special", sortOrder: 16 },
|
{ name: "월간집계", slug: "monthly-stats", description: "월간 통계", type: "special", sortOrder: 16 },
|
||||||
// 제휴업소 일반(사진)
|
// 제휴업소 일반
|
||||||
{ name: "제휴업소 일반(사진)", slug: "partners-photos", description: "사진 전용 게시판", type: "general", sortOrder: 17, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } },
|
{ name: "제휴업소", slug: "partners-photos", description: "사진 전용 게시판", type: "general", sortOrder: 17, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } },
|
||||||
|
// 광고/제휴
|
||||||
|
{ name: "제휴문의", slug: "partner-contact", description: "제휴문의", type: "general", sortOrder: 18, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } },
|
||||||
|
{ name: "제휴업소 요청", slug: "partner-req", description: "제휴업소 요청", type: "general", sortOrder: 19, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } },
|
||||||
];
|
];
|
||||||
|
|
||||||
const created = [];
|
const created = [];
|
||||||
@@ -159,6 +162,9 @@ async function upsertBoards(admin, categoryMap) {
|
|||||||
anonymous: "community",
|
anonymous: "community",
|
||||||
"find-therapist": "community",
|
"find-therapist": "community",
|
||||||
"blue-house": "community",
|
"blue-house": "community",
|
||||||
|
// 광고/제휴
|
||||||
|
"partner-contact": "ads-affiliates",
|
||||||
|
"partner-req": "ads-affiliates",
|
||||||
};
|
};
|
||||||
const categorySlug = mapBySlug[b.slug] || "community";
|
const categorySlug = mapBySlug[b.slug] || "community";
|
||||||
const category = categoryMap[categorySlug];
|
const category = categoryMap[categorySlug];
|
||||||
|
|||||||
@@ -18,6 +18,19 @@ export function AppHeader() {
|
|||||||
const [leftPositions, setLeftPositions] = React.useState<Record<string, number>>({});
|
const [leftPositions, setLeftPositions] = React.useState<Record<string, number>>({});
|
||||||
const [panelHeight, setPanelHeight] = React.useState<number>(0);
|
const [panelHeight, setPanelHeight] = React.useState<number>(0);
|
||||||
const [blockWidths, setBlockWidths] = React.useState<Record<string, number>>({});
|
const [blockWidths, setBlockWidths] = React.useState<Record<string, number>>({});
|
||||||
|
const closeTimer = React.useRef<number | null>(null);
|
||||||
|
|
||||||
|
const cancelClose = React.useCallback(() => {
|
||||||
|
if (closeTimer.current) {
|
||||||
|
window.clearTimeout(closeTimer.current);
|
||||||
|
closeTimer.current = null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const scheduleClose = React.useCallback(() => {
|
||||||
|
cancelClose();
|
||||||
|
closeTimer.current = window.setTimeout(() => setMegaOpen(false), 150);
|
||||||
|
}, [cancelClose]);
|
||||||
// 카테고리 로드
|
// 카테고리 로드
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetch("/api/categories", { cache: "no-store" })
|
fetch("/api/categories", { cache: "no-store" })
|
||||||
@@ -120,8 +133,8 @@ export function AppHeader() {
|
|||||||
{/* 데스크톱 메가메뉴 */}
|
{/* 데스크톱 메가메뉴 */}
|
||||||
<div
|
<div
|
||||||
className="relative hidden xl:block pl-10"
|
className="relative hidden xl:block pl-10"
|
||||||
onMouseEnter={() => setMegaOpen(true)}
|
onMouseEnter={() => { cancelClose(); setMegaOpen(true); }}
|
||||||
onMouseLeave={() => setMegaOpen(false)}
|
onMouseLeave={() => { scheduleClose(); }}
|
||||||
onFocusCapture={() => setMegaOpen(true)}
|
onFocusCapture={() => setMegaOpen(true)}
|
||||||
onBlurCapture={(e) => {
|
onBlurCapture={(e) => {
|
||||||
const next = (e as unknown as React.FocusEvent<HTMLDivElement>).relatedTarget as Node | null;
|
const next = (e as unknown as React.FocusEvent<HTMLDivElement>).relatedTarget as Node | null;
|
||||||
@@ -153,8 +166,10 @@ export function AppHeader() {
|
|||||||
megaOpen ? "opacity-100" : "pointer-events-none opacity-0"
|
megaOpen ? "opacity-100" : "pointer-events-none opacity-0"
|
||||||
}`}
|
}`}
|
||||||
style={{ top: headerBottom }}
|
style={{ top: headerBottom }}
|
||||||
|
onMouseEnter={() => { cancelClose(); setMegaOpen(true); }}
|
||||||
|
onMouseLeave={() => { scheduleClose(); }}
|
||||||
>
|
>
|
||||||
<div className="px-4 py-4 w-screen">
|
<div className="px-4 py-4 w-full max-w-7xl mx-auto overflow-x-hidden">
|
||||||
<div ref={panelRef} className="relative">
|
<div ref={panelRef} className="relative">
|
||||||
{categories.map((cat) => (
|
{categories.map((cat) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function RootLayout({
|
|||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
<div className="sticky top-0 z-50 border-b bg-white/80 backdrop-blur">
|
<div className="sticky top-0 z-50 border-b bg-white/80 backdrop-blur">
|
||||||
<div className="mx-auto">
|
<div className="mx-auto max-w-7xl w-full">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user