Initial commit: HMDesignSystem baseline
This commit is contained in:
commit
b6a0a61103
486 files changed
+68432
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import { useState } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import { BoldIcon, ItalicIcon, StarIcon } from "lucide-react"
|
||||
import { Toggle } from "@hm-design/ui"
|
||||
import { StateBoard } from "@/components/story-board"
|
||||
import { STORY_KICKER, StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
|
||||
// Toggle is a persistent pressed-state control. The representative Story shows each state with real, clickable toggles; the same
|
||||
// specimens also exist as Stories tagged `verification` (hidden from the sidebar). Icon-only toggles carry an `aria-label`.
|
||||
|
||||
function OffOn() {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Toggle>Off</Toggle>
|
||||
<Toggle defaultPressed>On</Toggle>
|
||||
<Toggle variant="outline">Outline off</Toggle>
|
||||
<Toggle variant="outline" defaultPressed>
|
||||
Outline on
|
||||
</Toggle>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function WithIcon() {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Toggle aria-label="굵게">
|
||||
<BoldIcon />
|
||||
</Toggle>
|
||||
<Toggle aria-label="기울임" defaultPressed>
|
||||
<ItalicIcon />
|
||||
</Toggle>
|
||||
<Toggle variant="outline">
|
||||
<StarIcon />
|
||||
즐겨찾기
|
||||
</Toggle>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Sizes() {
|
||||
return (
|
||||
<div className="grid gap-3">
|
||||
{(["sm", "default", "lg"] as const).map((size) => (
|
||||
<div key={size} data-toggle-size={size} className="flex flex-wrap items-center gap-3">
|
||||
<Toggle size={size} variant="outline" aria-label={`${size} icon-only`}><StarIcon /></Toggle>
|
||||
<Toggle size={size} variant="outline"><StarIcon />즐겨찾기</Toggle>
|
||||
<Toggle size={size} variant="outline">{size}</Toggle>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Disabled() {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Toggle disabled>Disabled off</Toggle>
|
||||
<Toggle disabled defaultPressed>
|
||||
Disabled on
|
||||
</Toggle>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Controlled() {
|
||||
const [pressed, setPressed] = useState(false)
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<Toggle variant="outline" pressed={pressed} onPressedChange={setPressed}>
|
||||
알림
|
||||
</Toggle>
|
||||
<span className="text-sm text-muted-foreground" data-testid="controlled">{`pressed = ${String(pressed)}`}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// className extension: the consumer's width / rounded utilities win over the default look.
|
||||
function Extended() {
|
||||
return (
|
||||
<Toggle variant="outline" className="w-40 rounded-full">
|
||||
w-40 rounded-full
|
||||
</Toggle>
|
||||
)
|
||||
}
|
||||
|
||||
const sections = [
|
||||
{ id: "offon", label: "Off / On", node: <OffOn /> },
|
||||
{ id: "icon", label: "Icon-only and icon + text", node: <WithIcon /> },
|
||||
{ id: "sizes", label: "Sizes", node: <Sizes /> },
|
||||
{ id: "disabled", label: "Disabled", node: <Disabled /> },
|
||||
{ id: "controlled", label: "Controlled", node: <Controlled /> },
|
||||
{ id: "extended", label: "className extension", node: <Extended /> },
|
||||
]
|
||||
|
||||
const meta = { title: "Components/Form/Toggle", component: Toggle, parameters: { layout: "padded" } } satisfies Meta<typeof Toggle>
|
||||
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",
|
||||
parameters: { layout: "fullscreen", controls: { disable: true } },
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader
|
||||
kicker={STORY_KICKER.components}
|
||||
title="Toggle"
|
||||
description="눌림(On) 상태를 유지하는 단일 버튼입니다."
|
||||
/>
|
||||
<StorySection title="Overview">{board("offon")}</StorySection>
|
||||
<StorySection title="Sizes">
|
||||
<Sizes />
|
||||
</StorySection>
|
||||
<StorySection title="Variants" description="아이콘만, 아이콘 + 텍스트입니다.">{board("icon")}</StorySection>
|
||||
<StorySection title="States">{board("disabled", "controlled")}</StorySection>
|
||||
<StorySection title="Usage" description="className 확장입니다.">{board("extended")}</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
|
||||
export const States: Story = { tags: ["verification"], render: () => <OffOn /> }
|
||||
export const IconOnly: Story = { tags: ["verification"], name: "Icon Only", render: () => <WithIcon /> }
|
||||
export const DisabledToggle: Story = { tags: ["verification"], name: "Disabled", render: () => <Disabled /> }
|
||||
|
||||
export const ContentNodes: Story = {
|
||||
tags: ["verification"],
|
||||
render: () => (
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Toggle aria-label="Array icon">{[<StarIcon key="icon" />]}</Toggle>
|
||||
<Toggle aria-label="Fragment icon"><><StarIcon /></></Toggle>
|
||||
<Toggle><><StarIcon />즐겨찾기</></Toggle>
|
||||
<Toggle><StarIcon /><span>즐겨찾기</span></Toggle>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
Reference in new issue
Block a user