summaryrefslogtreecommitdiff
path: root/components/layout/DynamicMenuRender.tsx
blob: f94223aea055fade45add11fd3c91ac73d8f56a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"use client";

import Link from "next/link";
import { cn } from "@/lib/utils";
import { NavigationMenuLink } from "@/components/ui/navigation-menu";
import type { MenuTreeNode } from "@/lib/menu-v2/types";

interface DynamicMenuRenderProps {
  groups: MenuTreeNode[] | undefined;
  lng: string;
  getTitle: (node: MenuTreeNode) => string;
  getDescription: (node: MenuTreeNode) => string | null;
  onItemClick?: () => void;
}

export default function DynamicMenuRender({
  groups,
  lng,
  getTitle,
  getDescription,
  onItemClick,
}: DynamicMenuRenderProps) {
  if (!groups || groups.length === 0) {
    return (
      <div className="p-4 text-sm text-muted-foreground">
        메뉴가 없습니다.
      </div>
    );
  }

  // 그룹별로 메뉴 분류
  const groupedMenus = new Map<string, MenuTreeNode[]>();
  const ungroupedMenus: MenuTreeNode[] = [];

  for (const item of groups) {
    if (item.nodeType === "group") {
      // 그룹인 경우, 그룹의 children을 해당 그룹에 추가
      const groupTitle = getTitle(item);
      if (!groupedMenus.has(groupTitle)) {
        groupedMenus.set(groupTitle, []);
      }
      if (item.children) {
        groupedMenus.get(groupTitle)!.push(...item.children);
      }
    } else if (item.nodeType === "menu") {
      // 직접 메뉴인 경우 (그룹 없이 직접 메뉴그룹에 속한 경우)
      ungroupedMenus.push(item);
    }
  }

  // 그룹이 없고 메뉴만 있는 경우 - 단순 그리드 렌더링
  if (groupedMenus.size === 0 && ungroupedMenus.length > 0) {
    return (
      <ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
        {ungroupedMenus.map((menu) => (
          <MenuListItem
            key={menu.id}
            href={`/${lng}${menu.menuPath}`}
            title={getTitle(menu)}
            onClick={onItemClick}
          >
            {getDescription(menu)}
          </MenuListItem>
        ))}
      </ul>
    );
  }

  // 그룹별 렌더링 - 가로 스크롤 지원
  // 컨텐츠가 85vw를 초과할 때만 스크롤 발생
  return (
    <div className="p-4 max-w-[85vw]">
      <div className="flex gap-6 overflow-x-auto">
        {/* 그룹화되지 않은 메뉴 (있는 경우) */}
        {ungroupedMenus.length > 0 && (
          <div className="w-[200px] flex-shrink-0">
            <ul className="space-y-2">
              {ungroupedMenus.map((menu) => (
                <MenuListItem
                  key={menu.id}
                  href={`/${lng}${menu.menuPath}`}
                  title={getTitle(menu)}
                  onClick={onItemClick}
                >
                  {getDescription(menu)}
                </MenuListItem>
              ))}
            </ul>
          </div>
        )}

        {/* 그룹별 메뉴 - 순서대로 가로 배치 */}
        {Array.from(groupedMenus.entries()).map(([groupTitle, menus]) => (
          <div key={groupTitle} className="w-[200px] flex-shrink-0">
            <h4 className="mb-2 text-sm font-semibold text-muted-foreground whitespace-nowrap">
              {groupTitle}
            </h4>
            <ul className="space-y-2">
              {menus.map((menu) => (
                <MenuListItem
                  key={menu.id}
                  href={`/${lng}${menu.menuPath}`}
                  title={getTitle(menu)}
                  onClick={onItemClick}
                >
                  {getDescription(menu)}
                </MenuListItem>
              ))}
            </ul>
          </div>
        ))}
      </div>
    </div>
  );
}

interface MenuListItemProps {
  href: string;
  title: string;
  children?: React.ReactNode;
  onClick?: () => void;
}

function MenuListItem({ href, title, children, onClick }: MenuListItemProps) {
  return (
    <li>
      <NavigationMenuLink asChild>
        <Link
          href={href}
          onClick={onClick}
          className={cn(
            "block select-none space-y-1 rounded-md p-2 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground"
          )}
        >
          <div className="text-sm font-medium leading-none">{title}</div>
          {children && (
            <p className="line-clamp-2 text-xs leading-snug text-muted-foreground">
              {children}
            </p>
          )}
        </Link>
      </NavigationMenuLink>
    </li>
  );
}