Drop page / section descriptions, state notes and repeated captions from the representative Stories of 44 components, the Patterns, Center Graphic and OrgChart pages. Value readouts, spec lists and captions hide only on the representative page; hidden verification Stories keep them. Tabs Default API and the Filter Bar draft / applied counts move to the hidden DefaultApi and DraftApplied Stories; Center Overview is hidden with its direct URL kept. Story IDs are unchanged. Align the component, pattern and OrgChart docs with the pages, point the quality reference check at the DraftApplied Story, and make the P8 styles check expect no section header on the preview-only Expanded page.
152 lines
5.8 KiB
TypeScript
152 lines
5.8 KiB
TypeScript
import type { ReactNode } from "react"
|
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
import {
|
|
NavigationMenu,
|
|
NavigationMenuContent,
|
|
NavigationMenuIndicator,
|
|
NavigationMenuItem,
|
|
NavigationMenuLink,
|
|
NavigationMenuList,
|
|
NavigationMenuTrigger,
|
|
} from "@hm-design/ui"
|
|
import { StateBoard } from "@/components/story-board"
|
|
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
|
|
|
// Navigation Menu is for destinations (links), not commands. The representative Story opens the real menus with hover, click or
|
|
// keyboard; the same specimens also exist as Stories tagged `verification` (hidden from the sidebar). Title + description children
|
|
// inside a content link are marked with `data-description` for the secondary text.
|
|
|
|
function Destination({ href, title, description, active }: { href: string; title: string; description?: string; active?: boolean }) {
|
|
return (
|
|
<NavigationMenuLink href={href} active={active}>
|
|
<span>{title}</span>
|
|
{description ? <span data-description="">{description}</span> : null}
|
|
</NavigationMenuLink>
|
|
)
|
|
}
|
|
|
|
function Specimen({ viewport = true }: { viewport?: boolean }) {
|
|
return (
|
|
<NavigationMenu viewport={viewport}>
|
|
<NavigationMenuList>
|
|
<NavigationMenuItem>
|
|
<NavigationMenuTrigger>제품</NavigationMenuTrigger>
|
|
<NavigationMenuContent>
|
|
<ul className="sm:w-[min(360px,calc(100cqw-18px))]" style={{ display: "grid", gap: 4 }}>
|
|
<li>
|
|
<Destination href="#analytics" title="분석" description="노선과 시설물 데이터를 한눈에 확인합니다." />
|
|
</li>
|
|
<li>
|
|
<Destination href="#maps" title="지도" description="공간 정보를 지도 위에서 관리합니다." />
|
|
</li>
|
|
<li>
|
|
<Destination href="#reports" title="리포트" description="점검 결과를 보고서로 만듭니다." />
|
|
</li>
|
|
</ul>
|
|
</NavigationMenuContent>
|
|
</NavigationMenuItem>
|
|
<NavigationMenuItem>
|
|
<NavigationMenuTrigger>자료</NavigationMenuTrigger>
|
|
<NavigationMenuContent>
|
|
<ul className="sm:w-[min(300px,calc(100cqw-18px))]" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 4 }}>
|
|
<li>
|
|
<Destination href="#docs" title="문서" />
|
|
</li>
|
|
<li>
|
|
<Destination href="#guides" title="가이드" />
|
|
</li>
|
|
<li>
|
|
<Destination href="#api" title="API" />
|
|
</li>
|
|
<li>
|
|
<Destination href="#blog" title="블로그" />
|
|
</li>
|
|
</ul>
|
|
</NavigationMenuContent>
|
|
</NavigationMenuItem>
|
|
<NavigationMenuItem>
|
|
<NavigationMenuLink href="#company" active>
|
|
회사
|
|
</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
<NavigationMenuItem>
|
|
<NavigationMenuLink href="#contact">문의</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
<NavigationMenuItem>
|
|
<NavigationMenuTrigger disabled>준비 중</NavigationMenuTrigger>
|
|
</NavigationMenuItem>
|
|
</NavigationMenuList>
|
|
{viewport ? <NavigationMenuIndicator /> : null}
|
|
</NavigationMenu>
|
|
)
|
|
}
|
|
|
|
// Size specimens to their review area (including the panel's 8px padding + 1px border on each side).
|
|
// Mobile uses the content's natural minimum width instead of a fixed desktop specimen width.
|
|
const frame = (node: ReactNode) => (
|
|
<div
|
|
style={{ minHeight: 256, width: "100%", containerType: "inline-size" }}
|
|
className="max-md:[&_[data-viewport=false]_[data-slot=navigation-menu-content]]:absolute"
|
|
>
|
|
{node}
|
|
</div>
|
|
)
|
|
|
|
const meta = { title: "Components/Navigation/Navigation Menu", component: NavigationMenu, parameters: { layout: "padded" } } satisfies Meta<typeof NavigationMenu>
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
export const All: Story = {
|
|
name: "Navigation Menu",
|
|
parameters: { layout: "fullscreen", controls: { disable: true } },
|
|
render: () => (
|
|
<StoryPage>
|
|
<StoryPageHeader title="Navigation Menu" />
|
|
<StorySection title="Overview">
|
|
<StateBoard
|
|
min={520}
|
|
states={[
|
|
{ id: "viewport", label: "Viewport", children: frame(<Specimen />) },
|
|
]}
|
|
/>
|
|
</StorySection>
|
|
<StorySection title="Variants">
|
|
<StateBoard
|
|
min={520}
|
|
states={[
|
|
{ id: "no-viewport", label: "No viewport", children: frame(<Specimen viewport={false} />) },
|
|
]}
|
|
/>
|
|
</StorySection>
|
|
</StoryPage>
|
|
),
|
|
}
|
|
|
|
export const Default: Story = { tags: ["verification"], render: () => frame(<Specimen />) }
|
|
export const NoViewport: Story = { tags: ["verification"], name: "No Viewport", render: () => frame(<Specimen viewport={false} />) }
|
|
|
|
// Three enabled triggers with different widths exercise first / middle / last offsets in both positioning modes.
|
|
export const IndicatorAlignment: Story = {
|
|
tags: ["verification"],
|
|
render: () => (
|
|
<StateBoard min={520} states={[true, false].map((viewport) => ({
|
|
id: viewport ? "viewport" : "no-viewport",
|
|
label: `Indicator alignment · viewport=${viewport}`,
|
|
children: frame(
|
|
<NavigationMenu viewport={viewport}>
|
|
<NavigationMenuList>
|
|
{["제품", "기술 자료", "지원 센터"].map((label) => (
|
|
<NavigationMenuItem key={label}>
|
|
<NavigationMenuTrigger>{label}</NavigationMenuTrigger>
|
|
<NavigationMenuContent><Destination href="#verification" title={`${label} 안내`} /></NavigationMenuContent>
|
|
</NavigationMenuItem>
|
|
))}
|
|
</NavigationMenuList>
|
|
{viewport ? <NavigationMenuIndicator /> : null}
|
|
</NavigationMenu>
|
|
),
|
|
}))} />
|
|
),
|
|
}
|
|
|