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.
111 lines
4.5 KiB
TypeScript
111 lines
4.5 KiB
TypeScript
import { useState } from "react"
|
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuCheckboxItem,
|
|
ContextMenuContent,
|
|
ContextMenuGroup,
|
|
ContextMenuItem,
|
|
ContextMenuLabel,
|
|
ContextMenuRadioGroup,
|
|
ContextMenuRadioItem,
|
|
ContextMenuSeparator,
|
|
ContextMenuShortcut,
|
|
ContextMenuSub,
|
|
ContextMenuSubContent,
|
|
ContextMenuSubTrigger,
|
|
ContextMenuTrigger,
|
|
} from "@hm-design/ui"
|
|
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
|
|
|
// Context Menu opens from pointer context (right click, the context-menu key / Shift+F10, long press) on its trigger area.
|
|
// The representative Story is a target area: right click it. Radix owns keyboard navigation, focus return and Escape.
|
|
|
|
function Full() {
|
|
const [showStatus, setShowStatus] = useState(true)
|
|
const [showPanel, setShowPanel] = useState(false)
|
|
const [position, setPosition] = useState("bottom")
|
|
return (
|
|
<ContextMenu>
|
|
{/* tabIndex lets keyboard users focus the area and open the menu with Shift+F10 / the context-menu key; Radix Trigger is a plain span. */}
|
|
<ContextMenuTrigger tabIndex={0} className="flex h-40 w-80 items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring">
|
|
이 영역에서 오른쪽 클릭
|
|
</ContextMenuTrigger>
|
|
<ContextMenuContent className="w-56">
|
|
<ContextMenuLabel>파일</ContextMenuLabel>
|
|
<ContextMenuGroup>
|
|
<ContextMenuItem>
|
|
열기 <ContextMenuShortcut>⌘O</ContextMenuShortcut>
|
|
</ContextMenuItem>
|
|
<ContextMenuItem>
|
|
이름 바꾸기 <ContextMenuShortcut>F2</ContextMenuShortcut>
|
|
</ContextMenuItem>
|
|
<ContextMenuItem disabled>붙여넣기 (사용 불가)</ContextMenuItem>
|
|
</ContextMenuGroup>
|
|
<ContextMenuSeparator />
|
|
<ContextMenuLabel>보기</ContextMenuLabel>
|
|
<ContextMenuCheckboxItem checked={showStatus} onCheckedChange={setShowStatus}>
|
|
상태 표시줄
|
|
</ContextMenuCheckboxItem>
|
|
<ContextMenuCheckboxItem checked={showPanel} onCheckedChange={setShowPanel}>
|
|
패널
|
|
</ContextMenuCheckboxItem>
|
|
<ContextMenuSeparator />
|
|
<ContextMenuLabel>위치</ContextMenuLabel>
|
|
<ContextMenuRadioGroup value={position} onValueChange={setPosition}>
|
|
<ContextMenuRadioItem value="top">상단</ContextMenuRadioItem>
|
|
<ContextMenuRadioItem value="bottom">하단</ContextMenuRadioItem>
|
|
<ContextMenuRadioItem value="right">오른쪽</ContextMenuRadioItem>
|
|
</ContextMenuRadioGroup>
|
|
<ContextMenuSeparator />
|
|
<ContextMenuSub>
|
|
<ContextMenuSubTrigger>더 보기</ContextMenuSubTrigger>
|
|
<ContextMenuSubContent>
|
|
<ContextMenuItem>이메일로 공유</ContextMenuItem>
|
|
<ContextMenuItem>링크 복사</ContextMenuItem>
|
|
</ContextMenuSubContent>
|
|
</ContextMenuSub>
|
|
<ContextMenuSeparator />
|
|
<ContextMenuItem variant="destructive">삭제</ContextMenuItem>
|
|
</ContextMenuContent>
|
|
</ContextMenu>
|
|
)
|
|
}
|
|
|
|
const meta = { title: "Components/Overlay/Context Menu", component: ContextMenu, parameters: { layout: "centered" } } satisfies Meta<typeof ContextMenu>
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
// Representative Story: right click the area to open the full menu (item, disabled, separator, shortcut, checkbox, radio group, sub menu, destructive).
|
|
export const All: Story = {
|
|
name: "Context Menu",
|
|
parameters: { layout: "fullscreen", controls: { disable: true } },
|
|
render: () => (
|
|
<StoryPage>
|
|
<StoryPageHeader title="Context Menu" />
|
|
<StorySection title="Overview">
|
|
<Full />
|
|
</StorySection>
|
|
</StoryPage>
|
|
),
|
|
}
|
|
|
|
// Verification Story: opens the menu at the area center on load (synthetic contextmenu event), hidden from the sidebar (tag `verification`).
|
|
export const Open: Story = {
|
|
tags: ["verification"],
|
|
parameters: { layout: "fullscreen" },
|
|
render: () => (
|
|
<div className="flex min-h-[560px] justify-center p-8">
|
|
<Full />
|
|
</div>
|
|
),
|
|
play: async ({ canvasElement }) => {
|
|
const area = canvasElement.querySelector<HTMLElement>("[data-slot='context-menu-trigger']")
|
|
if (!area) return
|
|
const r = area.getBoundingClientRect()
|
|
area.dispatchEvent(
|
|
new MouseEvent("contextmenu", { bubbles: true, cancelable: true, clientX: r.left + r.width / 2, clientY: r.top + r.height / 2, button: 2 }),
|
|
)
|
|
},
|
|
}
|