import { useState, type ReactNode } from "react" import type { Meta, StoryObj } from "@storybook/react-vite" // Public API only, like a consuming project. import { SWHeader, SWHeaderPropertyBar, SWHomeMenu, SWHomeMenuItem, SWHomeMenuSection, SWPopup, SWPopupRow, SWPopupSection, SWRecentFiles, SWTheme, type SWHeaderProperty, type SWHomeMenuAccent, type SWHomeMenuItemData, type SWHomeMenuSectionData, type SWHomeMenuSelectEvent, } from "@hm-design/sw" import "@hm-design/sw/styles.css" import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page" import "./sw-home-menu.css" import { figmaHomeMenuSections, figmaRecentFiles, withRecentFiles, } from "./sw-home-menu-fixture" import placeholderLogo from "./assets/sw-header-logo-placeholder.svg" import figmaPropertyBar from "./assets/home-menu/figma-26-10492.png" // Owner: docs/sw/figma-additional/analysis-report.md (Figma rj6wpsfk24lOWvWiVRAANP 26:10591 / 662:67267). // 2026-10-19: the former SW/Home Menu Stories live under SW/Header (SW normal mode only; SW/Viewer has no logo menu). // "Home Menu" is the sidebar Story (단독 사용 hidden 2026-10-21); the other Stories keep their checks reachable // by URL (verification tag). 2026-10-20: Figma screenshot comparisons that no test or document used were removed; // the Figma originals stay in docs/sw/figma-additional/screenshots/. const KICKER = "HM DESIGN SYSTEM / SW" const meta = { title: "SW/Header", parameters: { layout: "fullscreen", controls: { disable: true } }, } satisfies Meta export default meta type Story = StoryObj const headerArgs = { logoSrc: placeholderLogo, logoAlt: "임시 로고", documentTitle: "C0020101-006-평면및종단면도(06)", drawingModeLabel: "그리기모드", layer: { name: "0_Layer_Cludmark" }, color: { value: "255,229,0", swatch: "#ffe500" }, lineStyle: { name: "Continuous", weight: "0.25", scale: "1.00" }, textStyle: { font: "Arial", name: "Standard" }, dimensionStyle: { name: "Standard" }, } const documents = [{ id: "doc-1", name: "C0020101-006-평면및종단면도(06)" }, { id: "doc-2", name: "C0020101-007-횡단면도" }] function useLog() { const [entries, setEntries] = useState([]) return { entries, record: (entry: string) => setEntries(list => [entry, ...list].slice(0, 8)) } } function Log({ entries }: { entries: string[] }) { return
    {entries.length ? entries.map((entry, index) =>
  1. {entry}
  2. ) :
  3. 이벤트 없음
  4. }
} const describe = (event: SWHomeMenuSelectEvent) => `onItemSelect(${event.section?.id ?? "-"} / ${event.item.id}, active=${event.active})` /** * SWHeader `homeMenu`: the logo toggles the menu (no onHomeClick), SWHeader places it from the measured header * (object area − 2px to the window area; Figma x 375 → 1680 at 1920). New / Open stay in the document dropdown. * `autoFocus={false}`: a menu open on load does not take focus (several menus share the Home Menu page). */ function HeaderWithMenu({ width, defaultOpen = false, defaultActiveIds = [], accent, recent = true, autoFocus, children }: { width?: number; defaultOpen?: boolean; defaultActiveIds?: string[]; accent?: SWHomeMenuAccent; recent?: boolean; autoFocus?: boolean children?: (log: ReactNode) => ReactNode }) { const [open, setOpen] = useState(defaultOpen) const [activeIds, setActiveIds] = useState(defaultActiveIds) const { entries, record } = useLog() const [sections] = useState(() => recent ? withRecentFiles(figmaHomeMenuSections, record(`onFileOpen(${file.id})`)} />) : figmaHomeMenuSections) return ( <>
record("onDocumentNew")} onDocumentOpen={() => record("onDocumentOpen")} onHomeClick={() => record("onHomeClick (homeMenu 사용 중에는 호출되지 않음)")} homeMenuOpen={open} onHomeMenuOpenChange={(next, reason) => { setOpen(next); record(`onHomeMenuOpenChange(${next}, ${reason})`) }} homeMenu={{ sections, accent, activeIds, autoFocus, onActiveChange: next => { setActiveIds(next); record(`onActiveChange([${next.join(", ")}])`) }, onItemSelect: event => record(describe(event)), }} />
{children?.()} ) } /** The sidebar Story: the logo menu of SWHeader (SW normal mode) with its Figma states, sub panel and Escape stack. */ export const HomeMenu: Story = { name: "Home Menu", render: () => ( {log => log}
{log => log}
), } /** 1:1 capture canvas at the Figma 1920 x 77 frame (no story page chrome); home-menu-integration.browser.mjs measures it. */ function FigmaCanvas() { return (
{/* The integrated placement (measured by SWHeader), not a fixed x: it must land on Figma x 375. */}
) } export const HomeMenuCaptureDefault: Story = { name: "Home Menu · 비교 캔버스 · Default", tags: ["!autodocs", "verification"], render: () => } export const HomeMenuHeaderComposition: Story = { name: "Home Menu · Header 조합 · 로고 클릭", tags: ["verification"], render: () => ( {log => log} ), } function DynamicDemo() { const [sections, setSections] = useState(figmaHomeMenuSections) const [selection, setSelection] = useState<"multiple" | "single">("multiple") const [accent, setAccent] = useState("auto") const [activeIds, setActiveIds] = useState([]) const [count, setCount] = useState(1) const { entries, record } = useLog() const update = (sectionId: string, change: (items: readonly SWHomeMenuItemData[]) => SWHomeMenuItemData[]) => setSections(list => list.map(section => section.id === sectionId ? { ...section, items: change(section.items) } : section)) const toggleItem = (id: string, key: "hidden" | "disabled") => setSections(list => list.map(section => ({ ...section, items: section.items.map(item => item.id === id ? { ...item, [key]: !item[key] } : item), }))) const mono = return ( <>
{ setActiveIds(next); record(`onActiveChange([${next.join(", ")}])`) }} onItemSelect={event => record(describe(event))} /> ) } export const HomeMenuDynamicData: Story = { name: "Home Menu · Props · 데이터 변경", tags: ["verification"], render: () => ( ), } function StandaloneDemo() { const [active, setActive] = useState(false) const { entries, record } = useLog() const mono = return ( <> record(describe(event))}>
{ setActive(next); record(`onActiveChange(${next})`) }} />
) } export const HomeMenuStandalone: Story = { name: "Home Menu · 단독 사용 · 조합형", // 2026-10-21 SW Storybook menu cleanup: hidden from the sidebar; ID and direct URL kept for the existing checks. tags: ["verification"], render: () => ( ), } // Property bar check (Figma 26:10492) against the current SWHeaderPropertyBar. Values are the Figma sample; the // bar itself is unchanged (3 differences, awaiting approval: home-menu-integration report §속성Bar). const barProperties: SWHeaderProperty[] = [ { id: "radius", name: "반지름", value: "4.0526", type: "number", groupId: "bar" }, { id: "diameter", name: "지름", value: "8.1052", type: "number", groupId: "bar" }, { id: "circumference", name: "원주", value: "8.1052", type: "number", groupId: "bar" }, { id: "area", name: "면적", value: "51.5962", type: "number", groupId: "bar" }, ] function PropertyBarCompare() { const [values, setValues] = useState(barProperties) return ( setValues(list => list.map(item => item.id === property.id ? { ...item, value } : item))} /> ) } export const HomeMenuPropertyBarCheck: Story = { name: "Home Menu · 속성Bar 비교 (26:10492)", tags: ["verification"], render: () => (
Figma 26:10492
), } export const HomeMenuCapturePropertyBar: Story = { name: "Home Menu · 비교 캔버스 · 속성Bar", tags: ["!autodocs", "verification"], render: () => {}} />, } /** Integration: logo menu + property window + SWPopup at the same time (Escape stack and focus). */ function IntegrationDemo() { const [open, setOpen] = useState(false) const [panelOpen, setPanelOpen] = useState(false) const [popupOpen, setPopupOpen] = useState(false) const { entries, record } = useLog() const [sections] = useState(() => withRecentFiles(figmaHomeMenuSections, record(`onFileOpen(${file.id})`)} />)) return ( <>
record("onDocumentNew")} onDocumentOpen={() => record("onDocumentOpen")} homeMenuOpen={open} onHomeMenuOpenChange={(next, reason) => { setOpen(next); record(`onHomeMenuOpenChange(${next}, ${reason})`) }} homeMenu={{ sections, onItemSelect: event => record(describe(event)) }} propertyPanelOpen={panelOpen} onPropertyPanelOpenChange={next => { setPanelOpen(next); record(`onPropertyPanelOpenChange(${next})`) }} propertyPanel={{ selection: { id: "none", title: "선택된 객체 없음", kind: "none" } }} /> {popupOpen && ( { setPopupOpen(false); record("SWPopup onClose") }}> 값 )}
) } export const HomeMenuIntegration: Story = { name: "Home Menu · 통합 · Popup · Escape", tags: ["verification"], render: () => ( ), } /** Responsive check: the header at the viewport width with the menu open (no story page chrome). */ export const HomeMenuCaptureResponsive: Story = { name: "Home Menu · 비교 캔버스 · 반응형", tags: ["!autodocs", "verification"], render: () => }