Files
shkim4 1472e51f47 feat(sw): integrate home menu into SWHeader (Task A + B)
Combine Task A (e8409ef, SW 0.2.0 integrated improvement) with Task B
(396539d, Figma-based SWHomeMenu / SWHomeMenuSection / SWHomeMenuItem /
SWRecentFiles). Task B's 84 new files are taken unchanged except for the
public-import Storybook update and joining the popup Escape stack.

- Export the home menu from @hm-design/sw
- SWHeader homeMenu / homeMenuOpen / defaultHomeMenuOpen /
  onHomeMenuOpenChange: hidden by default, the logo toggles it (onHomeClick
  is not called while homeMenu is set), placed from the measured header,
  closes with / closes the document and field dropdowns
- Logo aria-expanded / aria-controls; menu layer CSS (z-index 20)
- AI catalog / patterns / design rules, example G, README, usage guide
- Storybook SW/Home Menu uses @hm-design/sw; integration story

Verified: SW typecheck, tsc -b, SW build, Storybook build, AI docs 22/22,
28/28 existing SW shots pixel-identical, regression 96 PASS / 0 FAIL,
home menu checks 22/22. Not run: tarball consumer install.
2026-10-19 15:39:47 +09:00

63 lines
3.5 KiB
TypeScript

// Example G — the logo (home) menu (Figma 26:10591): SWHeader `homeMenu` + sections data + the optional recent
// files panel. Hidden by default; a logo click opens it, a second click closes it. New / Open stay in the
// document dropdown. Every text, icon and action is the program's data; the package ships none.
import { useState } from "react"
import {
SWHeader, SWRecentFiles,
type SWFileEntry, type SWHomeMenuItemData, type SWHomeMenuSectionData,
} from "@hm-design/sw"
import { documents } from "./sample-data"
// One currentColor icon per tile follows the state colors (Default #B6B4AF, Hover / Active #FF5C00).
const Icon = ({ d }: { d: string }) => (
<svg viewBox="0 0 18 18" width="18" height="18" aria-hidden="true"><path d={d} fill="none" stroke="currentColor" strokeWidth="1.6" /></svg>
)
const recentFiles: SWFileEntry[] = [
{ id: "r1", name: "평면도_A.dwg", path: "D:\\Projects\\A" },
{ id: "r2", name: "종단면도_B.dwg", path: "D:\\Projects\\B" },
]
export function ExampleGHomeMenu({ onEvent }: { onEvent?: (name: string) => void }) {
const [activeDocumentId, setActiveDocumentId] = useState(documents[0].id)
const [extra, setExtra] = useState<SWHomeMenuItemData[]>([])
// The same handlers as the document dropdown: the menu adds entry points, never a second implementation.
const openFile = () => onEvent?.("document:open")
const newFile = () => onEvent?.("document:new")
const sections: SWHomeMenuSectionData[] = [
{ id: "working", title: "작업중인 파일", items: [
{ id: "save", label: "저장", icon: <Icon d="M3 3h10l2 2v10H3z M6 3v4h6V3" />, toggle: false, onSelect: () => onEvent?.("menu:save") },
{ id: "export", label: "내보내기", icon: <Icon d="M9 12V3 M5 7l4-4 4 4 M3 15h12" />, toggle: false, onSelect: () => onEvent?.("menu:export") },
] },
{ id: "open", title: "다른 파일 열기", items: [
{ id: "find-open", label: "찾아열기", icon: <Icon d="M3 5h5l2 2h5v8H3z" />, toggle: false, onSelect: openFile },
// Optional sub panel: the tile toggles it (Active = open), the menu shows the outline accent meanwhile.
{ id: "recent", label: "최근파일", icon: <Icon d="M9 4v5l3 2 M9 16A7 7 0 1 0 9 2a7 7 0 0 0 0 14" />,
panel: <SWRecentFiles files={recentFiles} onFileOpen={file => onEvent?.(`recent:${file.id}`)} />, panelLabel: "최근파일" },
{ id: "new-file", label: "새파일", icon: <Icon d="M4 2h7l3 3v11H4z M9 8v6 M6 11h6" />, toggle: false, onSelect: newFile },
...extra,
] },
]
return (
<>
<SWHeader
documentTitle={documents.find(item => item.id === activeDocumentId)?.name ?? ""}
logoAlt="예제 SW"
documents={documents}
activeDocumentId={activeDocumentId}
onDocumentSelect={document => { setActiveDocumentId(document.id); onEvent?.(`document:${document.id}`) }}
onDocumentNew={newFile}
onDocumentOpen={openFile}
homeMenu={{ sections, onItemSelect: event => onEvent?.(`menu:select:${event.item.id}:${event.active}`) }}
onHomeMenuOpenChange={(open, reason) => onEvent?.(`menu:${open ? "open" : "close"}:${reason}`)}
/>
{/* Adding an item is a data change only: width, dividers and corners follow the new item count. */}
<button type="button" style={{ marginTop: 40 }} onClick={() => setExtra(list => [...list, { id: `extra-${list.length + 1}`, label: `추가 ${list.length + 1}`, icon: <Icon d="M9 3v12 M3 9h12" /> }])}>
메뉴 항목 추가
</button>
</>
)
}