Initial commit: HMDesignSystem baseline
This commit is contained in:
commit
b6a0a61103
486 files changed
+68432
No files matched your search
@@ -0,0 +1,169 @@
|
||||
import { useState } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react"
|
||||
import { ToggleGroup, ToggleGroupItem } from "@hm-design/ui"
|
||||
import { StateBoard } from "@/components/story-board"
|
||||
import { STORY_KICKER, StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
|
||||
// Toggle Group is a group of pressed-state items (toolbar-style), not a content switcher (that is Tabs). The representative Story
|
||||
// shows single / multiple / icon-only / disabled / vertical / spaced groups with real controls; the same specimens also exist as
|
||||
// Stories tagged `verification` (hidden from the sidebar). Icon-only items carry an `aria-label`.
|
||||
|
||||
function Single() {
|
||||
const [value, setValue] = useState("left")
|
||||
return (
|
||||
<div className="grid gap-2">
|
||||
<ToggleGroup type="single" variant="outline" value={value} onValueChange={(v) => v && setValue(v)} aria-label="정렬">
|
||||
<ToggleGroupItem value="left">왼쪽</ToggleGroupItem>
|
||||
<ToggleGroupItem value="center">가운데</ToggleGroupItem>
|
||||
<ToggleGroupItem value="right">오른쪽</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
<span className="text-sm text-muted-foreground" data-testid="single">{`value = ${value}`}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Multiple() {
|
||||
const [value, setValue] = useState<string[]>(["bold"])
|
||||
return (
|
||||
<div className="grid gap-2">
|
||||
<ToggleGroup type="multiple" variant="outline" value={value} onValueChange={setValue} aria-label="서식">
|
||||
<ToggleGroupItem value="bold" aria-label="굵게">
|
||||
<BoldIcon />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="italic" aria-label="기울임">
|
||||
<ItalicIcon />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="underline" aria-label="밑줄">
|
||||
<UnderlineIcon />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
<span className="text-sm text-muted-foreground" data-testid="multiple">{`value = [${value.join(", ")}]`}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Disabled() {
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
<ToggleGroup type="single" variant="outline" defaultValue="a" disabled aria-label="비활성 그룹">
|
||||
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
||||
<ToggleGroupItem value="b">B</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
<ToggleGroup type="single" variant="outline" defaultValue="a" aria-label="비활성 항목">
|
||||
<ToggleGroupItem value="a">A</ToggleGroupItem>
|
||||
<ToggleGroupItem value="b" disabled>
|
||||
B (비활성)
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="c">C</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Vertical() {
|
||||
return (
|
||||
<ToggleGroup type="single" variant="outline" orientation="vertical" defaultValue="left" aria-label="세로 정렬">
|
||||
<ToggleGroupItem value="left" aria-label="왼쪽">
|
||||
<AlignLeftIcon />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="center" aria-label="가운데">
|
||||
<AlignCenterIcon />
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="right" aria-label="오른쪽">
|
||||
<AlignRightIcon />
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
)
|
||||
}
|
||||
|
||||
function Spaced() {
|
||||
return (
|
||||
<ToggleGroup type="multiple" variant="outline" spacing={2} aria-label="간격 있는 그룹">
|
||||
<ToggleGroupItem value="a">첫째</ToggleGroupItem>
|
||||
<ToggleGroupItem value="b">둘째</ToggleGroupItem>
|
||||
<ToggleGroupItem value="c">셋째</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
)
|
||||
}
|
||||
|
||||
function Many() {
|
||||
return (
|
||||
<ToggleGroup type="multiple" variant="outline" spacing={1} className="flex-wrap" aria-label="많은 항목">
|
||||
{Array.from({ length: 10 }, (_, i) => (
|
||||
<ToggleGroupItem key={i} value={`${i}`}>{`옵션 ${i + 1}`}</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
)
|
||||
}
|
||||
|
||||
function Sizes() {
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
{(["sm", "default", "lg"] as const).map((size) => (
|
||||
<ToggleGroup key={size} type="multiple" size={size} variant="outline" aria-label={`${size} content kinds`}>
|
||||
<ToggleGroupItem value="icon" aria-label={`${size} icon-only`}><BoldIcon /></ToggleGroupItem>
|
||||
<ToggleGroupItem value="mixed"><BoldIcon />굵게</ToggleGroupItem>
|
||||
<ToggleGroupItem value="text">{size}</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const sections = [
|
||||
{ id: "single", label: "Single selection", node: <Single /> },
|
||||
{ id: "multiple", label: "Multiple selection (icon-only)", node: <Multiple /> },
|
||||
{ id: "sizes", label: "Sizes · Icon-only / Icon + Text / Text-only", node: <Sizes /> },
|
||||
{ id: "disabled", label: "Disabled group / item", node: <Disabled /> },
|
||||
{ id: "vertical", label: "Vertical", node: <Vertical /> },
|
||||
{ id: "spaced", label: "spacing={2} (separated)", node: <Spaced /> },
|
||||
{ id: "many", label: "Many items (spacing + flex-wrap, consumer opts in)", node: <Many /> },
|
||||
]
|
||||
|
||||
// No `component`: ToggleGroup props are a union (single | multiple), so Stories without args would not type-check.
|
||||
const meta = { title: "Components/Form/Toggle Group", parameters: { layout: "padded" } } satisfies Meta
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
const board = (...ids: string[]) => (
|
||||
<StateBoard min={360} states={sections.filter((s) => ids.includes(s.id)).map((s) => ({ id: s.id, label: s.label, children: s.node }))} />
|
||||
)
|
||||
|
||||
// Representative Story: the common Story page; the sections group the same specimens.
|
||||
export const All: Story = {
|
||||
name: "Toggle Group",
|
||||
parameters: { layout: "fullscreen", controls: { disable: true } },
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader
|
||||
kicker={STORY_KICKER.components}
|
||||
title="Toggle Group"
|
||||
description="눌림 상태를 갖는 항목 묶음(툴바형)입니다. 내용을 전환하는 용도는 Tabs를 씁니다."
|
||||
/>
|
||||
<StorySection title="Overview">{board("single")}</StorySection>
|
||||
<StorySection title="Sizes" description="Icon-only / Icon + Text / Text-only.">
|
||||
<Sizes />
|
||||
</StorySection>
|
||||
<StorySection title="Variants" description="다중 선택, 세로 방향, 항목 간격(spacing)입니다.">{board("multiple", "vertical", "spaced")}</StorySection>
|
||||
<StorySection title="States">{board("disabled")}</StorySection>
|
||||
<StorySection title="Usage">{board("many")}</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
|
||||
export const SingleSelection: Story = { tags: ["verification"], name: "Single", render: () => <Single /> }
|
||||
export const MultipleSelection: Story = { tags: ["verification"], name: "Multiple", render: () => <Multiple /> }
|
||||
export const VerticalGroup: Story = { tags: ["verification"], name: "Vertical", render: () => <Vertical /> }
|
||||
|
||||
export const ContentNodes: Story = {
|
||||
tags: ["verification"],
|
||||
render: () => (
|
||||
<ToggleGroup type="multiple" variant="outline" aria-label="Content nodes">
|
||||
<ToggleGroupItem value="array" aria-label="Array icon">{[<BoldIcon key="icon" />]}</ToggleGroupItem>
|
||||
<ToggleGroupItem value="fragment" aria-label="Fragment icon"><><BoldIcon /></></ToggleGroupItem>
|
||||
<ToggleGroupItem value="mixed"><><BoldIcon />굵게</></ToggleGroupItem>
|
||||
<ToggleGroupItem value="span"><BoldIcon /><span>굵게</span></ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
),
|
||||
}
|
||||
Reference in new issue
Block a user