feat: page title 컴포넌트 생성
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { BaseLayout } from "@common/components/ui";
|
||||
import type { MenuItem } from "@common/components/api";
|
||||
import { useMenuStore } from "@/shared/lib/use-menu-store";
|
||||
|
||||
export default function WithBaseLayout({
|
||||
children,
|
||||
}: {
|
||||
@@ -9,9 +12,15 @@ export default function WithBaseLayout({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { setCurrentMenuInfo } = useMenuStore((state) => state);
|
||||
|
||||
const handleMenuClick = (item: MenuItem) => {
|
||||
setCurrentMenuInfo(item);
|
||||
router.push(item.urlPath);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout currentPath={pathname ?? ""} routeHandler={router.push}>
|
||||
<BaseLayout currentPath={pathname ?? ""} handleMenuClick={handleMenuClick}>
|
||||
{children}
|
||||
</BaseLayout>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useStore } from "zustand";
|
||||
import { useContext } from "react";
|
||||
import type { MenuStore } from "../store/menu-store";
|
||||
import { StoreContext } from "../providers/store-provider";
|
||||
|
||||
export const useMenuStore = <T>(selector: (store: MenuStore) => T): T => {
|
||||
const storeContext = useContext(StoreContext);
|
||||
|
||||
if (!storeContext) {
|
||||
throw new Error("useMenuStore must be used within StoreProvider");
|
||||
}
|
||||
|
||||
return useStore(storeContext.menuStore, selector);
|
||||
};
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useRef } from "react";
|
||||
import { createAlertStore } from "../store/alert-store";
|
||||
import { createUserStore } from "../store/user-store";
|
||||
import { createAlertStore, createMenuStore, createUserStore } from "../store";
|
||||
|
||||
type StoreContextType = {
|
||||
alertStore: ReturnType<typeof createAlertStore>;
|
||||
menuStore: ReturnType<typeof createMenuStore>;
|
||||
userStore: ReturnType<typeof createUserStore>;
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ export const StoreProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
if (storeRef.current === null) {
|
||||
storeRef.current = {
|
||||
alertStore: createAlertStore(),
|
||||
menuStore: createMenuStore(),
|
||||
userStore: createUserStore(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export { createAlertStore } from "./alert-store";
|
||||
export { createMenuStore } from "./menu-store";
|
||||
export { createUserStore, type IUserInfo } from "./user-store";
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { createStore } from "zustand/vanilla";
|
||||
import type { MenuItem } from "@common/components/api";
|
||||
|
||||
export type MenuState = {
|
||||
currentMenuInfo: MenuItem | null;
|
||||
};
|
||||
|
||||
export type MenuActions = {
|
||||
setCurrentMenuInfo: (menu: MenuItem) => void;
|
||||
};
|
||||
|
||||
export type MenuStore = MenuState & MenuActions;
|
||||
|
||||
export const defaultInitState: MenuState = {
|
||||
currentMenuInfo: null,
|
||||
};
|
||||
|
||||
export const createMenuStore = (initState: MenuState = defaultInitState) => {
|
||||
return createStore<MenuStore>()((set) => ({
|
||||
...initState,
|
||||
setCurrentMenuInfo: (menu: MenuItem) => set({ currentMenuInfo: menu }),
|
||||
}));
|
||||
};
|
||||
@@ -1,3 +1,6 @@
|
||||
import { PageTitle } from "@common/components";
|
||||
import { useMenuStore } from "../lib/use-menu-store";
|
||||
|
||||
function PageLayout({
|
||||
children,
|
||||
pageTitle,
|
||||
@@ -5,11 +8,15 @@ function PageLayout({
|
||||
children: React.ReactNode;
|
||||
pageTitle: string;
|
||||
}) {
|
||||
const { currentMenuInfo } = useMenuStore((state) => state);
|
||||
|
||||
const menuPaths = currentMenuInfo?.menuPath
|
||||
?.split(">")
|
||||
.map((path) => path.trim());
|
||||
|
||||
return (
|
||||
<div className="h-auto min-h-full bg-container">
|
||||
<h2 className="px-[1.8rem] pt-[2rem] text-[2.4rem] font-semibold text-title">
|
||||
{pageTitle}
|
||||
</h2>
|
||||
<PageTitle title={pageTitle} menuPaths={menuPaths ?? []} />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface MenuItem {
|
||||
urlPath: string;
|
||||
menuLnkgTypeCd: string;
|
||||
menuLnkgSn: number;
|
||||
menuPath: string;
|
||||
qrcd: string;
|
||||
useYn: string;
|
||||
otptYn: string;
|
||||
|
||||
@@ -13,6 +13,7 @@ export * from "./input";
|
||||
export * from "./label";
|
||||
export * from "./layout";
|
||||
export * from "./loading";
|
||||
export * from "./page-title";
|
||||
export * from "./popover";
|
||||
export * from "./radio-group";
|
||||
export * from "./select";
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import type { MenuItem } from "../../api";
|
||||
import { QueryProvider } from "../../provider/query-provider";
|
||||
import { SidebarProvider } from "../sidebar";
|
||||
import { LNB } from "../lnb";
|
||||
import { Header } from "../header";
|
||||
import { Footer } from "./footer";
|
||||
|
||||
import { SidebarProvider } from "../sidebar";
|
||||
import "./common.css";
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
export function BaseLayout({
|
||||
children,
|
||||
queryClient,
|
||||
currentPath,
|
||||
routeHandler,
|
||||
handleMenuClick,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
queryClient?: QueryClient;
|
||||
currentPath?: string;
|
||||
routeHandler?: (path: string) => void;
|
||||
handleMenuClick?: (item: MenuItem) => void;
|
||||
}) {
|
||||
return (
|
||||
<QueryProvider client={queryClient}>
|
||||
@@ -24,7 +24,7 @@ export function BaseLayout({
|
||||
<div className="flex-1">
|
||||
<Header />
|
||||
<div className="flex h-[calc(100vh-50px)]">
|
||||
<LNB currentPath={currentPath} routeHandler={routeHandler} />
|
||||
<LNB currentPath={currentPath} handleMenuClick={handleMenuClick} />
|
||||
<div className="flex flex-1 flex-col">
|
||||
<div className="max-h-[calc(100%-50px)] flex-1 overflow-auto">
|
||||
{children}
|
||||
|
||||
@@ -22,11 +22,11 @@ import { getParentIds } from "./lib";
|
||||
export function LNB({
|
||||
viewOnly,
|
||||
currentPath,
|
||||
routeHandler,
|
||||
handleMenuClick,
|
||||
}: {
|
||||
viewOnly?: boolean;
|
||||
currentPath?: string;
|
||||
routeHandler?: (path: string) => void;
|
||||
handleMenuClick?: (item: MenuItem) => void;
|
||||
}) {
|
||||
const { data: menuList } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
@@ -45,7 +45,7 @@ export function LNB({
|
||||
|
||||
const handleClick = () => {
|
||||
setCookie("menu-code-key", item.menuCd.toString());
|
||||
routeHandler?.(item.urlPath);
|
||||
handleMenuClick?.(item);
|
||||
};
|
||||
|
||||
if (item.children) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
|
||||
const PageTitle = ({
|
||||
title,
|
||||
menuPaths,
|
||||
}: {
|
||||
title: string;
|
||||
menuPaths: string[];
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex w-full items-center justify-between px-[1.8rem] pt-[2rem]">
|
||||
<h2 className="text-[2.4rem] font-semibold text-title">{title}</h2>
|
||||
{menuPaths.length && (
|
||||
<div className="flex items-center gap-3">
|
||||
<i className="diveicon di-home-fill text-foreground" />
|
||||
{menuPaths?.map((path, i) => (
|
||||
<React.Fragment key={path}>
|
||||
{i !== 0 && (
|
||||
<i className="diveicon di-arrow-forward text-foreground" />
|
||||
)}
|
||||
<span className="text-xl text-foreground">{path}</span>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { PageTitle };
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { PageTitle } from ".";
|
||||
|
||||
const meta: Meta<typeof PageTitle> = {
|
||||
title: "UI/PageTitle",
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PageTitle>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<div className="w-[40rem]">
|
||||
<PageTitle title="타이틀" menuPaths={["메뉴 1", "메뉴 2", "메뉴 3"]} />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
Reference in New Issue
Block a user