@conscience-technology/ctds 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -2
- package/assets/components/AppFrame/AppFrame.module.css +177 -0
- package/assets/components/AppFrame/AppFrame.tsx +93 -0
- package/assets/components/AppFrame/index.ts +6 -0
- package/assets/components/AttachmentChip/AttachmentChip.module.css +71 -0
- package/assets/components/AttachmentChip/AttachmentChip.tsx +74 -0
- package/assets/components/AttachmentChip/index.ts +2 -0
- package/assets/components/AttachmentThumb/AttachmentThumb.module.css +107 -0
- package/assets/components/AttachmentThumb/AttachmentThumb.tsx +91 -0
- package/assets/components/AttachmentThumb/index.ts +2 -0
- package/assets/components/Avatar/Avatar.module.css +167 -0
- package/assets/components/Avatar/Avatar.tsx +228 -0
- package/assets/components/Avatar/index.ts +2 -0
- package/assets/components/AvatarUpload/AvatarUpload.module.css +130 -0
- package/assets/components/AvatarUpload/AvatarUpload.tsx +139 -0
- package/assets/components/AvatarUpload/index.ts +2 -0
- package/assets/components/Badge/Badge.module.css +76 -0
- package/assets/components/Badge/Badge.tsx +68 -0
- package/assets/components/Badge/index.ts +2 -0
- package/assets/components/Breadcrumb/Breadcrumb.module.css +213 -0
- package/assets/components/Breadcrumb/Breadcrumb.tsx +279 -0
- package/assets/components/Breadcrumb/index.ts +8 -0
- package/assets/components/Button/Button.module.css +216 -0
- package/assets/components/Button/Button.tsx +149 -0
- package/assets/components/Button/index.ts +2 -0
- package/assets/components/Callout/Callout.module.css +146 -0
- package/assets/components/Callout/Callout.tsx +138 -0
- package/assets/components/Callout/index.ts +2 -0
- package/assets/components/ChatInput/ChatInput.module.css +241 -0
- package/assets/components/ChatInput/ChatInput.tsx +322 -0
- package/assets/components/ChatInput/ModelSelector.tsx +40 -0
- package/assets/components/ChatInput/SendButton.tsx +41 -0
- package/assets/components/ChatInput/index.ts +10 -0
- package/assets/components/Checkbox/Checkbox.module.css +66 -0
- package/assets/components/Checkbox/Checkbox.tsx +36 -0
- package/assets/components/Checkbox/index.ts +2 -0
- package/assets/components/Chip/Chip.module.css +128 -0
- package/assets/components/Chip/Chip.tsx +103 -0
- package/assets/components/Chip/index.ts +2 -0
- package/assets/components/ChoiceCard/ChoiceCard.module.css +142 -0
- package/assets/components/ChoiceCard/ChoiceCard.tsx +132 -0
- package/assets/components/ChoiceCard/index.ts +2 -0
- package/assets/components/ContextMenu/ContextMenu.module.css +102 -0
- package/assets/components/ContextMenu/ContextMenu.tsx +392 -0
- package/assets/components/ContextMenu/index.ts +9 -0
- package/assets/components/DatePicker/Calendar.tsx +234 -0
- package/assets/components/DatePicker/DESIGN_NOTES.md +106 -0
- package/assets/components/DatePicker/DESIGN_NOTES_TIME.md +125 -0
- package/assets/components/DatePicker/DatePicker.module.css +295 -0
- package/assets/components/DatePicker/DatePicker.tsx +387 -0
- package/assets/components/DatePicker/TimeSelector.tsx +69 -0
- package/assets/components/DatePicker/index.ts +8 -0
- package/assets/components/DatePicker/presets.ts +80 -0
- package/assets/components/DatePicker/utils.ts +148 -0
- package/assets/components/Disclosure/Disclosure.module.css +73 -0
- package/assets/components/Disclosure/Disclosure.tsx +129 -0
- package/assets/components/Disclosure/index.ts +6 -0
- package/assets/components/Drawer/Drawer.module.css +257 -0
- package/assets/components/Drawer/Drawer.tsx +126 -0
- package/assets/components/Drawer/index.ts +7 -0
- package/assets/components/EmptyState/EmptyState.module.css +102 -0
- package/assets/components/EmptyState/EmptyState.tsx +80 -0
- package/assets/components/EmptyState/index.ts +2 -0
- package/assets/components/FileIcon/FileIcon.module.css +66 -0
- package/assets/components/FileIcon/FileIcon.tsx +84 -0
- package/assets/components/FileIcon/fileExtMap.ts +112 -0
- package/assets/components/FileIcon/index.ts +9 -0
- package/assets/components/FilterButton/FilterButton.module.css +130 -0
- package/assets/components/FilterButton/FilterButton.tsx +94 -0
- package/assets/components/FilterButton/index.ts +2 -0
- package/assets/components/GalleryItem/GalleryItem.module.css +220 -0
- package/assets/components/GalleryItem/GalleryItem.tsx +163 -0
- package/assets/components/GalleryItem/index.ts +7 -0
- package/assets/components/Icon/Icon.tsx +616 -0
- package/assets/components/Icon/context.tsx +39 -0
- package/assets/components/Icon/index.ts +4 -0
- package/assets/components/IconButton/IconButton.module.css +109 -0
- package/assets/components/IconButton/IconButton.tsx +110 -0
- package/assets/components/IconButton/index.ts +2 -0
- package/assets/components/Input/Input.module.css +338 -0
- package/assets/components/Input/Input.tsx +371 -0
- package/assets/components/Input/index.ts +2 -0
- package/assets/components/ListItem/ListItem.module.css +318 -0
- package/assets/components/ListItem/ListItem.tsx +262 -0
- package/assets/components/ListItem/index.ts +9 -0
- package/assets/components/MenuItem/MenuItem.module.css +437 -0
- package/assets/components/MenuItem/MenuItem.tsx +529 -0
- package/assets/components/MenuItem/index.ts +8 -0
- package/assets/components/MetricCard/MetricCard.module.css +153 -0
- package/assets/components/MetricCard/MetricCard.tsx +140 -0
- package/assets/components/MetricCard/index.ts +2 -0
- package/assets/components/Modal/Modal.module.css +175 -0
- package/assets/components/Modal/Modal.tsx +98 -0
- package/assets/components/Modal/index.ts +2 -0
- package/assets/components/Onboarding/DocsIndex.tsx +82 -0
- package/assets/components/Onboarding/LayoutBuilder.tsx +160 -0
- package/assets/components/Onboarding/Onboarding.module.css +1394 -0
- package/assets/components/Onboarding/OnboardingAxisPanel.tsx +515 -0
- package/assets/components/Onboarding/OnboardingPage.tsx +1193 -0
- package/assets/components/Onboarding/PresetsGallery.tsx +65 -0
- package/assets/components/Onboarding/actions.ts +250 -0
- package/assets/components/Onboarding/index.ts +8 -0
- package/assets/components/Onboarding/palette.css +40 -0
- package/assets/components/Onboarding/wizardData.ts +265 -0
- package/assets/components/Pagination/Pagination.module.css +82 -0
- package/assets/components/Pagination/Pagination.tsx +248 -0
- package/assets/components/Pagination/index.ts +6 -0
- package/assets/components/ProgressBar/ProgressBar.module.css +64 -0
- package/assets/components/ProgressBar/ProgressBar.tsx +54 -0
- package/assets/components/ProgressBar/index.ts +2 -0
- package/assets/components/QuotationBar/QuotationBar.module.css +61 -0
- package/assets/components/QuotationBar/QuotationBar.tsx +48 -0
- package/assets/components/QuotationBar/index.ts +2 -0
- package/assets/components/Radio/Radio.module.css +63 -0
- package/assets/components/Radio/Radio.tsx +33 -0
- package/assets/components/Radio/index.ts +2 -0
- package/assets/components/Select/Select.module.css +151 -0
- package/assets/components/Select/Select.tsx +425 -0
- package/assets/components/Select/index.ts +10 -0
- package/assets/components/SelectableCard/SelectableCard.module.css +177 -0
- package/assets/components/SelectableCard/SelectableCard.tsx +83 -0
- package/assets/components/SelectableCard/index.ts +2 -0
- package/assets/components/Shortcut/Shortcut.module.css +56 -0
- package/assets/components/Shortcut/Shortcut.tsx +156 -0
- package/assets/components/Shortcut/index.ts +2 -0
- package/assets/components/Sidebar/Sidebar.module.css +242 -0
- package/assets/components/Sidebar/Sidebar.tsx +264 -0
- package/assets/components/Sidebar/index.ts +7 -0
- package/assets/components/SidebarSplit/SidebarSplit.module.css +126 -0
- package/assets/components/SidebarSplit/SidebarSplit.tsx +222 -0
- package/assets/components/SidebarSplit/index.ts +6 -0
- package/assets/components/Slider/Slider.module.css +120 -0
- package/assets/components/Slider/Slider.tsx +88 -0
- package/assets/components/Slider/index.ts +2 -0
- package/assets/components/SplitButton/SplitButton.module.css +190 -0
- package/assets/components/SplitButton/SplitButton.tsx +145 -0
- package/assets/components/SplitButton/index.ts +6 -0
- package/assets/components/Stepper/Stepper.module.css +182 -0
- package/assets/components/Stepper/Stepper.tsx +185 -0
- package/assets/components/Stepper/index.ts +2 -0
- package/assets/components/Switch/Switch.module.css +116 -0
- package/assets/components/Switch/Switch.tsx +58 -0
- package/assets/components/Switch/index.ts +2 -0
- package/assets/components/TableCell/TableCell.module.css +137 -0
- package/assets/components/TableCell/TableCellAvatar.tsx +42 -0
- package/assets/components/TableCell/TableCellBadge.tsx +23 -0
- package/assets/components/TableCell/TableCellIcon.tsx +39 -0
- package/assets/components/TableCell/TableCellImage.tsx +49 -0
- package/assets/components/TableCell/TableCellLink.tsx +33 -0
- package/assets/components/TableCell/TableCellMeter.tsx +55 -0
- package/assets/components/TableCell/TableCellNumber.tsx +43 -0
- package/assets/components/TableCell/TableCellText.tsx +30 -0
- package/assets/components/TableCell/index.ts +23 -0
- package/assets/components/TableHeader/TableHeader.module.css +287 -0
- package/assets/components/TableHeader/TableHeader.tsx +590 -0
- package/assets/components/TableHeader/index.ts +9 -0
- package/assets/components/TableRow/TableRow.module.css +290 -0
- package/assets/components/TableRow/TableRow.tsx +253 -0
- package/assets/components/TableRow/index.ts +8 -0
- package/assets/components/TableRowEdit/TableRowEdit.module.css +126 -0
- package/assets/components/TableRowEdit/TableRowEdit.tsx +247 -0
- package/assets/components/TableRowEdit/index.ts +2 -0
- package/assets/components/TableRowSelect/TableRowSelect.module.css +23 -0
- package/assets/components/TableRowSelect/TableRowSelect.tsx +175 -0
- package/assets/components/TableRowSelect/index.ts +2 -0
- package/assets/components/Tabs/Tabs.module.css +234 -0
- package/assets/components/Tabs/Tabs.tsx +426 -0
- package/assets/components/Tabs/index.ts +9 -0
- package/assets/components/Textarea/Textarea.module.css +318 -0
- package/assets/components/Textarea/Textarea.tsx +426 -0
- package/assets/components/Textarea/index.ts +7 -0
- package/assets/components/ThemeController.tsx +271 -0
- package/assets/components/TimePicker/TimePicker.module.css +317 -0
- package/assets/components/TimePicker/TimePicker.tsx +821 -0
- package/assets/components/TimePicker/index.ts +8 -0
- package/assets/components/TimePicker/utils.ts +115 -0
- package/assets/components/Toast/Toast.module.css +264 -0
- package/assets/components/Toast/Toast.tsx +243 -0
- package/assets/components/Toast/index.ts +15 -0
- package/assets/components/Toolbar/Toolbar.module.css +72 -0
- package/assets/components/Toolbar/Toolbar.tsx +75 -0
- package/assets/components/Toolbar/index.ts +2 -0
- package/assets/components/Tooltip/Tooltip.module.css +42 -0
- package/assets/components/Tooltip/Tooltip.tsx +332 -0
- package/assets/components/Tooltip/index.ts +2 -0
- package/assets/components/Topbar/Topbar.module.css +26 -0
- package/assets/components/Topbar/Topbar.tsx +47 -0
- package/assets/components/Topbar/index.ts +2 -0
- package/assets/components/_cn.ts +3 -0
- package/assets/components/_color.ts +48 -0
- package/assets/components/_data.ts +659 -0
- package/assets/components/_utils.css +19 -0
- package/assets/components/axisOptions.ts +24 -0
- package/assets/components/tokens.css +742 -0
- package/assets/prompts/GUIDE.md +251 -0
- package/assets/prompts/OVERRIDE.md +170 -0
- package/assets/prompts/PRINCIPLES.md +130 -0
- package/assets/prompts/axes/density.md +38 -0
- package/assets/prompts/axes/font.md +36 -0
- package/assets/prompts/axes/hue.md +36 -0
- package/assets/prompts/axes/icon.md +48 -0
- package/assets/prompts/axes/mode.md +50 -0
- package/assets/prompts/axes/motion.md +77 -0
- package/assets/prompts/axes/palette.md +35 -0
- package/assets/prompts/axes/shape.md +33 -0
- package/assets/prompts/axes/voice.md +54 -0
- package/assets/prompts/components/README.md +84 -0
- package/assets/prompts/components/app-frame.md +94 -0
- package/assets/prompts/components/attachment-chip.md +46 -0
- package/assets/prompts/components/attachment-thumb.md +49 -0
- package/assets/prompts/components/avatar.md +86 -0
- package/assets/prompts/components/badge.md +104 -0
- package/assets/prompts/components/breadcrumb.md +77 -0
- package/assets/prompts/components/button.md +99 -0
- package/assets/prompts/components/callout.md +81 -0
- package/assets/prompts/components/chat-input.md +84 -0
- package/assets/prompts/components/checkbox.md +46 -0
- package/assets/prompts/components/chip.md +84 -0
- package/assets/prompts/components/choice-card.md +127 -0
- package/assets/prompts/components/context-menu.md +145 -0
- package/assets/prompts/components/date-picker.md +104 -0
- package/assets/prompts/components/drawer.md +152 -0
- package/assets/prompts/components/empty-state.md +69 -0
- package/assets/prompts/components/file-icon.md +73 -0
- package/assets/prompts/components/filter-button.md +42 -0
- package/assets/prompts/components/form-field.md +74 -0
- package/assets/prompts/components/gallery-item.md +158 -0
- package/assets/prompts/components/icon-button.md +82 -0
- package/assets/prompts/components/icon.md +68 -0
- package/assets/prompts/components/input.md +95 -0
- package/assets/prompts/components/list-item.md +235 -0
- package/assets/prompts/components/menu-item.md +234 -0
- package/assets/prompts/components/metric-card.md +132 -0
- package/assets/prompts/components/modal.md +131 -0
- package/assets/prompts/components/pagination.md +92 -0
- package/assets/prompts/components/progress-bar.md +61 -0
- package/assets/prompts/components/quotation-bar.md +39 -0
- package/assets/prompts/components/radio.md +41 -0
- package/assets/prompts/components/select.md +104 -0
- package/assets/prompts/components/selectable-card.md +67 -0
- package/assets/prompts/components/shortcut.md +76 -0
- package/assets/prompts/components/sidebar.md +107 -0
- package/assets/prompts/components/slider.md +89 -0
- package/assets/prompts/components/split-button.md +71 -0
- package/assets/prompts/components/stepper.md +93 -0
- package/assets/prompts/components/switch.md +86 -0
- package/assets/prompts/components/table-cell.md +87 -0
- package/assets/prompts/components/table-header.md +157 -0
- package/assets/prompts/components/table-row-edit.md +129 -0
- package/assets/prompts/components/table-row-select.md +82 -0
- package/assets/prompts/components/table-row.md +156 -0
- package/assets/prompts/components/tabs.md +122 -0
- package/assets/prompts/components/textarea.md +105 -0
- package/assets/prompts/components/toast.md +149 -0
- package/assets/prompts/components/tooltip.md +71 -0
- package/assets/prompts/components/topbar.md +148 -0
- package/assets/prompts/primitives/emerge.md +43 -0
- package/assets/prompts/primitives/fade.md +31 -0
- package/assets/prompts/primitives/scale.md +32 -0
- package/assets/prompts/primitives/scroll.md +75 -0
- package/assets/prompts/primitives/slide.md +31 -0
- package/assets/prompts/themes/_SCHEMA.md +135 -0
- package/assets/prompts/themes/_TEMPLATE.md +73 -0
- package/assets/prompts/themes/expressive.md +118 -0
- package/assets/prompts/themes/pure.md +87 -0
- package/assets/prompts/themes/spare.md +73 -0
- package/assets/prompts/tokens/breakpoint.md +29 -0
- package/assets/prompts/tokens/color.md +78 -0
- package/assets/prompts/tokens/radius.md +33 -0
- package/assets/prompts/tokens/shadow.md +35 -0
- package/assets/prompts/tokens/spacing.md +86 -0
- package/assets/prompts/tokens/typography.md +76 -0
- package/assets/prompts/tokens/z-index.md +35 -0
- package/assets/public/expressive.png +0 -0
- package/assets/public/pure.png +0 -0
- package/assets/public/spare.png +0 -0
- package/assets/skill/SKILL.md +36 -0
- package/bin/ctds.js +76 -0
- package/package.json +35 -4
- package/src/commands/init.js +215 -0
- package/src/commands/update.js +275 -0
- package/src/utils/assets.js +78 -0
- package/src/utils/deps.js +51 -0
- package/src/utils/detect.js +47 -0
- package/src/utils/diff.js +52 -0
- package/src/utils/hash.js +16 -0
- package/src/utils/log.js +41 -0
- package/src/utils/meta.js +73 -0
- package/src/utils/prompt.js +28 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Sidebar
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
좌측 세로 nav. **라우팅 전용** — Tabs/필터는 쓸 곳이 아님. Brand(home 링크) + Section(그룹 라벨) + Item(라우트 링크) + Profile(사용자) + Footer가 compound 조합. `collapsed`로 icon-only 축소. Item은 [[components/menu-item]]을 그대로 어댑트 — 시각·상태 규칙 공유.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- 라우트 이동은 Item에 `asChild` + Next `<Link>`.
|
|
8
|
+
- 현재 라우트는 `active`로 명시 (MenuItem의 active 시각 그대로).
|
|
9
|
+
- Brand는 Home 링크 자리로. `symbol`(항상 노출) + `wordmark`(expanded만).
|
|
10
|
+
- Section 묶음은 `title`로 그룹. collapsed일 땐 title 자동 숨김.
|
|
11
|
+
- `collapsed` 상태에서는 `aria-label`과 `title`을 자동으로 걸어 접근성 유지.
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- 페이지 안 뷰 전환에 쓰지 않는다 — 그건 [[components/tabs]].
|
|
15
|
+
- Item 안에 variant로 다른 톤을 쓰지 않는다. active 외의 강조 금지.
|
|
16
|
+
- Sidebar 자체에 padding/width를 CSS로 덮지 않는다. `--sidebar-w*` 토큰 경유.
|
|
17
|
+
- Brand symbol·wordmark 크기 override 금지. `--sidebar-brand-symbol-max`·`--sidebar-brand-wordmark-max` 상한 안에서만.
|
|
18
|
+
|
|
19
|
+
### Fallback
|
|
20
|
+
collapsed 결정 애매하면 `false`. Section title 애매하면 생략.
|
|
21
|
+
|
|
22
|
+
### 관련
|
|
23
|
+
- [[components/menu-item]] — Item 시각·상태 전부 재사용
|
|
24
|
+
- [[components/avatar]] — Profile 왼쪽 아바타
|
|
25
|
+
- [[components/app-frame]] — Sidebar를 담는 grid 쉘
|
|
26
|
+
- [[axes/density]] — `--sidebar-w*` 토큰·control-h가 Density에 반응
|
|
27
|
+
- [[tokens/color]] — `--fg`·`--primary`·active 상태 매핑
|
|
28
|
+
|
|
29
|
+
### Props 시그니처
|
|
30
|
+
```tsx
|
|
31
|
+
<Sidebar collapsed={boolean}> // default: false
|
|
32
|
+
<Sidebar.Header>
|
|
33
|
+
<Sidebar.Brand symbol={<Icon />} wordmark={<Logo /> | "CTDS"} />
|
|
34
|
+
</Sidebar.Header>
|
|
35
|
+
|
|
36
|
+
<Sidebar.Section title="워크스페이스">
|
|
37
|
+
<Sidebar.Item
|
|
38
|
+
icon={<Icon name="house" />}
|
|
39
|
+
label="대시보드"
|
|
40
|
+
active={boolean}
|
|
41
|
+
trailing={ReactNode} // 배지·카운트
|
|
42
|
+
shortcut="⌘1"
|
|
43
|
+
hasSubmenu={boolean}
|
|
44
|
+
disabled={boolean}
|
|
45
|
+
asChild={boolean}
|
|
46
|
+
onClick={(e) => void}
|
|
47
|
+
/>
|
|
48
|
+
</Sidebar.Section>
|
|
49
|
+
|
|
50
|
+
<Sidebar.Separator />
|
|
51
|
+
|
|
52
|
+
<Sidebar.Footer>
|
|
53
|
+
<Sidebar.Profile
|
|
54
|
+
name="희영"
|
|
55
|
+
sub="admin@ctds.dev"
|
|
56
|
+
avatar={{ src, status }} // Avatar props 패스스루
|
|
57
|
+
trailing={<IconButton />}
|
|
58
|
+
/>
|
|
59
|
+
</Sidebar.Footer>
|
|
60
|
+
</Sidebar>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 상태 매트릭스
|
|
64
|
+
| 상태 | 시각 |
|
|
65
|
+
|---|---|
|
|
66
|
+
| expanded (기본) | 폭 `--sidebar-w`, 모든 자식 라벨·wordmark·trailing 노출 |
|
|
67
|
+
| collapsed | 폭 `--sidebar-w-collapsed`, Item은 icon-only, wordmark·Section title·Profile 텍스트·trailing 숨김 |
|
|
68
|
+
| Item active | MenuItem active 그대로 — `--bg-muted` 배경 + `fg-strong` + medium weight |
|
|
69
|
+
|
|
70
|
+
### 조합 예시
|
|
71
|
+
```tsx
|
|
72
|
+
// 기본 — 섹션 2개 + Profile
|
|
73
|
+
<Sidebar>
|
|
74
|
+
<Sidebar.Header>
|
|
75
|
+
<Sidebar.Brand symbol={<Icon name="rocket-launch" />} wordmark="CTDS" />
|
|
76
|
+
</Sidebar.Header>
|
|
77
|
+
|
|
78
|
+
<Sidebar.Section title="워크스페이스">
|
|
79
|
+
<Sidebar.Item asChild icon={<Icon name="house" />} label="대시보드" active>
|
|
80
|
+
<Link href="/">대시보드</Link>
|
|
81
|
+
</Sidebar.Item>
|
|
82
|
+
<Sidebar.Item asChild icon={<Icon name="chart-line" />} label="분석">
|
|
83
|
+
<Link href="/analytics">분석</Link>
|
|
84
|
+
</Sidebar.Item>
|
|
85
|
+
</Sidebar.Section>
|
|
86
|
+
|
|
87
|
+
<Sidebar.Separator />
|
|
88
|
+
|
|
89
|
+
<Sidebar.Section title="설정">
|
|
90
|
+
<Sidebar.Item icon={<Icon name="gear" />} label="환경설정" shortcut="⌘," />
|
|
91
|
+
</Sidebar.Section>
|
|
92
|
+
|
|
93
|
+
<Sidebar.Footer>
|
|
94
|
+
<Sidebar.Profile name="희영" sub="admin@ctds.dev" avatar={{ status: "online" }} />
|
|
95
|
+
</Sidebar.Footer>
|
|
96
|
+
</Sidebar>
|
|
97
|
+
|
|
98
|
+
// Collapsed — icon-only
|
|
99
|
+
<Sidebar collapsed>
|
|
100
|
+
<Sidebar.Header>
|
|
101
|
+
<Sidebar.Brand symbol={<Icon name="rocket-launch" />} />
|
|
102
|
+
</Sidebar.Header>
|
|
103
|
+
<Sidebar.Section>
|
|
104
|
+
<Sidebar.Item icon={<Icon name="house" />} label="대시보드" active />
|
|
105
|
+
</Sidebar.Section>
|
|
106
|
+
</Sidebar>
|
|
107
|
+
```
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Slider
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
연속·이산 수치를 **드래그로 조정**하는 최소 컨트롤. 트랙 + 채워진 부분 + 썸만 있는 미니멀 스펙 — 값 라벨·마크·range(양끝 두 개)는 이 컴포넌트가 다루지 않는다. 값을 텍스트로 보고 싶으면 옆에 별도 노드로 붙인다.
|
|
5
|
+
|
|
6
|
+
native `<input type="range">` 위에 스타일만 얹어 접근성(키보드 ← → Home End, 드래그, 스크린리더)을 그대로 상속.
|
|
7
|
+
|
|
8
|
+
### Do
|
|
9
|
+
- **연속·근사 조정에만 씀** — 볼륨, 밝기, 오프셋, 확대율처럼 "대략 이 정도" 조작이 자연스러운 값.
|
|
10
|
+
- **정확한 값 입력이 필요하면 [[components/input]]** (`type="number"`). 슬라이더는 근사, 입력은 정확 — 병행해도 좋다.
|
|
11
|
+
- **현재 값을 어딘가엔 표시**. 슬라이더 오른쪽에 `<span>{value}px</span>` 같은 라벨을 붙여 지금 값이 얼마인지 읽히게.
|
|
12
|
+
- **step은 의미 있는 최소 단위**. px 조정이면 1, 확대율이면 0.1, 볼륨이면 5 같은 식.
|
|
13
|
+
|
|
14
|
+
### Don't
|
|
15
|
+
- **불연속 3~5개 옵션 선택에 쓰지 않는다** — 그건 [[components/tabs]] `segmented` 또는 [[components/filter-button]].
|
|
16
|
+
- **On/Off 두 값만 있는 자리에 쓰지 않는다** — [[components/switch]].
|
|
17
|
+
- **range slider(양 끝 두 개)로 확장하지 않는다** — 이 컴포넌트는 단일 값만. 범위 선택은 별도 컴포넌트로.
|
|
18
|
+
- 썸 크기·트랙 두께를 CSS로 덮어쓰지 않는다 — Density 축 (`--control-h-*`, `--space-*`)이 결정.
|
|
19
|
+
|
|
20
|
+
### Fallback
|
|
21
|
+
size 애매하면 `md`, step 애매하면 `1`.
|
|
22
|
+
|
|
23
|
+
### 관련
|
|
24
|
+
- [[axes/density]] — `--control-h-*`가 썸 크기·트랙 높이에 매핑
|
|
25
|
+
- [[axes/shape]] — 트랙·썸 radius (`--radius-full` 고정, Shape 축 영향 없음)
|
|
26
|
+
- [[axes/motion]] — hover·drag 상태 전환 duration
|
|
27
|
+
- [[axes/palette]] — 채워진 트랙이 `--primary`
|
|
28
|
+
- [[tokens/color]] — `--primary` (채운 부분), `--bg-muted` (트랙 배경), `--border-strong` (썸 경계)
|
|
29
|
+
- [[components/input]] — 정확한 수치 입력 병행
|
|
30
|
+
|
|
31
|
+
### Props 시그니처
|
|
32
|
+
```tsx
|
|
33
|
+
<Slider
|
|
34
|
+
size="sm | md | lg" // default: md
|
|
35
|
+
min={number} // default: 0
|
|
36
|
+
max={number} // default: 100
|
|
37
|
+
step={number} // default: 1
|
|
38
|
+
value={number} // controlled
|
|
39
|
+
defaultValue={number} // uncontrolled 초기값
|
|
40
|
+
onValueChange={(n) => void} // 편의 콜백 (숫자로 바로 옴)
|
|
41
|
+
disabled
|
|
42
|
+
aria-label="…" // 시각 라벨 없으면 필수
|
|
43
|
+
/>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 상태 매트릭스
|
|
47
|
+
| 상태 | 트랙 | 썸 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| default | `--bg-muted` 배경 + `--primary` fill | `--bg` + `--border-strong` 경계 + `shadow-xs` |
|
|
50
|
+
| hover | 동일 | 경계 `--primary` |
|
|
51
|
+
| focus-visible | 동일 | `--ring-shadow` + `--primary` 경계 |
|
|
52
|
+
| active (드래그) | 동일 | scale 1.05, cursor grabbing |
|
|
53
|
+
| disabled | opacity 0.5 | opacity 0.5, cursor not-allowed |
|
|
54
|
+
|
|
55
|
+
### Size 가이드
|
|
56
|
+
| size | 썸 (control-h) | 트랙 두께 | 언제 |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `sm` | control-h-sm | space-0_5 | 툴바 · 인스펙터 · 조밀한 컨트롤 그룹 |
|
|
59
|
+
| `md` | control-h-md | space-1 | **기본.** 폼 · 설정 페이지 |
|
|
60
|
+
| `lg` | control-h-lg | space-1_5 | 터치 · 랜딩 · 큰 값 조정(볼륨·밝기 등) |
|
|
61
|
+
|
|
62
|
+
### 접근성
|
|
63
|
+
- 실제 `<input type="range">`라서 키보드 ← → (step), PageUp/PageDown (10*step), Home/End 가 자동 동작.
|
|
64
|
+
- 스크린리더가 min·max·현재 값을 읽음.
|
|
65
|
+
- 시각 라벨이 없으면 `aria-label` 필수. 옆에 값을 표시하는 노드가 있다면 `aria-describedby`로 연결하면 더 좋음.
|
|
66
|
+
|
|
67
|
+
### 조합 예시
|
|
68
|
+
```tsx
|
|
69
|
+
// 툴바 안 오프셋 조정 — 값 라벨을 옆에 붙여 현재 값을 노출
|
|
70
|
+
<div className="row">
|
|
71
|
+
<Slider
|
|
72
|
+
size="sm"
|
|
73
|
+
min={-40}
|
|
74
|
+
max={40}
|
|
75
|
+
step={1}
|
|
76
|
+
value={offset}
|
|
77
|
+
onValueChange={setOffset}
|
|
78
|
+
aria-label="Arrow offset"
|
|
79
|
+
/>
|
|
80
|
+
<span className="value">{offset}px</span>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
// 폼 안 볼륨 조정 — Input과 병행
|
|
84
|
+
<label>
|
|
85
|
+
볼륨
|
|
86
|
+
<Slider min={0} max={100} step={5} value={vol} onValueChange={setVol} />
|
|
87
|
+
<Input type="number" min={0} max={100} value={vol} onChange={(e) => setVol(Number(e.target.value))} />
|
|
88
|
+
</label>
|
|
89
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# SplitButton
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
시각적으로 하나로 붙어있지만 클릭 영역이 두 개로 분리된 버튼 — `main`(기본 액션) + `menu`(부가 옵션 드롭다운). 사용자에게 "이 액션의 가장 흔한 형태 + 대안" 관계를 한 컨트롤로 압축한다. 예: [저장 | ▾] → 저장 / 다른 이름으로 저장 / 사본 저장.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- **main은 예측 가능한 기본값** — 90% 사용자가 원할 액션. menu는 그 외 변형.
|
|
8
|
+
- `menuLabel` prop은 필수. 스크린리더가 caret 영역을 식별해야 함 ("옵션 더보기", "저장 옵션" 등).
|
|
9
|
+
- variant는 [[components/button]]과 동일 규칙 — 한 화면에 `primary` 1개.
|
|
10
|
+
- menu 열림 상태는 부모가 관리하고 `aria-expanded`는 caret button에 얹는다 (필요 시 `...rest`로 전달할 수 있게 컴포넌트 확장).
|
|
11
|
+
|
|
12
|
+
### Don't
|
|
13
|
+
- 두 액션이 **의미상 무관**하면 SplitButton 아님 → 그냥 Button 2개.
|
|
14
|
+
- menu에 3개 이상 항목이 들어가면 검토. 너무 많으면 그냥 [[components/menu]] 트리거 Button 하나로.
|
|
15
|
+
- 구분선 색·굵기 CSS로 덮어쓰지 않는다. currentColor × 알파로 variant 톤 자동 반응.
|
|
16
|
+
- `ghost` / `destructive`는 지원하지 않는다. Ghost는 무게가 최소인데 옆에 caret이 붙는 순간 시각 위계가 튀고, destructive는 부가 옵션까지 감싸기엔 위험 신호가 과함. 삭제 계열은 [[components/button]] `destructive` / `destructive-ghost` 단독으로.
|
|
17
|
+
|
|
18
|
+
### Fallback
|
|
19
|
+
variant 애매하면 `secondary`, size 애매하면 `md`.
|
|
20
|
+
|
|
21
|
+
### 관련
|
|
22
|
+
- [[components/button]] — variant/size 규칙 공유
|
|
23
|
+
- [[components/menu]] — menu 클릭 시 열리는 드롭다운
|
|
24
|
+
- [[axes/density]] — `--control-h-*`가 두 영역 높이·정사각 폭에 매핑
|
|
25
|
+
- [[axes/shape]] — root radius가 Shape 축에 반응
|
|
26
|
+
|
|
27
|
+
### Props 시그니처
|
|
28
|
+
```tsx
|
|
29
|
+
<SplitButton
|
|
30
|
+
size="sm | md | lg" // default: md
|
|
31
|
+
variant="primary | inverse | secondary | outline" // default: primary
|
|
32
|
+
leftIcon={ReactNode} // main 라벨 앞
|
|
33
|
+
menuIcon={ReactNode} // 기본 caret-down 대체
|
|
34
|
+
onMainClick={handler}
|
|
35
|
+
onMenuClick={openMenu}
|
|
36
|
+
menuLabel="옵션 더보기" // 필수, 접근성용
|
|
37
|
+
fullWidth loading disabled
|
|
38
|
+
>
|
|
39
|
+
저장
|
|
40
|
+
</SplitButton>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 구조·동작
|
|
44
|
+
- 내부적으로 `<button>` 2개 + 사이 `<span>` divider
|
|
45
|
+
- 각 영역이 독립 `:hover` — main hover 시 menu는 그대로, 반대도 동일
|
|
46
|
+
- Divider는 상하 여백(`--space-1_5`) 두고 세로 hairline, 색은 variant fg의 25% 알파
|
|
47
|
+
- 포커스링은 각 button별로. 링이 옆 영역 위로 올라오게 z-index 처리
|
|
48
|
+
|
|
49
|
+
### 조합 예시
|
|
50
|
+
```tsx
|
|
51
|
+
// 저장 + 다른 형식으로 저장 (드롭다운)
|
|
52
|
+
<SplitButton
|
|
53
|
+
variant="primary"
|
|
54
|
+
leftIcon={<Icon name="check" />}
|
|
55
|
+
onMainClick={save}
|
|
56
|
+
onMenuClick={() => setMenuOpen(true)}
|
|
57
|
+
menuLabel="저장 옵션"
|
|
58
|
+
>
|
|
59
|
+
저장
|
|
60
|
+
</SplitButton>
|
|
61
|
+
|
|
62
|
+
// 필터 적용 + 필터 편집
|
|
63
|
+
<SplitButton
|
|
64
|
+
variant="outline"
|
|
65
|
+
onMainClick={applyLastFilter}
|
|
66
|
+
onMenuClick={openFilterMenu}
|
|
67
|
+
menuLabel="필터 편집"
|
|
68
|
+
>
|
|
69
|
+
최근 필터
|
|
70
|
+
</SplitButton>
|
|
71
|
+
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Stepper
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
다단계 프로세스의 진행 위치를 시각적으로 보여주는 컴포넌트. 각 스텝은 `upcoming | active | done` 세 상태를 가지며, 상태 대비로 사용자가 "지금 어디에 있고 얼마나 남았는지"를 즉시 파악하게 한다. Rail·툴바 같은 껍데기(브랜드 슬롯·푸터)는 이 컴포넌트가 담지 않는다 — Stepper는 **스텝 리스트만** 담당.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- **기본은 `orientation="vertical"`**. 온보딩·설정·긴 폼처럼 화면 좌측 사이드에 붙는 표준 케이스.
|
|
8
|
+
- **좁은 헤더·상단 배너에는 `orientation="horizontal"`**. 세로 공간이 부족하고 스텝 개수가 3~5개일 때.
|
|
9
|
+
- **스텝이 6개를 넘으면 hint를 지운다** — title만 남기고 밀도를 확보.
|
|
10
|
+
- **활성 스텝은 한 번에 하나**. `activeIndex`로만 제어. 자식 Item에 `state`를 직접 넣는 건 예외 케이스(비선형 진행)에서만.
|
|
11
|
+
- **미도달 스텝 클릭은 막는다** — 상위에서 `disabled` prop 또는 `onStepClick`에서 조건 처리. 스텝 스킵을 허용하면 진행률의 의미가 무너진다.
|
|
12
|
+
- **marker는 `dot` 기본**. `number`는 순서가 정보 그 자체일 때만 (예: "3단계 결제 프로세스"처럼 숫자가 사용자 언어에 이미 있는 경우).
|
|
13
|
+
- **색은 로컬 CSS 변수로 오버라이드**. 특수 팔레트(온보딩 다크 rail 등)에서는 `--stepper-fg` · `--stepper-fg-muted` · `--stepper-fg-subtle` · `--stepper-line` · `--stepper-dot-idle` · `--stepper-marker-fg`를 부모에서 재정의.
|
|
14
|
+
|
|
15
|
+
### Don't
|
|
16
|
+
- Stepper 안에 브랜드 로고·푸터·CTA 버튼을 넣지 않는다 — 상위 shell(Rail)의 책임.
|
|
17
|
+
- marker 안에 이모지·아바타·커스텀 배지 삽입 금지. dot / number / check만 허용.
|
|
18
|
+
- 활성 스텝을 진하게, 미도달을 진하게 동시에 강조하지 않는다. 위계는 하나(active만 `--stepper-fg`).
|
|
19
|
+
- 스텝 사이 dashed line을 solid로 바꾸지 않는다 — dashed는 "진행 여지"의 시각 신호.
|
|
20
|
+
- Motion 축과 무관하게 marker에 커스텀 애니메이션(bounce, spin 등) 붙이지 않는다. 상태 전환은 `--transition-colors`로 통일.
|
|
21
|
+
- `data-state="active"` 위에 hover가 border/bg를 덮어쓰지 않는다. [[feedback/hover-over-active]] 원칙.
|
|
22
|
+
|
|
23
|
+
### Fallback
|
|
24
|
+
orientation 애매하면 `vertical`, marker 애매하면 `dot`. 스텝 6개 초과에 orientation·marker 판단 안 서면 사용자에게 확인.
|
|
25
|
+
|
|
26
|
+
### 관련
|
|
27
|
+
- [[components/progress-bar]] — 연속형 진행률. 스텝이 discrete 하지 않으면 이쪽.
|
|
28
|
+
- [[components/tabs]] — 순서·진행 개념 없이 병렬 전환이면 Tabs.
|
|
29
|
+
- [[axes/density]] — `--space-scale`이 스텝 간격 · marker 크기에 반영
|
|
30
|
+
- [[axes/motion]] — 상태 전환 duration/easing
|
|
31
|
+
- [[tokens/color]] — `--fg` · `--fg-muted` · `--fg-subtle` · `--border` 매핑
|
|
32
|
+
- [[tokens/spacing]] — `--space-5`(marker), `--space-6`(item-gap) 참고
|
|
33
|
+
|
|
34
|
+
### Props 시그니처
|
|
35
|
+
```tsx
|
|
36
|
+
<Stepper
|
|
37
|
+
activeIndex={number}
|
|
38
|
+
orientation="vertical | horizontal" // default: vertical
|
|
39
|
+
marker="dot | number" // default: dot
|
|
40
|
+
onStepClick={(index: number) => void}
|
|
41
|
+
>
|
|
42
|
+
<Stepper.Item title="..." hint="..." disabled={boolean} state="upcoming | active | done" />
|
|
43
|
+
</Stepper>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Variant 매트릭스
|
|
47
|
+
| orientation | marker | 용도 | 금지 조합 |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `vertical` | `dot` | **기본.** 온보딩·설정·긴 폼의 좌측 rail | — |
|
|
50
|
+
| `vertical` | `number` | 순서가 사용자 언어에 이미 있는 세로 rail (드묾) | 스텝 6개 초과 시 hint까지 붙이면 답답 |
|
|
51
|
+
| `horizontal` | `dot` | 좁은 헤더·상단 배너, 스텝 3~5개 | 스텝 6개 초과 금지 (dashed line이 짧아짐) |
|
|
52
|
+
| `horizontal` | `number` | 결제·체크아웃 같은 "명시적 순서" 헤더 프로세스 | 스텝 6개 초과 금지 |
|
|
53
|
+
|
|
54
|
+
### 색 오버라이드 예 (특수 팔레트)
|
|
55
|
+
```css
|
|
56
|
+
/* 예: 온보딩 다크 rail에서 Stepper를 밝은 톤으로 뒤집기 */
|
|
57
|
+
.myDarkRail {
|
|
58
|
+
--stepper-fg: var(--onb-rail-fg);
|
|
59
|
+
--stepper-fg-muted: var(--onb-rail-fg-muted);
|
|
60
|
+
--stepper-fg-subtle: var(--onb-rail-fg-subtle);
|
|
61
|
+
--stepper-line: var(--onb-rail-line);
|
|
62
|
+
--stepper-dot-idle: var(--onb-rail-dot-idle);
|
|
63
|
+
--stepper-marker-fg: var(--onb-rail);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 조합 예시
|
|
68
|
+
```tsx
|
|
69
|
+
// 온보딩 세로 rail — 기본 dot
|
|
70
|
+
<Stepper
|
|
71
|
+
activeIndex={stepIdx}
|
|
72
|
+
onStepClick={(i) => {
|
|
73
|
+
if (i <= stepIdx || isReachable(i)) setStepIdx(i);
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
{STEPS.map((s, i) => (
|
|
77
|
+
<Stepper.Item
|
|
78
|
+
key={s.key}
|
|
79
|
+
title={s.title}
|
|
80
|
+
hint={s.hint}
|
|
81
|
+
disabled={i > stepIdx && !isReachable(i)}
|
|
82
|
+
/>
|
|
83
|
+
))}
|
|
84
|
+
</Stepper>
|
|
85
|
+
|
|
86
|
+
// 체크아웃 상단 헤더 — horizontal + number
|
|
87
|
+
<Stepper activeIndex={1} orientation="horizontal" marker="number">
|
|
88
|
+
<Stepper.Item title="장바구니" />
|
|
89
|
+
<Stepper.Item title="배송 정보" />
|
|
90
|
+
<Stepper.Item title="결제" />
|
|
91
|
+
<Stepper.Item title="완료" />
|
|
92
|
+
</Stepper>
|
|
93
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
**즉시 적용되는 on/off** 컨트롤. 토글하는 순간 상태가 변한다 — 저장 버튼이 따로 없는 자리에 쓴다.
|
|
5
|
+
시맨틱은 `role="switch"`가 붙은 native checkbox. 시각은 알약 트랙 + 원형 썸 — 켜지면 트랙이 `--primary`로 채워지고 썸이 우측으로 이동.
|
|
6
|
+
|
|
7
|
+
### Do
|
|
8
|
+
- **즉시 반영에만**: 다크모드, 알림 on/off, 자동 저장, 필터 옵션. 토글 → 부작용까지 즉시.
|
|
9
|
+
- **"선택 여부"가 아니라 "상태 전환"에만**: 폼 제출 후 반영되는 옵션은 [[components/checkbox]].
|
|
10
|
+
- **라벨은 별도 컴포넌트로**: Switch 자체는 컨트롤만. 라벨은 `<label>` wrap이나 옆 텍스트 노드로 배치. 컴팩트한 툴바에서는 라벨을 아예 생략(그룹 자체가 라벨 역할)해도 됨.
|
|
11
|
+
- **폼 안이면 `name` 필수**: native checkbox 위에 얹은 거라 폼 제출에 자연스럽게 포함됨.
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- **다중 선택에 쓰지 않는다** — [[components/checkbox]].
|
|
15
|
+
- **저장이 필요한 자리에 쓰지 않는다** — Switch를 켜자마자 부작용이 없다면 사용자는 "정말 켜졌나?" 헷갈린다.
|
|
16
|
+
- 트랙 색을 status(성공/경고/삭제)로 바꾸지 않는다 — 켜짐은 항상 `--primary`. 상태 표현은 다른 컴포넌트가 담당.
|
|
17
|
+
- 썸·트랙 크기를 CSS로 덮어쓰지 않는다. `size` prop · Density 축이 결정.
|
|
18
|
+
|
|
19
|
+
### Fallback
|
|
20
|
+
size 애매하면 `md`.
|
|
21
|
+
|
|
22
|
+
### 관련
|
|
23
|
+
- [[components/checkbox]] — 선택 여부(폼 제출 후 반영)
|
|
24
|
+
- [[components/tabs]] `segmented` — 세 개 이상 배타 옵션
|
|
25
|
+
- [[axes/density]] — `--space-*` 스케일이 트랙·썸 크기에 매핑
|
|
26
|
+
- [[axes/motion]] — 썸 이동 duration
|
|
27
|
+
- [[tokens/color]] — `--primary` (켜짐), `--bg-muted` (꺼짐), `--fg-disabled` (disabled 썸)
|
|
28
|
+
|
|
29
|
+
### Props 시그니처
|
|
30
|
+
```tsx
|
|
31
|
+
<Switch
|
|
32
|
+
size="sm | md | lg" // default: md
|
|
33
|
+
checked / defaultChecked // native input props
|
|
34
|
+
onCheckedChange={(on) => void} // 편의 콜백 (boolean 바로 옴)
|
|
35
|
+
disabled
|
|
36
|
+
name
|
|
37
|
+
aria-label="…" // 시각 라벨 없으면 필수
|
|
38
|
+
/>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 상태 매트릭스
|
|
42
|
+
| 상태 | 트랙 | 썸 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| off · default | `--bg-muted` + `--border` hairline | `--bg` 원 + `shadow-xs` |
|
|
45
|
+
| off · hover | 경계 `--border-strong` | 동일 |
|
|
46
|
+
| on · default | `--primary` fill, 보더도 `--primary` | 우측 이동 |
|
|
47
|
+
| on · hover | `--primary`를 fg 12% 섞어 살짝 진하게 | 동일 |
|
|
48
|
+
| focus-visible | 트랙에 `--ring-shadow` | 동일 |
|
|
49
|
+
| disabled | `--bg-muted`, 투명 보더 | `--fg-disabled`, shadow none |
|
|
50
|
+
|
|
51
|
+
### Size 가이드
|
|
52
|
+
| size | 트랙 (w × h) | 언제 |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `sm` | space-6 × space-3 | 툴바, 인스펙터, 밀집 폼 |
|
|
55
|
+
| `md` | space-8 × space-4 | **기본.** 설정 페이지, 일반 폼 |
|
|
56
|
+
| `lg` | space-10 × space-5 | 터치, 온보딩, 큰 옵션 그룹 |
|
|
57
|
+
|
|
58
|
+
### 접근성
|
|
59
|
+
- `role="switch"` + native `input type="checkbox"` → 스크린리더가 "스위치, 켜짐/꺼짐"으로 읽음.
|
|
60
|
+
- 시각 라벨이 없으면 `aria-label` 필수.
|
|
61
|
+
- `<label>` 래퍼가 있어서 트랙·썸 어디 클릭해도 토글.
|
|
62
|
+
- 키보드: Space로 토글, Tab으로 포커스.
|
|
63
|
+
|
|
64
|
+
### 조합 예시
|
|
65
|
+
```tsx
|
|
66
|
+
// 툴바 안 옵션 토글 — 그룹이 라벨 역할, aria-label로만 시맨틱 라벨
|
|
67
|
+
<div className="toolbar-group">
|
|
68
|
+
<span>Time</span>
|
|
69
|
+
<Switch size="sm" checked={withTime} onCheckedChange={setWithTime} aria-label="Time" />
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
// 폼 안 옵션 — 텍스트 라벨과 세트
|
|
73
|
+
<label className="row">
|
|
74
|
+
<Switch checked={notify} onCheckedChange={setNotify} name="notify" />
|
|
75
|
+
<span>이메일 알림 받기</span>
|
|
76
|
+
</label>
|
|
77
|
+
|
|
78
|
+
// 설정 페이지 — 라벨 왼쪽, Switch 오른쪽 (justify-between)
|
|
79
|
+
<div className="setting-row">
|
|
80
|
+
<div>
|
|
81
|
+
<div>다크 모드</div>
|
|
82
|
+
<div className="hint">시스템 설정을 따르지 않고 강제 적용</div>
|
|
83
|
+
</div>
|
|
84
|
+
<Switch checked={dark} onCheckedChange={setDark} aria-label="다크 모드" />
|
|
85
|
+
</div>
|
|
86
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# TableCell
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
[[components/table-row]]·[[components/table-row-edit]]의 셀 자리에 넣는 **의미별 서브 컴포넌트 세트**. 하나의 TableCell이 아니라 **9종** — Text·Number·Image·Tag·Badge·Icon·Avatar·Link·Meter. 셀 안 콘텐츠 종류마다 톤·정렬·간격이 다른데 그 결정을 셀 레벨에서 끊어내 Row는 레이아웃만 담당하게 한다.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- 콘텐츠 성격에 맞는 서브 컴포넌트를 고른다. 성격 혼재 금지.
|
|
8
|
+
- Number는 `tabular-nums`가 자동. 숫자 셀은 반드시 Number 사용.
|
|
9
|
+
- Avatar 셀은 `label` + `sublabel`(이메일·역할) 2행 구조. 이름만이면 label만.
|
|
10
|
+
- Tag는 **다중값**(기술 스택·태그 모음), Badge는 **단일 상태**.
|
|
11
|
+
- 색은 톤(`tone`) prop으로만. hex·custom color 금지.
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- 숫자를 Text로 렌더하지 않는다 — tabular 정렬 깨짐.
|
|
15
|
+
- 숫자 셀 **우측정렬 금지** ([[feedback_no_right_align_numbers]]). 좌측정렬 유지.
|
|
16
|
+
- 셀 안에 두 종류 섞지 않는다(Avatar + Badge 등). 그러려면 상위 Row 커스텀 셀로.
|
|
17
|
+
- Tag·Badge를 Icon 셀에 섞어 쓰지 않는다. 아이콘은 상태 시그널 전용.
|
|
18
|
+
- 셀 자체에 padding override 금지. Row의 셀 간격 토큰이 전담.
|
|
19
|
+
|
|
20
|
+
### Fallback
|
|
21
|
+
tone 애매하면 Text=`default`, Icon=`default`, Meter=`default`.
|
|
22
|
+
|
|
23
|
+
### 관련
|
|
24
|
+
- [[components/table-row]] · [[components/table-row-edit]] · [[components/table-row-select]] — 담는 Row
|
|
25
|
+
- [[components/table-header]] — 컬럼 정의
|
|
26
|
+
- [[components/avatar]] — Avatar 셀 안쪽
|
|
27
|
+
- [[components/badge]] · [[components/tag]] — 각 셀의 소스
|
|
28
|
+
- [[tokens/color]] — tone 매핑
|
|
29
|
+
|
|
30
|
+
### Props 시그니처
|
|
31
|
+
```tsx
|
|
32
|
+
// 공통: 모든 TableCell*은 <span>으로 렌더. HTML attributes 통과.
|
|
33
|
+
<TableCellText tone="default | strong | muted | subtle">{string | ReactNode}</TableCellText>
|
|
34
|
+
<TableCellNumber value={number} format?={Intl.NumberFormatOptions} />
|
|
35
|
+
<TableCellImage src={string} alt={string} size={number} shape="circle | rounded | square" />
|
|
36
|
+
<TableCellBadge status="success | warning | destructive | info" variant="soft | solid">
|
|
37
|
+
{string}
|
|
38
|
+
</TableCellBadge>
|
|
39
|
+
<TableCellIcon icon={<Icon />} tone="default | success | warning | destructive | info" />
|
|
40
|
+
<TableCellAvatar
|
|
41
|
+
src?={string} name?={string} initials?={string}
|
|
42
|
+
shape="circle | rounded" // default: circle
|
|
43
|
+
status?="online | offline | busy | away"
|
|
44
|
+
label?={ReactNode} // 미지정 시 name 재사용
|
|
45
|
+
sublabel?={ReactNode} // 이메일·역할
|
|
46
|
+
/>
|
|
47
|
+
<TableCellLink href={string} external?={boolean}>{ReactNode}</TableCellLink>
|
|
48
|
+
<TableCellMeter value={0..100} tone="default | success | warning | destructive" label?={string} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 서브 컴포넌트 선택 가이드
|
|
52
|
+
| 셀 콘텐츠 | 사용 |
|
|
53
|
+
|---|---|
|
|
54
|
+
| 일반 텍스트 | `TableCellText` (tone 조절) |
|
|
55
|
+
| 금액·개수·비율 | `TableCellNumber` (tabular-nums) |
|
|
56
|
+
| 썸네일·프로덕트 사진 | `TableCellImage` |
|
|
57
|
+
| 다중 메타 라벨 (기술·분류·속성) | `TableCellBadge` 여러 개를 `inline-flex` + `space-1` gap으로 나열 |
|
|
58
|
+
| 단일 상태 라벨 | `TableCellBadge` |
|
|
59
|
+
| 상태 아이콘만 | `TableCellIcon` |
|
|
60
|
+
| 사람·팀 식별 | `TableCellAvatar` |
|
|
61
|
+
| 외부 링크·상세 라우팅 | `TableCellLink` |
|
|
62
|
+
| 진행률·사용량 바 | `TableCellMeter` |
|
|
63
|
+
|
|
64
|
+
### 조합 예시
|
|
65
|
+
```tsx
|
|
66
|
+
// 사용자 리스트 — Avatar + 숫자 + Badge
|
|
67
|
+
<TableRow
|
|
68
|
+
cells={[
|
|
69
|
+
<TableCellAvatar src={u.avatar} name={u.name} sublabel={u.email} />,
|
|
70
|
+
<TableCellText tone="muted">{u.role}</TableCellText>,
|
|
71
|
+
<TableCellNumber value={u.projects} />,
|
|
72
|
+
<TableCellBadge status={u.active ? "success" : "warning"}>
|
|
73
|
+
{u.active ? "활성" : "휴면"}
|
|
74
|
+
</TableCellBadge>,
|
|
75
|
+
]}
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
// 분석 테이블 — 숫자 + Meter + Link
|
|
79
|
+
<TableRow
|
|
80
|
+
cells={[
|
|
81
|
+
<TableCellText tone="strong">{row.name}</TableCellText>,
|
|
82
|
+
<TableCellNumber value={row.visits} />,
|
|
83
|
+
<TableCellMeter value={row.conversion} tone="success" label={`${row.conversion}%`} />,
|
|
84
|
+
<TableCellLink href={`/reports/${row.id}`}>자세히</TableCellLink>,
|
|
85
|
+
]}
|
|
86
|
+
/>
|
|
87
|
+
```
|