73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
import { List, PlusCircle, Pencil } from "lucide-react";
|
|
|
|
export type ViewType = "list" | "create" | "edit";
|
|
|
|
interface ViewTab {
|
|
id: ViewType;
|
|
label: string;
|
|
icon: React.ReactNode;
|
|
hint: string;
|
|
}
|
|
|
|
const VIEW_TABS: ViewTab[] = [
|
|
{ id: "list", label: "목록 화면", icon: <List size={13} />, hint: "목록 + 검색 + 그리드" },
|
|
{ id: "create", label: "등록 팝업", icon: <PlusCircle size={13} />, hint: "등록 팝업 편집" },
|
|
{ id: "edit", label: "수정 팝업", icon: <Pencil size={13} />, hint: "수정 팝업 편집" },
|
|
];
|
|
|
|
interface ViewTabBarProps {
|
|
activeView: ViewType;
|
|
onViewChange: (view: ViewType) => void;
|
|
/** 각 뷰에 컴포넌트가 있는지 (뱃지 표시용) */
|
|
viewCounts?: Record<ViewType, number>;
|
|
/**
|
|
* 표시할 뷰 탭. 생략 시 전체 표시.
|
|
* list 는 항상 포함 (루트). create/edit 는 목록 뷰에
|
|
* 해당 actionType 버튼이 배치됐을 때만 포함되는 식으로
|
|
* 부모에서 결정한다.
|
|
*/
|
|
visibleViews?: ViewType[];
|
|
}
|
|
|
|
export function ViewTabBar({
|
|
activeView,
|
|
onViewChange,
|
|
viewCounts,
|
|
visibleViews,
|
|
}: ViewTabBarProps) {
|
|
const tabs = visibleViews
|
|
? VIEW_TABS.filter((t) => visibleViews.includes(t.id))
|
|
: VIEW_TABS;
|
|
const activeTab = tabs.find((t) => t.id === activeView);
|
|
|
|
return (
|
|
<div className="view-tab-bar">
|
|
<div className="view-tab-group">
|
|
{tabs.map((tab) => {
|
|
const count = viewCounts?.[tab.id] ?? 0;
|
|
const isActive = tab.id === activeView;
|
|
|
|
return (
|
|
<button
|
|
key={tab.id}
|
|
type="button"
|
|
className={cn("view-tab", isActive && "active")}
|
|
onClick={() => onViewChange(tab.id)}
|
|
>
|
|
{tab.icon}
|
|
<span>{tab.label}</span>
|
|
{count > 0 && (
|
|
<span className="view-tab-badge">{count}</span>
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
<span className="view-tab-hint">{activeTab?.hint}</span>
|
|
</div>
|
|
);
|
|
}
|