@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,131 @@
|
|
|
1
|
+
# Modal
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
흐름을 끊고 한 가지 결정·입력·확인에 집중시키는 중앙 다이얼로그. 결과가 아니라 **다음 행동**을 요구할 때 쓴다. 결과 알림은 [[components/toast]], 상주 안내는 [[components/callout]].
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- **한 번에 하나의 결정**만. 두 가지 질문이면 Stepper 또는 두 모달로 분리.
|
|
8
|
+
- `title` 필수. 명사형 짧게(채널 생성 · 삭제 확인). 질문형("정말 삭제하시겠습니까?")은 `description`으로.
|
|
9
|
+
- **설명 문구는 `description` prop에 넣는다.** 헤더 영역 안에서 title 바로 아래 바짝 붙어, 하나의 "소개" 묶음으로 읽힘. `children`(body)에 `<p>`로 설명 넣지 않는다 — children은 **진짜 자유 콘텐츠(Input/Image/List/Form 등)** 전용 영역이고 title에서 떨어져 자리함.
|
|
10
|
+
- 설명이 2~3줄을 넘어가면 모달 자리가 아님 — 페이지 또는 Drawer로.
|
|
11
|
+
- children은 **자유 ReactNode** — Input/Image/List/Form 등. description과 분리된 아래 영역.
|
|
12
|
+
- `footer`는 액션 Button들을 넘김. **우측 정렬 · 최대 2~3개**. 순서는 **좌: outline(취소/이전), 우: primary(확정/다음)**.
|
|
13
|
+
- primary variant는 Button `md`가 기본 — 모달 안 Input도 `md`로 맞춰 수직 리듬 유지.
|
|
14
|
+
- 파괴적 확인은 `destructive` variant Button을 primary 자리에. "진짜 삭제하시겠습니까?"의 최종 확인 자리.
|
|
15
|
+
- dismiss는 **헤더 우측 X · ESC 키** 두 가지. 둘 다 기본 on.
|
|
16
|
+
- 저장·업로드 중처럼 중단 시 데이터 유실이 큰 경우 `disableEscape`로 ESC 차단 + `dismissible={false}`로 X도 제거 → 사용자는 footer 액션으로만 진행/취소.
|
|
17
|
+
|
|
18
|
+
### Don't
|
|
19
|
+
- **오버레이(바깥) 클릭으로 닫히지 않는다** — 실수 유발 방지. prop으로도 열지 않음. 가볍게 닫히는 UI가 필요하면 [[components/popover]].
|
|
20
|
+
- 모달 안에 모달 금지. 흐름이 꼬이고 ESC가 어느 쪽을 닫는지 모호.
|
|
21
|
+
- footer에 primary 2개 금지 — 무엇이 "다음"인지 모호해짐.
|
|
22
|
+
- 모달 안에 전체 페이지 분량 콘텐츠 금지. 스크롤이 5번 이상 필요하면 모달이 아니라 페이지 또는 Drawer 자리.
|
|
23
|
+
- 비동기 로딩 전용으로 모달 띄우지 않는다 — 인라인 Spinner 또는 Progress 사용. 모달은 입력·결정 전용.
|
|
24
|
+
- title을 CSS `text-transform: uppercase`로 꾸미지 않는다.
|
|
25
|
+
|
|
26
|
+
### Fallback
|
|
27
|
+
질문이 애매하면 "결과 알림이면 Toast, 흐름을 끊고 입력·확정이 필요하면 Modal, 가볍게 미리보기면 Popover". 사이즈·오버레이 클릭 등 정책 변경이 필요해 보이면 사용자에게 확인.
|
|
28
|
+
|
|
29
|
+
### 관련
|
|
30
|
+
- [[components/toast]] — 휘발성 결과 알림. Modal은 액션 요청.
|
|
31
|
+
- [[components/callout]] — 상주 안내.
|
|
32
|
+
- [[components/button]] — footer의 액션. 파괴적 확인은 `destructive` variant.
|
|
33
|
+
- [[primitives/fade]] · [[primitives/scale]] — overlay fade + content scale 조합.
|
|
34
|
+
- [[axes/motion]] — content scale 시작값(0.94/0.96/0.98) 스왑.
|
|
35
|
+
- [[tokens/z-index]] — overlay는 `z-overlay`, content는 `z-modal`.
|
|
36
|
+
|
|
37
|
+
### Props 시그니처
|
|
38
|
+
```tsx
|
|
39
|
+
<Modal
|
|
40
|
+
open={boolean}
|
|
41
|
+
onOpenChange={(open: boolean) => void}
|
|
42
|
+
title={ReactNode} // required
|
|
43
|
+
description={ReactNode} // 헤더 영역 안, title 바로 아래 한두 줄 설명
|
|
44
|
+
titleSrOnly={boolean} // 시각적 숨김 (일러스트 중심 모달 등)
|
|
45
|
+
footer={ReactNode} // Button들을 넘김. 없으면 footer 자체 미렌더
|
|
46
|
+
dismissible={boolean} // default: true (헤더 X 버튼)
|
|
47
|
+
dismissLabel={string} // X 버튼 스크린리더 라벨 (i18n은 호출부)
|
|
48
|
+
disableEscape={boolean} // ESC 닫힘 차단 (중요 작업 중)
|
|
49
|
+
>
|
|
50
|
+
{children} // 자유 ReactNode — Input / Image / Form / List 등. description과 분리된 아래 영역
|
|
51
|
+
</Modal>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Variant 매트릭스
|
|
55
|
+
Modal은 **단일 시각**이다. 사이즈·테마·톤 variant 없음 — 콘텐츠 자유도로 다양성을 담당.
|
|
56
|
+
|
|
57
|
+
| prop | 값 | 결과 |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `dismissible` | `true`(기본) / `false` | 헤더 X 노출/제거 |
|
|
60
|
+
| `disableEscape` | `false`(기본) / `true` | ESC 키 닫힘 활성/차단 |
|
|
61
|
+
| `titleSrOnly` | `false`(기본) / `true` | 헤더 title 시각적 숨김 (스크린리더엔 유지) |
|
|
62
|
+
|
|
63
|
+
**금지 조합**
|
|
64
|
+
- `dismissible=false` + `disableEscape=true` + `footer` 없음 — 사용자가 모달을 닫을 방법이 전혀 없음.
|
|
65
|
+
- `titleSrOnly=true` + 헤더에 title 역할을 하는 다른 요소 없음 — 시각적 맥락 상실.
|
|
66
|
+
|
|
67
|
+
### 조합 예시
|
|
68
|
+
```tsx
|
|
69
|
+
// 간단한 확인 — description만, children 없음
|
|
70
|
+
<Modal
|
|
71
|
+
open={open}
|
|
72
|
+
onOpenChange={setOpen}
|
|
73
|
+
title="채널 생성"
|
|
74
|
+
description="생성할 채널 이름을 입력해 주세요."
|
|
75
|
+
footer={
|
|
76
|
+
<>
|
|
77
|
+
<Button variant="outline" onClick={() => setOpen(false)}>취소</Button>
|
|
78
|
+
<Button variant="primary" onClick={next}>다음</Button>
|
|
79
|
+
</>
|
|
80
|
+
}
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
// Input이 들어가는 폼 모달 — description은 title 짝, Input은 children(line variant)
|
|
84
|
+
<Modal
|
|
85
|
+
open={open}
|
|
86
|
+
onOpenChange={setOpen}
|
|
87
|
+
title="새 폴더"
|
|
88
|
+
description="새 폴더가 생성될 이름을 입력해 주세요."
|
|
89
|
+
footer={
|
|
90
|
+
<>
|
|
91
|
+
<Button variant="outline" onClick={cancel}>취소</Button>
|
|
92
|
+
<Button variant="primary" onClick={create}>생성</Button>
|
|
93
|
+
</>
|
|
94
|
+
}
|
|
95
|
+
>
|
|
96
|
+
<Input
|
|
97
|
+
label="폴더 이름"
|
|
98
|
+
variant="line"
|
|
99
|
+
placeholder="예: 2026 Q1 리서치"
|
|
100
|
+
value={name}
|
|
101
|
+
onChange={e => setName(e.target.value)}
|
|
102
|
+
width="fill"
|
|
103
|
+
/>
|
|
104
|
+
</Modal>
|
|
105
|
+
|
|
106
|
+
// 파괴적 확인 (destructive primary)
|
|
107
|
+
<Modal
|
|
108
|
+
open={open}
|
|
109
|
+
onOpenChange={setOpen}
|
|
110
|
+
title="프로젝트 삭제"
|
|
111
|
+
description="이 작업은 되돌릴 수 없습니다. 모든 데이터가 영구 삭제됩니다."
|
|
112
|
+
footer={
|
|
113
|
+
<>
|
|
114
|
+
<Button variant="outline" onClick={cancel}>취소</Button>
|
|
115
|
+
<Button variant="destructive" onClick={remove}>삭제</Button>
|
|
116
|
+
</>
|
|
117
|
+
}
|
|
118
|
+
/>
|
|
119
|
+
|
|
120
|
+
// 저장 중 사용자가 실수로 닫지 못하게 (children에는 ProgressBar)
|
|
121
|
+
<Modal
|
|
122
|
+
open={saving}
|
|
123
|
+
onOpenChange={() => {}}
|
|
124
|
+
title="저장 중"
|
|
125
|
+
description="파일을 서버로 전송하는 중입니다."
|
|
126
|
+
dismissible={false}
|
|
127
|
+
disableEscape
|
|
128
|
+
>
|
|
129
|
+
<ProgressBar value={progress} />
|
|
130
|
+
</Modal>
|
|
131
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
|
|
5
|
+
긴 목록·테이블을 페이지 단위로 끊어 탐색시키는 nav 컨트롤. **하나의 컴포넌트가 variant로 세 가지 유즈케이스**(numbered / compact / jump)를 커버한다 — `DateRangePicker`처럼 분화된 컴포넌트를 만들지 않는다. prop 조합이 컴포넌트 분화보다 예측 가능성이 높다.
|
|
6
|
+
|
|
7
|
+
### Do
|
|
8
|
+
|
|
9
|
+
- **기본은 `size="md"`**. Pagination은 테이블 하단·관리 UI처럼 데이터 밀도 높은 자리에 가장 자주 들어가므로 md가 기본. 폼이나 일반 페이지의 긴 리스트에선 `lg`. (md는 내부적으로 Button sm을, lg는 Button md를 쓴다 — Button보다 한 단계 압축된 체계.)
|
|
10
|
+
- 현재 페이지는 **`variant="secondary"`로 표시**. primary 8% 알파 배경 + primary 텍스트. 페이지 안의 다른 primary 액션(저장·제출)과 시각 위계가 겹치지 않는다.
|
|
11
|
+
- 숫자 아이템은 **Button을 재사용**한다 — 라벨이 있으므로 Button이 맞다. `className`으로 `min-width`만 정사각 보정.
|
|
12
|
+
- prev/next·처음/끝 아이콘 아이템은 **IconButton을 재사용**한다 — Button을 icon-only로 쓰면 가로 패딩이 남아 정사각이 안 떨어진다. 사이즈 매핑은 `Pagination size=md → IconButton md`, `Pagination size=lg → IconButton lg` (width/height만 Pagination 내부 CSS에서 Button height와 일치시킴). 전부 `variant="ghost"` + `aria-label` 필수.
|
|
13
|
+
- 긴 목록(≥ 10쪽)에는 `variant="numbered"` 또는 `jump` 사용. 숫자 영역은 **현재 페이지 중심의 슬라이딩 윈도우** — 생략(…)이나 양 끝 고정 숫자를 쓰지 않는다. `windowSize`(5~10, 기본 5)로 보이는 숫자 개수만 조절.
|
|
14
|
+
- **prev/next는 "보이는 윈도우의 끝 숫자"로 점프**한다 (±1 아님). 예: `1 2 (3) 4 5`에서 next → 5, `1 2 3 4 (5) 6 7 8 9 10`에서 next → 10. 사용자가 이미 보이는 숫자 중 하나로 바로 점프해 윈도우를 통째로 넘길 수 있다. compact(숫자 윈도우 없음)에선 기존 ±1 유지.
|
|
15
|
+
- 테이블 하단의 좁은 상태 자리엔 `variant="compact"` — `"3/24"` 또는 커스텀 포맷터. prev/next와 숫자가 하나의 블록처럼 밀착(gap=0).
|
|
16
|
+
- 값은 1-indexed(`page=1`이 첫 페이지). controlled만 지원 — `page` + `onChange` 세트로 다룬다.
|
|
17
|
+
|
|
18
|
+
### Don't
|
|
19
|
+
|
|
20
|
+
- 현재 페이지를 `variant="primary"`로 쓰지 않는다 — 페이지의 "진짜" primary 액션과 시각 위계가 충돌한다.
|
|
21
|
+
- 전체 페이지 수가 1 이하일 때 Pagination을 숨긴다. 비활성 상태로 노출하지 않는다 (의미 없는 노이즈).
|
|
22
|
+
- 숫자 버튼에 `size` prop을 직접 섞어 쓰지 않는다. 한 Pagination 안의 모든 아이템은 같은 size.
|
|
23
|
+
- 커스텀 CSS로 Button 색/높이를 덮지 않는다. 모든 시각 조정은 Button variant/size 경유.
|
|
24
|
+
- 생략(…) 셀이나 "첫/끝 페이지 고정 노출" 패턴(`1 … 5 6 7 … 100`)을 되살리지 않는다. 숫자 영역은 **항상 연속된 슬라이딩 윈도우**다. 중간을 건너뛰고 싶으면 `jump` variant의 처음/끝 아이콘 버튼이 그 역할.
|
|
25
|
+
- disabled 시 숫자 Button을 Button 기본 disabled 스타일(회색 배경)로 두지 않는다. **배경 투명 + 숫자 색만 `--fg-disabled`로 연해져야** 밀집 자리에서 밑 배경을 가리지 않는다. Pagination.module.css에서 `.list .numberBtn*:disabled` 셀렉터로 덮어씀 — IconButton ghost disabled와 톤 일치.
|
|
26
|
+
|
|
27
|
+
### Fallback
|
|
28
|
+
|
|
29
|
+
variant 애매하면 `numbered`. 좁은 자리/모바일인데 variant 지정이 없으면 `simple`. 전체 페이지 수 모를 땐 `compact`로 "현재 페이지"만 표시.
|
|
30
|
+
|
|
31
|
+
### 관련
|
|
32
|
+
|
|
33
|
+
- [[components/button]] — 숫자·prev·next·처음/끝 아이템은 전부 Button 재사용. 사이즈·variant 체계 공유.
|
|
34
|
+
- [[components/table-row]] — 데이터 테이블 하단에 가장 자주 짝지어 놓는다. compact variant가 흔히 테이블 푸터에 들어감.
|
|
35
|
+
- [[axes/density]] — 버튼 높이가 `--control-h-md` 경유로 Density 축에 반응.
|
|
36
|
+
- [[axes/shape]] — radius가 Shape 축에 반응.
|
|
37
|
+
- [[tokens/color]] — 현재 페이지의 배경·텍스트는 `--secondary-bg` / `--secondary-fg` 경유.
|
|
38
|
+
|
|
39
|
+
### Props 시그니처
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<Pagination
|
|
43
|
+
variant="numbered | compact | jump" // default: "numbered"
|
|
44
|
+
size="md | lg" // default: "md"
|
|
45
|
+
page={number} // 1-indexed, controlled
|
|
46
|
+
total={number} // 전체 페이지 수
|
|
47
|
+
onChange={(page: number) => void}
|
|
48
|
+
|
|
49
|
+
windowSize={number} // default: 5. 5~10 범위, 자동 클램프
|
|
50
|
+
|
|
51
|
+
disabled={boolean}
|
|
52
|
+
compactFormatter={(page, total) => ReactNode} // compact variant 전용
|
|
53
|
+
aria-label={string}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Variant 매트릭스
|
|
58
|
+
|
|
59
|
+
| variant | 용도 | 금지 조합 |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `numbered` | 10쪽 이상 긴 목록의 표준. 현재 페이지 중심 슬라이딩 윈도우 + prev/next | 모바일 좁은 자리에서는 가로가 넘침 → `compact` |
|
|
62
|
+
| `compact` | 테이블 하단, 밀도 높은 상태 영역. `"3/24"`. prev/next와 숫자가 밀착한 블록. | 사용자가 임의 페이지로 점프해야 하는 자리 (숫자 클릭 불가) |
|
|
63
|
+
| `jump` | 매우 긴 아카이브·검색 결과. 처음·끝 포함 full nav | 전체 페이지 수가 적을 때(≤ 5)엔 과잉 |
|
|
64
|
+
|
|
65
|
+
### 조합 예시
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// 데이터 테이블 하단: 밀집 자리에 compact
|
|
69
|
+
<Pagination
|
|
70
|
+
variant="compact"
|
|
71
|
+
page={page}
|
|
72
|
+
total={totalPages}
|
|
73
|
+
onChange={setPage}
|
|
74
|
+
/>
|
|
75
|
+
|
|
76
|
+
// 긴 검색 결과: numbered가 표준
|
|
77
|
+
<Pagination
|
|
78
|
+
variant="numbered"
|
|
79
|
+
page={page}
|
|
80
|
+
total={120}
|
|
81
|
+
onChange={setPage}
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
// 아카이브: 처음·끝까지 한 번에, 윈도우 10개
|
|
85
|
+
<Pagination
|
|
86
|
+
variant="jump"
|
|
87
|
+
page={page}
|
|
88
|
+
total={320}
|
|
89
|
+
windowSize={10}
|
|
90
|
+
onChange={setPage}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ProgressBar
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
얇은 가로 **라인 로딩바**. 두 모드: `value` 있으면 **determinate**(그 %로 채움), 없으면 **indeterminate**(무한 왕복). 페이지 상단 네트워크·라우팅 로딩, 또는 인라인 업로드/다운로드 진행률에 사용. **본격적 Progress UI는 Stepper·Meter**([[components/table-cell]])가 담당 — ProgressBar는 "로딩" 신호 전용.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- 네트워크·라우팅 로딩: `position="fixed"` + indeterminate(value 미지정).
|
|
8
|
+
- 업로드·다운로드 진행률: `position="inline"` + determinate(value 0~100).
|
|
9
|
+
- `active=false`로 숨기면 자동 fade-out. 즉시 unmount 금지(깜빡임).
|
|
10
|
+
- role=`progressbar` + aria-valuenow 자동.
|
|
11
|
+
|
|
12
|
+
### Don't
|
|
13
|
+
- 긴 폼의 단계 표시엔 쓰지 않는다 — [[components/stepper]].
|
|
14
|
+
- 작업 비율·사용량 표시엔 쓰지 않는다 — TableCellMeter 또는 Chart.
|
|
15
|
+
- value가 100 넘지 않도록 상위에서 보장 (컴포넌트가 clamp하지만 로직 책임).
|
|
16
|
+
- 색을 variant로 바꾸지 않는다. 상태색(destructive 등)은 Meter 쪽에서.
|
|
17
|
+
|
|
18
|
+
### Fallback
|
|
19
|
+
position 애매하면 `fixed`, active 애매하면 `true`.
|
|
20
|
+
|
|
21
|
+
### 관련
|
|
22
|
+
- [[components/stepper]] — 단계 진행 표시 (ProgressBar와 역할 분리)
|
|
23
|
+
- [[components/table-cell]] — TableCellMeter가 비율 바
|
|
24
|
+
- [[axes/motion]] — indeterminate 애니메이션 duration이 Motion 축에 반응
|
|
25
|
+
|
|
26
|
+
### Props 시그니처
|
|
27
|
+
```tsx
|
|
28
|
+
<ProgressBar
|
|
29
|
+
value={number} // 0~100. 미지정 시 indeterminate
|
|
30
|
+
position="fixed | inline" // default: fixed
|
|
31
|
+
active={boolean} // default: true. false면 fade-out
|
|
32
|
+
/>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Mode 매트릭스
|
|
36
|
+
| 모드 | 조건 | 시각 | 쓰임 |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| determinate | `value` 숫자 | %만큼 채움 | 업로드·다운로드·설치 |
|
|
39
|
+
| indeterminate | `value` 미지정 | 무한 왕복 | 네트워크·라우팅 로딩 |
|
|
40
|
+
|
|
41
|
+
### Position 매트릭스
|
|
42
|
+
| position | 자리 | 쓰임 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `fixed` | 화면 최상단 fixed | **기본** — 라우팅 로딩바(Linear/Vercel 패턴) |
|
|
45
|
+
| `inline` | 부모 안 inline | 폼 안 업로드 진행 |
|
|
46
|
+
|
|
47
|
+
### 조합 예시
|
|
48
|
+
```tsx
|
|
49
|
+
// 라우팅 로딩 — 상단 fixed indeterminate
|
|
50
|
+
{isLoading && <ProgressBar />}
|
|
51
|
+
|
|
52
|
+
// 업로드 진행 — inline determinate
|
|
53
|
+
<div className="upload-row">
|
|
54
|
+
<span>{file.name}</span>
|
|
55
|
+
<ProgressBar position="inline" value={file.progress} />
|
|
56
|
+
<span>{file.progress}%</span>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
// active 토글 — fade-out
|
|
60
|
+
<ProgressBar active={isPending} />
|
|
61
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Quotation Bar
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
ChatInput 상단·Message 안에서 "**다른 글을 인용**"을 1줄로 접어 보여주는 바. `↳ icon + 1줄 ellipsis + X`. 긴 글은 자동으로 끝만 `…`. 삭제 가능한 자리엔 `onRemove`.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- 사용자가 메시지 선택·웹 결과를 인용으로 끌어오면 ChatInput 상단에 1건 노출.
|
|
8
|
+
- `title` 속성에 전체 텍스트 자동 세팅 — 호버 시 네이티브 툴팁으로 전문 확인.
|
|
9
|
+
- 삭제가 필요하면 `onRemove`. 레퍼런스는 X 상시 노출.
|
|
10
|
+
- 여러 인용은 현재 설계 **금지** — 1건만. 복수 인용은 상위에서 위아래로 쌓지 말고 선택 UI로 분리.
|
|
11
|
+
|
|
12
|
+
### Don't
|
|
13
|
+
- 여러 줄 보여주지 않는다 — 1줄 ellipsis가 원칙. 전문은 호버 툴팁·클릭 확장으로.
|
|
14
|
+
- 아이콘(`↳`) 바꾸지 않는다 — 레퍼런스 식별자.
|
|
15
|
+
- 배경을 과한 톤으로 덮지 않는다 — bg 기본, hairline border만.
|
|
16
|
+
- 안에 액션 버튼(Reply/Edit) 추가하지 않는다 — 인용은 "참조 표시"일 뿐, 상호작용은 상위 ChatInput·Message가 책임.
|
|
17
|
+
|
|
18
|
+
### Fallback
|
|
19
|
+
`onRemove` 없으면 X 숨김. 긴 글은 자동 ellipsis.
|
|
20
|
+
|
|
21
|
+
### 관련
|
|
22
|
+
- [[components/chat-input]] — 상위 소비처 (상단 bar 자리)
|
|
23
|
+
- [[components/message-bubble]] — 메시지 내 인용 표시에서도 재사용
|
|
24
|
+
- [[components/attachment-chip]] — 비슷한 레이아웃 (lead icon + content + X)
|
|
25
|
+
|
|
26
|
+
### Props 시그니처
|
|
27
|
+
```tsx
|
|
28
|
+
<QuotationBar
|
|
29
|
+
text={string} // 필수. 자동 ellipsis
|
|
30
|
+
onRemove={(e) => void} // 지정 시 우측 X
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 레이아웃
|
|
35
|
+
- `↳ icon` (`ArrowBendDownRight`, 16×16, `fg-muted`)
|
|
36
|
+
- gap: `space-2`
|
|
37
|
+
- 본문: `fg-muted`, 1줄 ellipsis
|
|
38
|
+
- X: `space-5`, `fg-muted` → hover 시 `bg-subtle` 틴트
|
|
39
|
+
- 외곽: hairline border, radius-md, bg
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Radio
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
같은 그룹 안에서 **하나만 선택**하는 컨트롤. 시각(`.indicator`: 외곽 원 + 내부 도트)과 시맨틱(native `<input type="radio">`)을 분리.
|
|
5
|
+
|
|
6
|
+
색은 [[tokens/color]]의 `--primary` 기반. 상태 4종은 [[components/checkbox]]와 공통 규칙.
|
|
7
|
+
|
|
8
|
+
### Do
|
|
9
|
+
- **같은 name으로 묶는다**: 그룹 전체가 하나의 `name` 속성을 공유. 시맨틱 그룹은 `role="radiogroup"` 래퍼 권장.
|
|
10
|
+
- **옵션 2개 이상**: 라디오는 최소 2개일 때 의미가 성립. 1개면 [[components/checkbox]]나 [[components/switch]].
|
|
11
|
+
- **선택 취소가 필요하면 Radio가 아니다**: radio는 한 번 선택되면 다시 “아무것도 선택 안 함”으로 못 돌아온다. 그런 UX가 필요하면 checkbox + 그룹 로직.
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- 옵션이 5개를 넘기면 Select를 검토. 라디오는 “한눈에 보이는 소수의 선택지”용.
|
|
15
|
+
- SVG 도트를 직접 그리지 않는다. CSS `background`/`transform: scale`로 이미 checked 애니메이션까지 처리됨.
|
|
16
|
+
|
|
17
|
+
### Fallback
|
|
18
|
+
size `md`, status 없음.
|
|
19
|
+
|
|
20
|
+
### 관련
|
|
21
|
+
- [[components/checkbox]] — 다중 선택 버전. Props/스타일 스펙 형제.
|
|
22
|
+
- [[tokens/color]] — `--primary` · 상태 토큰
|
|
23
|
+
|
|
24
|
+
### Props 시그니처
|
|
25
|
+
```tsx
|
|
26
|
+
<Radio
|
|
27
|
+
size="sm | md" // default: md
|
|
28
|
+
status="success | warning | destructive | info" // optional
|
|
29
|
+
name / value / checked / defaultChecked / onChange / disabled ... // native input
|
|
30
|
+
>
|
|
31
|
+
{children} // 라벨
|
|
32
|
+
</Radio>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 상태 매트릭스
|
|
36
|
+
| 상태 | 외곽 원 | 내부 도트 |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| unchecked | 배경 `--bg` · 테두리 `--border-strong` | scale(0) (숨김) |
|
|
39
|
+
| hover | 테두리 `--fg` | scale(0) |
|
|
40
|
+
| checked | 배경 `--primary` · 테두리 `--primary` | scale(1) · 색 `--bg` |
|
|
41
|
+
| checked + status | 배경/테두리 상태 토큰 | 그대로 |
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
단일 값 선택 콤보. Radix Select 위의 compound 어댑터. **Trigger 시각은 [[components/input]]과 100% 동일** (variant/size/invalid/label/description/warning 전부 공유), **Content/Item 시각은 [[components/context-menu]]와 동일** (팝오버 표면 + MenuItem 어댑터). 즉 이 컴포넌트는 두 계층을 잇는 **얇은 어댑터** — 자체 시각 규칙 금지.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- Input 옆에 나란히 놓을 땐 size·variant를 **반드시 통일**.
|
|
8
|
+
- `width`는 Input의 InputWidth 규약 그대로: `"fill" | "md" | "lg" | number`.
|
|
9
|
+
- `label`·`description`·`warning`은 Root에 — 트리거 아래 자동 렌더.
|
|
10
|
+
- Group + Label로 논리 묶음. 긴 리스트는 Separator로 끊음.
|
|
11
|
+
- Item에 `leadingActions`로 아이콘·국기·색 스와치 삽입.
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- Trigger에 custom CSS로 Input 시각을 벗어나지 않는다 — 두 컴포넌트가 다르게 보이면 폼 전체 시각이 깨짐.
|
|
15
|
+
- 5개 이하 옵션엔 Select 금지 — [[components/tabs]] variant=`segmented`가 자연스러움.
|
|
16
|
+
- 다중 선택에 쓰지 않는다. 그건 [[components/filter-button]] 또는 Combobox(미구현).
|
|
17
|
+
- Content에 자체 애니메이션 재정의 금지 — `.m-float` 유틸이 Radix in/out 담당.
|
|
18
|
+
|
|
19
|
+
### Fallback
|
|
20
|
+
size 애매하면 `md`, variant 애매하면 `outline`, width 애매하면 `"fill"`.
|
|
21
|
+
|
|
22
|
+
### 관련
|
|
23
|
+
- [[components/input]] — Trigger 시각·width·label 규약 공유
|
|
24
|
+
- [[components/menu-item]] — Content 안쪽 Item 어댑터
|
|
25
|
+
- [[components/context-menu]] · [[components/dropdown-menu]] — 팝오버 표면 공유
|
|
26
|
+
- [[components/form-field]] — label/description/warning 자리
|
|
27
|
+
- [[axes/density]] — control-h가 Trigger에, menu-item-h가 Item에
|
|
28
|
+
- [[axes/shape]] — radius가 Shape 축에 반응
|
|
29
|
+
|
|
30
|
+
### Props 시그니처
|
|
31
|
+
```tsx
|
|
32
|
+
<Select
|
|
33
|
+
size="sm | md | lg" // default: md
|
|
34
|
+
variant="outline | filled | line" // default: outline
|
|
35
|
+
width="fill | md | lg | number" // default: "fill"
|
|
36
|
+
value={string} defaultValue={string}
|
|
37
|
+
onValueChange={(v: string) => void}
|
|
38
|
+
disabled={boolean}
|
|
39
|
+
invalid={boolean}
|
|
40
|
+
label={ReactNode}
|
|
41
|
+
description={ReactNode}
|
|
42
|
+
warning={ReactNode} // 있으면 invalid=true 자동
|
|
43
|
+
placeholder="선택하세요"
|
|
44
|
+
id={string} // label htmlFor 자동 연결
|
|
45
|
+
>
|
|
46
|
+
<Select.Item value="kr" leadingActions={[<Icon name="flag" />]}>한국</Select.Item>
|
|
47
|
+
<Select.Separator />
|
|
48
|
+
<Select.Group>
|
|
49
|
+
<Select.Label>유럽</Select.Label>
|
|
50
|
+
<Select.Item value="de">독일</Select.Item>
|
|
51
|
+
<Select.Item value="fr">프랑스</Select.Item>
|
|
52
|
+
</Select.Group>
|
|
53
|
+
</Select>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Size × Item 매핑
|
|
57
|
+
| Trigger size | Item size | 쓰임 |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `sm` | `sm` | 밀도 높은 폼·툴바 |
|
|
60
|
+
| `md` | `sm` | **기본** — 트리거는 보통, 메뉴는 조밀 |
|
|
61
|
+
| `lg` | `md` | 랜딩·온보딩의 큰 입력 |
|
|
62
|
+
|
|
63
|
+
> 트리거가 lg여도 메뉴는 과하게 커지지 않도록 한 단계 작게 매핑. 실전에서 자연스러움.
|
|
64
|
+
|
|
65
|
+
### 상태 매트릭스
|
|
66
|
+
| 상태 | 시각 |
|
|
67
|
+
|---|---|
|
|
68
|
+
| default | Input default |
|
|
69
|
+
| hover | Input hover (border-strong) |
|
|
70
|
+
| focus-visible | Input focus (ring) |
|
|
71
|
+
| open | Content 열림 — `.m-float` emerge |
|
|
72
|
+
| disabled | opacity 0.5, cursor not-allowed |
|
|
73
|
+
| invalid / warning | border destructive, warning 텍스트 노출 |
|
|
74
|
+
|
|
75
|
+
### 조합 예시
|
|
76
|
+
```tsx
|
|
77
|
+
// 폼 안 Select — label + description, Input 옆에 나란히
|
|
78
|
+
<Select
|
|
79
|
+
size="md"
|
|
80
|
+
label="국가"
|
|
81
|
+
description="청구 주소 기준"
|
|
82
|
+
placeholder="선택하세요"
|
|
83
|
+
value={country}
|
|
84
|
+
onValueChange={setCountry}
|
|
85
|
+
>
|
|
86
|
+
<Select.Item value="kr" leadingActions={[<span>🇰🇷</span>]}>한국</Select.Item>
|
|
87
|
+
<Select.Item value="us" leadingActions={[<span>🇺🇸</span>]}>미국</Select.Item>
|
|
88
|
+
<Select.Item value="jp" leadingActions={[<span>🇯🇵</span>]}>일본</Select.Item>
|
|
89
|
+
</Select>
|
|
90
|
+
|
|
91
|
+
// 그룹 + 분리선
|
|
92
|
+
<Select label="담당자" placeholder="선택하세요">
|
|
93
|
+
<Select.Group>
|
|
94
|
+
<Select.Label>디자인</Select.Label>
|
|
95
|
+
<Select.Item value="hy">희영</Select.Item>
|
|
96
|
+
<Select.Item value="ds">도수</Select.Item>
|
|
97
|
+
</Select.Group>
|
|
98
|
+
<Select.Separator />
|
|
99
|
+
<Select.Group>
|
|
100
|
+
<Select.Label>엔지니어링</Select.Label>
|
|
101
|
+
<Select.Item value="jw">지원</Select.Item>
|
|
102
|
+
</Select.Group>
|
|
103
|
+
</Select>
|
|
104
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# SelectableCard
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
카드 전체가 선택 타겟인 컨트롤. title·description으로 옵션을 서술해야 할 때 [[components/radio]]·[[components/checkbox]]의 라벨만으로 부족한 경우 사용. native `<input type="radio|checkbox">`를 label 안에 숨겨 시맨틱과 폼 전송을 그대로 받는다.
|
|
5
|
+
|
|
6
|
+
### Do
|
|
7
|
+
- **옵션 서술이 필요할 때만**: title 한 줄로 충분하면 Radio·Checkbox. description이 있어야 뜻이 분명해질 때 SelectableCard.
|
|
8
|
+
- **같은 그룹은 같은 `name`**: 시맨틱 그룹은 `role="radiogroup"` 래퍼 권장 (mode="radio"·"icon").
|
|
9
|
+
- **mode 선택**: 단일 선택은 `radio` 또는 `icon`, 다중 선택은 `checkbox`. `icon`은 아이콘이 옵션 성격을 즉시 알려줄 때만.
|
|
10
|
+
- **카드 간 간격 `space-3`**: 카드끼리 붙지 않도록 vertical stack의 gap은 최소 `space-3`.
|
|
11
|
+
- **상단 잘림 방지**: 카드는 hover 시 `translateY(-1px)`로 살짝 뜬다. 부모 컨테이너가 `overflow: hidden`이거나 카드가 컨테이너 top에 정확히 붙어있으면 뜨는 순간 상단이 잘린다. 컴포넌트가 `margin-top: var(--hairline)`로 1차 방어하지만, 리스트 컨테이너에도 `padding-top` 최소 `space-1` 확보 또는 `overflow: visible` 유지 권장.
|
|
12
|
+
|
|
13
|
+
### Don't
|
|
14
|
+
- 인디케이터를 카드 밖에 두지 않는다 — leading 슬롯 고정.
|
|
15
|
+
- title/description 밖의 커스텀 콘텐츠(폼 필드, 버튼)를 카드 안에 넣지 않는다. 그건 [[components/callout]] 또는 별도 카드.
|
|
16
|
+
- 옵션이 6개를 넘기면 Select/Combobox 검토. SelectableCard는 "한 화면에 나열되는 소수 옵션"용.
|
|
17
|
+
- `mode="icon"`에 `icon` 미지정 금지 — 좌측 슬롯이 비어 정렬이 무너진다.
|
|
18
|
+
|
|
19
|
+
### Fallback
|
|
20
|
+
`mode` 미지정 시 `radio`. `icon` 미지정이면 mode="radio" 강제 fallback.
|
|
21
|
+
|
|
22
|
+
### 관련
|
|
23
|
+
- [[components/radio]] — 텍스트 한 줄 옵션. SelectableCard의 native input 시맨틱 형제.
|
|
24
|
+
- [[components/checkbox]] — 다중 선택 인디케이터 원본.
|
|
25
|
+
- [[components/callout]] — 안내 카드. 선택 대상이 아님.
|
|
26
|
+
- [[tokens/color]] — checked 상태의 `--fg`, `--primary` 토큰.
|
|
27
|
+
|
|
28
|
+
### Props 시그니처
|
|
29
|
+
```tsx
|
|
30
|
+
<SelectableCard
|
|
31
|
+
mode="radio | checkbox | icon" // default: radio
|
|
32
|
+
title={ReactNode} // required
|
|
33
|
+
description={ReactNode}
|
|
34
|
+
icon={IconName} // mode="icon" 필수
|
|
35
|
+
name / value / checked / defaultChecked / onChange / disabled ...
|
|
36
|
+
/>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Variant 매트릭스
|
|
40
|
+
| mode | leading 슬롯 | 선택 정책 | 용도 |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `radio` | 라디오 도트 | 그룹 내 단일 | 옵션 뜻을 title+description으로 설명해야 하는 단일 선택 |
|
|
43
|
+
| `checkbox` | 체크 박스 | 다중 | 여러 항목 동시 선택 (기능 토글, 필터 세트) |
|
|
44
|
+
| `icon` | Icon 컴포넌트 | 그룹 내 단일 | 아이콘이 옵션 성격을 즉시 전달하는 단일 선택 |
|
|
45
|
+
|
|
46
|
+
**금지 조합**
|
|
47
|
+
- `mode="checkbox"` + 상호배타 그룹 로직 → 의미 충돌. radio를 쓴다.
|
|
48
|
+
- `mode="icon"` + `icon` 미지정 → leading 슬롯 공백.
|
|
49
|
+
|
|
50
|
+
### 조합 예시
|
|
51
|
+
```tsx
|
|
52
|
+
<div role="radiogroup" style={{ display: "grid", gap: "var(--space-3)" }}>
|
|
53
|
+
<SelectableCard
|
|
54
|
+
name="agent-mode"
|
|
55
|
+
value="leaf"
|
|
56
|
+
title="Leaf"
|
|
57
|
+
description="Works alone in one turn."
|
|
58
|
+
defaultChecked
|
|
59
|
+
/>
|
|
60
|
+
<SelectableCard
|
|
61
|
+
name="agent-mode"
|
|
62
|
+
value="supervisor"
|
|
63
|
+
title="Supervisor"
|
|
64
|
+
description="Splits the task across sub-workers it spawns."
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
```
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Shortcut
|
|
2
|
+
|
|
3
|
+
### 원칙
|
|
4
|
+
키보드 단축키를 표시하는 최소 시각 라벨. Menu Item의 trailing 슬롯, Tooltip 힌트, Command Palette 결과 행이 공유한다. 얕은 표면(`--hover`) + hairline border의 “한 덩어리 키캡” 안에 각 심볼이 얕은 gap으로 떨어져 있는 구조. 상호작용·상태 없음 — 순수 시각 라벨이다.
|
|
5
|
+
|
|
6
|
+
`<kbd>` 태그로 렌더해 시맨틱을 유지한다. Menu Item의 `shortcut` prop이 문자열을 받아 내부에서 Shortcut을 자동 렌더링하므로, 대부분의 경우 직접 조합할 필요 없이 Menu Item 경유로 쓴다.
|
|
7
|
+
|
|
8
|
+
### Do
|
|
9
|
+
- **문자열이면 자동 분해에 맡긴다**: `"⌘K"` → `[⌘][K]` 두 키캡. `Array.from`으로 code point 분해하므로 이모지·서로게이트도 안전.
|
|
10
|
+
- **다글자 키는 배열로**: `["Ctrl", "K"]` · `["Shift", "⏎"]` 처럼 원소 하나가 한 키. Windows/Linux 표기가 필요한 서비스 대응.
|
|
11
|
+
- **실제 심볼을 우선**: `⌘` · `⇧` · `⌥` · `⌃` · `⏎` · `⌫` · `⇥` · `␣` · `⎋`. 대문자 축약(`CMD`·`SHIFT`)은 [[feedback_no_uppercase]] 원칙상 금지.
|
|
12
|
+
- **size는 붙는 곳에 맞춘다**: Menu Item `sm` 옆이면 `sm`, `md` 옆이면 `md`. 라벨 폰트보다 커지지 않게 한 단계 낮게 짝지음. Menu Item에서 렌더할 땐 자동으로 사이즈가 짝지어짐.
|
|
13
|
+
- **가장 흔한 사용처는 Menu Item**: `<MenuItem shortcut="⌘K">` — trailing 슬롯 맨 앞에 자동 삽입된다. 직접 `trailingActions`에 넣지 않는다.
|
|
14
|
+
|
|
15
|
+
### Don't
|
|
16
|
+
- 인터랙션을 붙이지 않는다. Shortcut은 “표기”일 뿐 실제 키 핸들링은 상위 컴포넌트(Menu Item, Command Palette, 페이지 전역 훅) 책임.
|
|
17
|
+
- 문장이나 아이콘을 넣지 않는다. 심볼 단위 표기 전용.
|
|
18
|
+
- 색을 hex/tailwind로 직접 지정하지 않는다. 표면·텍스트·border 모두 토큰 경유.
|
|
19
|
+
- 대문자 축약 + wide letter-spacing 조합 금지 ([[feedback_no_uppercase]]).
|
|
20
|
+
- 여러 키캡 사이에 `+`나 `-` 같은 구분자 문자를 넣지 않는다. 심볼 사이 gap이 이미 구분자 역할을 한다.
|
|
21
|
+
|
|
22
|
+
### Fallback
|
|
23
|
+
size 애매하면 `sm`. Menu Item 안에서 쓸 때는 Menu Item이 자동으로 짝지어주므로 신경 쓰지 않아도 됨.
|
|
24
|
+
|
|
25
|
+
### 관련
|
|
26
|
+
- [[components/menu-item]] — 가장 흔한 사용처. `shortcut` prop이 내부에서 Shortcut 렌더링.
|
|
27
|
+
- [[components/tooltip]] · [[components/command-palette]] — 힌트/결과 행에서 함께 등장.
|
|
28
|
+
- [[axes/density]] — 폰트·padding이 Density 축에 반응.
|
|
29
|
+
- [[axes/shape]] — radius가 Shape 축에 반응.
|
|
30
|
+
- [[tokens/color]] — `--hover`(표면) · `--border`(hairline) · `--fg-subtle`(텍스트).
|
|
31
|
+
- [[feedback_no_uppercase]] — 대문자 축약 대신 심볼 사용.
|
|
32
|
+
|
|
33
|
+
### Props 시그니처
|
|
34
|
+
```tsx
|
|
35
|
+
<Shortcut
|
|
36
|
+
size="sm | md" // default: sm
|
|
37
|
+
>
|
|
38
|
+
{children} // string | string[] | ReactNode
|
|
39
|
+
</Shortcut>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`children` 처리 규칙:
|
|
43
|
+
- `string` → `Array.from`으로 심볼 단위 분해 (`"⌘⇧P"` → `[⌘][⇧][P]`, 공백은 무시).
|
|
44
|
+
- `string[]` → 각 원소가 한 키 (다글자 키 대응: `["Ctrl", "K"]`).
|
|
45
|
+
- `ReactNode` → 그대로 렌더 (완전 자유 조합).
|
|
46
|
+
|
|
47
|
+
### Size 가이드
|
|
48
|
+
| size | 폰트 | radius | 언제 |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `sm` | `--text-xs` | `--radius-xs` | **기본.** Menu Item sm/md 옆, Command Palette 결과 행 |
|
|
51
|
+
| `md` | `--label-sm-size` | `--radius-sm` | Menu Item lg 옆 (필요 시), 페이지 문서 안 예시 |
|
|
52
|
+
|
|
53
|
+
### 조합 예시
|
|
54
|
+
```tsx
|
|
55
|
+
// 가장 흔한 사용 — Menu Item shortcut prop 경유
|
|
56
|
+
<MenuItem leadingActions={[<Icon name="search" />]} shortcut="⌘K">
|
|
57
|
+
검색
|
|
58
|
+
</MenuItem>
|
|
59
|
+
|
|
60
|
+
<MenuItem leadingActions={[<Icon name="user" />]} shortcut="⌘,">
|
|
61
|
+
계정 설정
|
|
62
|
+
</MenuItem>
|
|
63
|
+
|
|
64
|
+
// 직접 사용 — Tooltip 힌트
|
|
65
|
+
<Tooltip content={<>붙여넣기 <Shortcut>⌘V</Shortcut></>}>
|
|
66
|
+
<IconButton icon={<Icon name="clipboard" />} aria-label="붙여넣기" />
|
|
67
|
+
</Tooltip>
|
|
68
|
+
|
|
69
|
+
// 다글자 키 (Windows/Linux 표기)
|
|
70
|
+
<Shortcut>{["Ctrl", "K"]}</Shortcut>
|
|
71
|
+
<Shortcut>{["Shift", "⏎"]}</Shortcut>
|
|
72
|
+
|
|
73
|
+
// 조합 심볼
|
|
74
|
+
<Shortcut>⌘⇧P</Shortcut> {/* Command + Shift + P */}
|
|
75
|
+
<Shortcut>⌫</Shortcut> {/* Backspace 단독 */}
|
|
76
|
+
```
|