1
0
forked from baron/baron-sso

admin/dev 사이드바 프레임 공통화

This commit is contained in:
2026-05-15 10:16:37 +09:00
parent 0327409631
commit 0bf8089120
5 changed files with 263 additions and 223 deletions

View File

@@ -0,0 +1,49 @@
import type { ComponentType, ReactNode } from "react";
import { shellLayoutClasses } from "./layout";
export type ShellSidebarNavItem = {
labelKey: string;
labelFallback: string;
to: string;
icon: ComponentType<{ size?: number | string }>;
isExternal?: boolean;
end?: boolean;
isActive?: boolean;
};
type ShellSidebarProps = {
brandLabel: string;
brandTitle: string;
brandIcon: ReactNode;
navContent: ReactNode;
footerContent: ReactNode;
};
export function AppSidebar({
brandLabel,
brandTitle,
brandIcon,
navContent,
footerContent,
}: ShellSidebarProps) {
return (
<aside className={shellLayoutClasses.aside}>
<div>
<div className={shellLayoutClasses.brandSection}>
<div className={shellLayoutClasses.brandWrap}>
<div className={shellLayoutClasses.brandIcon}>{brandIcon}</div>
<div>
<p className="text-xs uppercase tracking-[0.18em] text-muted-foreground">
{brandLabel}
</p>
<h1 className="text-lg font-semibold">{brandTitle}</h1>
</div>
</div>
</div>
<nav className={shellLayoutClasses.navWrap}>{navContent}</nav>
</div>
<div>{footerContent}</div>
</aside>
);
}

View File

@@ -22,55 +22,9 @@ type ShellProfileSummaryParams = {
export const SHELL_THEME_STORAGE_KEY = "admin_theme";
export const SHELL_SESSION_EXPIRY_STORAGE_KEY =
"baron_session_expiry_enabled";
export const shellLayoutClasses = {
root: "grid min-h-screen bg-background text-foreground md:grid-cols-[240px,1fr]",
aside:
"flex flex-col justify-between border-b border-border bg-card md:sticky md:top-0 md:h-screen md:border-b-0 md:border-r md:bg-card md:backdrop-blur",
asideStatic:
"border-b border-border bg-card md:sticky md:top-0 md:h-screen md:border-b-0 md:border-r md:bg-card md:backdrop-blur",
brandSection:
"flex items-center justify-between px-5 py-4 md:block md:space-y-6 md:py-6",
brandWrap: "flex items-center gap-3 md:flex-col md:items-start",
brandIcon:
"grid h-11 w-11 place-items-center rounded-xl bg-primary/15 text-primary shadow-[0_12px_30px_rgba(54,211,153,0.22)]",
scopeBadge:
"hidden rounded-full border border-border px-3 py-2 text-xs text-muted-foreground md:inline-flex md:items-center md:gap-2",
navWrap: "px-2 pb-4 md:px-3 md:pb-8",
navMeta:
"flex flex-wrap gap-2 px-3 pb-4 text-[11px] text-muted-foreground md:flex-col md:items-start",
navList: "flex flex-col gap-1",
navItemBase:
"flex items-center gap-3 rounded-xl px-3 py-3 text-sm transition",
navItemActive:
"bg-primary/10 text-primary shadow-[0_12px_40px_rgba(54,211,153,0.18)]",
navItemIdle: "text-muted-foreground hover:bg-muted/10 hover:text-foreground",
sidebarFooterNotice:
"hidden space-y-2 px-5 pb-6 pt-2 text-xs text-[var(--color-muted)] md:block",
logoutButton:
"flex w-full items-center gap-3 rounded-xl px-3 py-3 text-sm text-muted-foreground transition hover:bg-destructive/10 hover:text-destructive",
header: "sticky top-0 z-20 border-b border-border bg-background/90 backdrop-blur",
headerElevated:
"sticky top-0 z-50 border-b border-border bg-background/90 backdrop-blur",
headerInner: "flex items-center justify-between px-5 py-4 md:px-8",
headerTitleWrap: "flex flex-col gap-1",
headerActions: "flex items-center gap-2 text-sm",
actionButton:
"inline-flex items-center gap-2 rounded-full border border-border px-3 py-2 text-muted-foreground transition hover:bg-muted/20",
sessionBadge:
"hidden rounded-full border px-3 py-2 text-xs font-medium md:inline-flex",
profileInitial:
"grid h-8 w-8 place-items-center rounded-full bg-primary/15 text-xs font-semibold text-primary",
profileMenu:
"absolute right-0 z-30 mt-2 w-72 rounded-xl border border-border bg-card p-3 shadow-xl",
profileCard:
"mt-2 flex flex-col gap-2 rounded-lg border border-border px-3 py-3",
settingsCard: "mt-2 rounded-lg border border-border px-3 py-3",
content: "relative",
contentWide: "relative min-w-0",
main: "px-5 py-6 md:px-10 md:py-10",
mainMinWidth: "min-w-0 px-5 py-6 md:px-10 md:py-10",
} as const;
export { AppSidebar } from "./AppSidebar";
export type { ShellSidebarNavItem } from "./AppSidebar";
export { shellLayoutClasses } from "./layout";
export function readShellTheme(): ShellTheme {
return window.localStorage.getItem(SHELL_THEME_STORAGE_KEY) === "dark"

48
common/shell/layout.ts Normal file
View File

@@ -0,0 +1,48 @@
export const shellLayoutClasses = {
root: "grid min-h-screen bg-background text-foreground md:grid-cols-[240px,1fr]",
aside:
"flex flex-col justify-between border-b border-border bg-card md:sticky md:top-0 md:h-screen md:border-b-0 md:border-r md:bg-card md:backdrop-blur",
asideStatic:
"border-b border-border bg-card md:sticky md:top-0 md:h-screen md:border-b-0 md:border-r md:bg-card md:backdrop-blur",
brandSection:
"flex items-center justify-between px-5 py-4 md:block md:space-y-6 md:py-6",
brandWrap: "flex items-center gap-3 md:flex-col md:items-start",
brandIcon:
"grid h-11 w-11 place-items-center rounded-xl bg-primary/15 text-primary shadow-[0_12px_30px_rgba(54,211,153,0.22)]",
scopeBadge:
"hidden rounded-full border border-border px-3 py-2 text-xs text-muted-foreground md:inline-flex md:items-center md:gap-2",
navWrap: "px-2 pb-4 md:px-3 md:pb-8",
navMeta:
"flex flex-wrap gap-2 px-3 pb-4 text-[11px] text-muted-foreground md:flex-col md:items-start",
navList: "flex flex-col gap-1",
navItemBase:
"flex items-center gap-3 rounded-xl px-3 py-3 text-sm transition",
navItemActive:
"bg-primary/10 text-primary shadow-[0_12px_40px_rgba(54,211,153,0.18)]",
navItemIdle: "text-muted-foreground hover:bg-muted/10 hover:text-foreground",
sidebarFooterNotice:
"hidden space-y-2 px-5 pb-6 pt-2 text-xs text-[var(--color-muted)] md:block",
logoutButton:
"flex w-full items-center gap-3 rounded-xl px-3 py-3 text-sm text-muted-foreground transition hover:bg-destructive/10 hover:text-destructive",
header: "sticky top-0 z-20 border-b border-border bg-background/90 backdrop-blur",
headerElevated:
"sticky top-0 z-50 border-b border-border bg-background/90 backdrop-blur",
headerInner: "flex items-center justify-between px-5 py-4 md:px-8",
headerTitleWrap: "flex flex-col gap-1",
headerActions: "flex items-center gap-2 text-sm",
actionButton:
"inline-flex items-center gap-2 rounded-full border border-border px-3 py-2 text-muted-foreground transition hover:bg-muted/20",
sessionBadge:
"hidden rounded-full border px-3 py-2 text-xs font-medium md:inline-flex",
profileInitial:
"grid h-8 w-8 place-items-center rounded-full bg-primary/15 text-xs font-semibold text-primary",
profileMenu:
"absolute right-0 z-30 mt-2 w-72 rounded-xl border border-border bg-card p-3 shadow-xl",
profileCard:
"mt-2 flex flex-col gap-2 rounded-lg border border-border px-3 py-3",
settingsCard: "mt-2 rounded-lg border border-border px-3 py-3",
content: "relative",
contentWide: "relative min-w-0",
main: "px-5 py-6 md:px-10 md:py-10",
mainMinWidth: "min-w-0 px-5 py-6 md:px-10 md:py-10",
} as const;