@conscience-technology/ctds 0.0.0-stage → 0.1.1
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 +254 -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,177 @@
|
|
|
1
|
+
/* SelectableCard ────────────────────────────────────────────────
|
|
2
|
+
카드 전체가 선택 타겟. native <input type="radio|checkbox">를 안에 숨기고,
|
|
3
|
+
label로 감싸 네이티브 시맨틱과 폼 전송을 그대로 받는다.
|
|
4
|
+
좌측 leading 슬롯: mode=radio→dot, checkbox→check, icon→Icon.
|
|
5
|
+
|
|
6
|
+
레이어 구조:
|
|
7
|
+
.surface = 배경·경계·hover tint (position: absolute · inset:0 · pointer-events: none)
|
|
8
|
+
.content = 실제 콘텐츠 (leading + text)
|
|
9
|
+
surface를 따로 두면 border-width 변경 없이도 이중 링 추가 시 layout jitter 방지.
|
|
10
|
+
|
|
11
|
+
Accent 색은 전역 --selected-fg 토큰 (mono=fg, standard/colorful=primary). */
|
|
12
|
+
.root {
|
|
13
|
+
position: relative;
|
|
14
|
+
display: block;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
user-select: none;
|
|
17
|
+
color: var(--fg);
|
|
18
|
+
font-family: var(--font-sans);
|
|
19
|
+
border-radius: var(--radius-md);
|
|
20
|
+
transition: var(--transition-colors), var(--transition-press);
|
|
21
|
+
/* Hover 시 translateY(-1px)로 뜨기 때문에 상단이 잘리지 않도록 hairline 여백 확보.
|
|
22
|
+
부모 컨테이너가 overflow:hidden이거나 정확히 top:0에 붙어있어도 안전. */
|
|
23
|
+
margin-top: var(--hairline);
|
|
24
|
+
}
|
|
25
|
+
.root[data-disabled="true"] {
|
|
26
|
+
cursor: not-allowed;
|
|
27
|
+
opacity: 0.5;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Surface — 카드 경계만. 배경은 투명(부모 표면 그대로 노출).
|
|
31
|
+
hover에서 border 승격, checked에서 accent 경계로 전환. */
|
|
32
|
+
.surface {
|
|
33
|
+
position: absolute;
|
|
34
|
+
inset: 0;
|
|
35
|
+
border-radius: inherit;
|
|
36
|
+
background: transparent;
|
|
37
|
+
border: 1px solid var(--border);
|
|
38
|
+
transition: var(--transition-colors), var(--transition-ring);
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Content — leading + text.
|
|
43
|
+
line-height 시각 보정: 상단은 title text의 line-height 여백이 포함되므로 한 단계 축소. */
|
|
44
|
+
.content {
|
|
45
|
+
position: relative;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: flex-start;
|
|
48
|
+
gap: var(--space-3);
|
|
49
|
+
padding: var(--space-3) var(--space-4) var(--space-4);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Leading — mode별 시각 요소 컨테이너. title 첫 줄 baseline과 맞추기 위해 lh-base 높이. */
|
|
53
|
+
.leading {
|
|
54
|
+
display: inline-flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
height: var(--lh-base);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Radio dot — 선택 전 배경 없음(부모 표면 노출). */
|
|
62
|
+
.radioDot {
|
|
63
|
+
display: inline-block;
|
|
64
|
+
width: var(--space-4);
|
|
65
|
+
height: var(--space-4);
|
|
66
|
+
border-radius: var(--radius-full);
|
|
67
|
+
box-shadow: inset 0 0 0 1px var(--border-strong);
|
|
68
|
+
background: transparent;
|
|
69
|
+
transition:
|
|
70
|
+
box-shadow var(--duration-slow) var(--ease-out),
|
|
71
|
+
background var(--duration-slow) var(--ease-out);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Checkbox — 선택 전 배경 없음(부모 표면 노출). */
|
|
75
|
+
.checkBox {
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
width: var(--space-4);
|
|
80
|
+
height: var(--space-4);
|
|
81
|
+
border-radius: var(--radius-xs);
|
|
82
|
+
border: 1px solid var(--border-strong);
|
|
83
|
+
background: transparent;
|
|
84
|
+
color: transparent;
|
|
85
|
+
transition: var(--transition-colors);
|
|
86
|
+
}
|
|
87
|
+
.checkBox > svg {
|
|
88
|
+
width: 100%;
|
|
89
|
+
height: 100%;
|
|
90
|
+
display: block;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Icon (mode="icon") */
|
|
94
|
+
.icon {
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
justify-content: center;
|
|
98
|
+
color: var(--fg-muted);
|
|
99
|
+
transition: var(--transition-colors);
|
|
100
|
+
}
|
|
101
|
+
.icon > svg {
|
|
102
|
+
width: var(--icon-size-md);
|
|
103
|
+
height: var(--icon-size-md);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Text block */
|
|
107
|
+
.text {
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
gap: var(--space-0_5);
|
|
111
|
+
min-width: 0;
|
|
112
|
+
flex: 1;
|
|
113
|
+
}
|
|
114
|
+
.title {
|
|
115
|
+
font-size: var(--text-base);
|
|
116
|
+
line-height: var(--lh-base);
|
|
117
|
+
font-weight: var(--font-weight-medium);
|
|
118
|
+
color: var(--fg);
|
|
119
|
+
}
|
|
120
|
+
.description {
|
|
121
|
+
font-size: var(--text-sm);
|
|
122
|
+
line-height: var(--lh-sm);
|
|
123
|
+
color: var(--fg-muted);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* ── States ────────────────────────────────────────────────── */
|
|
127
|
+
|
|
128
|
+
/* Hover — 배경/경계 손대지 않고 카드가 살짝 뜨는 움직임 + shadow로만 표현.
|
|
129
|
+
상태(active/checked)와 겹쳐도 signal을 덮지 않는 속성만 사용. */
|
|
130
|
+
.root:hover:not([data-disabled="true"]) {
|
|
131
|
+
transform: translateY(-1px);
|
|
132
|
+
}
|
|
133
|
+
.root:hover:not([data-disabled="true"]) .surface {
|
|
134
|
+
box-shadow: var(--shadow-sm);
|
|
135
|
+
}
|
|
136
|
+
.root:active:not([data-disabled="true"]) {
|
|
137
|
+
transform: translateY(0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Focus — input 포커스가 surface에 ring으로 반영 */
|
|
141
|
+
.input:focus-visible ~ .surface {
|
|
142
|
+
box-shadow: var(--ring-shadow);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Checked — 경계 색만 accent로 승격. stroke 굵기 1px 유지 (Shape 축 영향 없음).
|
|
146
|
+
배경은 유지 — 색공간 오염 방지. */
|
|
147
|
+
.input:checked ~ .surface {
|
|
148
|
+
border-color: var(--selected-fg);
|
|
149
|
+
}
|
|
150
|
+
.input:checked:focus-visible ~ .surface {
|
|
151
|
+
box-shadow: var(--ring-shadow);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Radio dot — checked 시 안쪽 도트를 accent 색으로. stroke는 항상 1px.
|
|
155
|
+
바깥은 투명(부모 표면 노출). */
|
|
156
|
+
.input:checked ~ .content .radioDot {
|
|
157
|
+
box-shadow: inset 0 0 0 1px var(--selected-fg);
|
|
158
|
+
background: radial-gradient(
|
|
159
|
+
circle at center,
|
|
160
|
+
var(--selected-fg) 0,
|
|
161
|
+
var(--selected-fg) 30%,
|
|
162
|
+
transparent 31%
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Checkbox — checked 시 배경/경계/체크 색이 accent로 반전. */
|
|
167
|
+
.input:checked ~ .content .checkBox {
|
|
168
|
+
background: var(--selected-fg);
|
|
169
|
+
border-color: var(--selected-fg);
|
|
170
|
+
color: var(--bg);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Icon (mode="icon") — checked 시 accent 색으로 승격 */
|
|
174
|
+
.input:checked ~ .content .icon {
|
|
175
|
+
color: var(--selected-fg);
|
|
176
|
+
}
|
|
177
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { forwardRef, type InputHTMLAttributes, type ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { Icon, type IconName } from "../Icon";
|
|
6
|
+
|
|
7
|
+
import styles from "./SelectableCard.module.css";
|
|
8
|
+
|
|
9
|
+
export type SelectableCardMode = "radio" | "checkbox" | "icon";
|
|
10
|
+
|
|
11
|
+
export type SelectableCardProps = Omit<
|
|
12
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
13
|
+
"type" | "size" | "title"
|
|
14
|
+
> & {
|
|
15
|
+
/**
|
|
16
|
+
* radio: 단일 선택. 좌측에 라디오 인디케이터.
|
|
17
|
+
* checkbox: 다중 선택. 좌측에 체크 인디케이터.
|
|
18
|
+
* icon: 단일 선택. 좌측에 icon prop이 렌더된다. icon 필수.
|
|
19
|
+
*/
|
|
20
|
+
mode?: SelectableCardMode;
|
|
21
|
+
title: ReactNode;
|
|
22
|
+
description?: ReactNode;
|
|
23
|
+
/** mode="icon"일 때 좌측 슬롯에 렌더. */
|
|
24
|
+
icon?: IconName;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const SelectableCard = forwardRef<HTMLInputElement, SelectableCardProps>(
|
|
28
|
+
(
|
|
29
|
+
{
|
|
30
|
+
className,
|
|
31
|
+
mode = "radio",
|
|
32
|
+
title,
|
|
33
|
+
description,
|
|
34
|
+
icon,
|
|
35
|
+
disabled,
|
|
36
|
+
...rest
|
|
37
|
+
},
|
|
38
|
+
ref,
|
|
39
|
+
) => {
|
|
40
|
+
const rootClass = [styles.root, styles[mode], className ?? ""]
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
.join(" ");
|
|
43
|
+
const inputType = mode === "checkbox" ? "checkbox" : "radio";
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<label className={rootClass} data-disabled={disabled ? "true" : "false"}>
|
|
47
|
+
<input
|
|
48
|
+
ref={ref}
|
|
49
|
+
type={inputType}
|
|
50
|
+
className={`${styles.input} visually-hidden`}
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
{...rest}
|
|
53
|
+
/>
|
|
54
|
+
<span className={styles.surface} aria-hidden />
|
|
55
|
+
<span className={styles.content}>
|
|
56
|
+
<span className={styles.leading} aria-hidden>
|
|
57
|
+
{mode === "radio" && <span className={styles.radioDot} />}
|
|
58
|
+
{mode === "checkbox" && (
|
|
59
|
+
<span className={styles.checkBox}>
|
|
60
|
+
<Icon name="check" />
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
63
|
+
{mode === "icon" && icon && (
|
|
64
|
+
<span className={styles.icon}>
|
|
65
|
+
<Icon name={icon} />
|
|
66
|
+
</span>
|
|
67
|
+
)}
|
|
68
|
+
</span>
|
|
69
|
+
<span className={styles.text}>
|
|
70
|
+
<span className={styles.title}>{title}</span>
|
|
71
|
+
{description && (
|
|
72
|
+
<span className={styles.description}>{description}</span>
|
|
73
|
+
)}
|
|
74
|
+
</span>
|
|
75
|
+
</span>
|
|
76
|
+
</label>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
SelectableCard.displayName = "SelectableCard";
|
|
82
|
+
|
|
83
|
+
export { SelectableCard };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* Shortcut ─────────────────────────────────────────────────────
|
|
2
|
+
키보드 단축키 표시 전용. MenuItem trailing, Tooltip 안, Command Palette 힌트에
|
|
3
|
+
모두 공유되는 최소 컴포넌트. 인터랙션·상태 없음 — 순수 시각 라벨.
|
|
4
|
+
|
|
5
|
+
미니멀 정책: 표면·테두리 없음. 색·간격만으로 심볼 그룹 인상. */
|
|
6
|
+
.root {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
flex-shrink: 0;
|
|
10
|
+
font-family: var(--font-code);
|
|
11
|
+
font-weight: var(--font-weight-regular);
|
|
12
|
+
line-height: 1;
|
|
13
|
+
letter-spacing: var(--tracking-normal);
|
|
14
|
+
color: var(--fg-subtle);
|
|
15
|
+
background: var(--bg-muted);
|
|
16
|
+
border: 0;
|
|
17
|
+
box-shadow: none; /* globals.css의 kbd 전역 그림자 리셋 */
|
|
18
|
+
white-space: nowrap;
|
|
19
|
+
user-select: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* 내부 개별 키 span — 문자열 children이 심볼 단위로 쪼개져 각 하나씩 감싸짐.
|
|
23
|
+
.keyIcon은 phosphor 아이콘이 들어간 슬롯 (Cmd/Shift/Option/Arrow 등),
|
|
24
|
+
그 외는 문자 텍스트 (A~Z, 0~9, `,`, `.`, `/` 등). */
|
|
25
|
+
.key {
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
/* 폭이 좁은 문자(,·.·/)도 자연스럽게 정렬되도록 최소 폭만 확보. */
|
|
30
|
+
min-width: 1ch;
|
|
31
|
+
text-align: center;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.keyIcon {
|
|
35
|
+
/* 아이콘은 이미 정사각 — 강제 min-width 해제해서 자연 폭 유지. */
|
|
36
|
+
min-width: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Sizes ──────────────────────────────────────────────────────── */
|
|
40
|
+
/* MenuItem sm/md 옆에 자연스럽게 붙는 두 사이즈.
|
|
41
|
+
높이는 line-height + padding으로 결정, 별도 min-height 강제 없음.
|
|
42
|
+
심볼 사이 gap은 padding-inline보다 살짝 좁게 — "한 덩어리" 인상 유지. */
|
|
43
|
+
.sm {
|
|
44
|
+
padding-block: var(--space-0_5);
|
|
45
|
+
padding-inline: var(--space-1);
|
|
46
|
+
gap: var(--space-0_5);
|
|
47
|
+
font-size: var(--text-2xs); /* 12 */
|
|
48
|
+
border-radius: var(--radius-xs);
|
|
49
|
+
}
|
|
50
|
+
.md {
|
|
51
|
+
padding-block: var(--space-1);
|
|
52
|
+
padding-inline: var(--space-1_5);
|
|
53
|
+
gap: var(--space-0_5);
|
|
54
|
+
font-size: var(--text-2xs); /* 12 */
|
|
55
|
+
border-radius: var(--radius-sm);
|
|
56
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Command as PhCommand,
|
|
4
|
+
ArrowFatUp as PhShift,
|
|
5
|
+
Option as PhOption,
|
|
6
|
+
Control as PhControl,
|
|
7
|
+
ArrowElbowDownLeft as PhReturn,
|
|
8
|
+
Backspace as PhBackspace,
|
|
9
|
+
ArrowElbowDownRight as PhTab,
|
|
10
|
+
ArrowUp as PhArrowUp,
|
|
11
|
+
ArrowDown as PhArrowDown,
|
|
12
|
+
ArrowLeft as PhArrowLeft,
|
|
13
|
+
ArrowRight as PhArrowRight,
|
|
14
|
+
type Icon as PhIcon,
|
|
15
|
+
} from "@phosphor-icons/react";
|
|
16
|
+
|
|
17
|
+
import styles from "./Shortcut.module.css";
|
|
18
|
+
|
|
19
|
+
export type ShortcutSize = "sm" | "md";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 각 키 토큰. 문자열이면 아래 규칙으로 렌더:
|
|
23
|
+
* - 심볼 매핑에 걸리면 phosphor 아이콘
|
|
24
|
+
* - 다글자 이름("cmd", "shift", "enter", "esc", "space" 등)도 매핑에 걸리면 아이콘
|
|
25
|
+
* - 그 외(단일 알파벳/숫자/문장부호/미매핑)는 텍스트 그대로
|
|
26
|
+
* 완전 자유가 필요하면 ReactNode 전달.
|
|
27
|
+
*/
|
|
28
|
+
export type ShortcutKey = string | ReactNode;
|
|
29
|
+
|
|
30
|
+
export type ShortcutProps = Omit<HTMLAttributes<HTMLElement>, "children"> & {
|
|
31
|
+
size?: ShortcutSize;
|
|
32
|
+
/**
|
|
33
|
+
* 단축키. 사용법:
|
|
34
|
+
* "⌘K" → [⌘][K] (심볼 단위로 자동 분해)
|
|
35
|
+
* "⌘⇧P" → [⌘][⇧][P]
|
|
36
|
+
* ["cmd", "K"] → 이름으로 명시 (다글자 별칭 지원)
|
|
37
|
+
* ["ctrl", "shift", "P"]
|
|
38
|
+
* 매핑 없는 문자열은 그대로 텍스트("esc", "space", "del", 알파벳/숫자 등).
|
|
39
|
+
*/
|
|
40
|
+
children: string | ShortcutKey[] | ReactNode;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const sizeClass: Record<ShortcutSize, string> = {
|
|
44
|
+
sm: styles.sm,
|
|
45
|
+
md: styles.md,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/* 심볼/별칭 → phosphor 아이콘.
|
|
49
|
+
Icon axis와 무관하게 phosphor로 고정 (한 페이지 = 한 family 원칙의 예외 —
|
|
50
|
+
단축키는 시스템 관례 심볼이라 라이브러리 스왑 대상이 아님). */
|
|
51
|
+
const iconMap: Record<string, PhIcon> = {
|
|
52
|
+
// Command
|
|
53
|
+
"⌘": PhCommand,
|
|
54
|
+
cmd: PhCommand,
|
|
55
|
+
command: PhCommand,
|
|
56
|
+
meta: PhCommand,
|
|
57
|
+
// Shift
|
|
58
|
+
"⇧": PhShift,
|
|
59
|
+
shift: PhShift,
|
|
60
|
+
// Option / Alt
|
|
61
|
+
"⌥": PhOption,
|
|
62
|
+
opt: PhOption,
|
|
63
|
+
option: PhOption,
|
|
64
|
+
alt: PhOption,
|
|
65
|
+
// Control
|
|
66
|
+
"⌃": PhControl,
|
|
67
|
+
ctrl: PhControl,
|
|
68
|
+
control: PhControl,
|
|
69
|
+
// Return / Enter
|
|
70
|
+
"⏎": PhReturn,
|
|
71
|
+
"↵": PhReturn,
|
|
72
|
+
enter: PhReturn,
|
|
73
|
+
return: PhReturn,
|
|
74
|
+
// Backspace / Delete
|
|
75
|
+
"⌫": PhBackspace,
|
|
76
|
+
backspace: PhBackspace,
|
|
77
|
+
// Tab
|
|
78
|
+
"⇥": PhTab,
|
|
79
|
+
tab: PhTab,
|
|
80
|
+
// Arrows
|
|
81
|
+
"↑": PhArrowUp,
|
|
82
|
+
up: PhArrowUp,
|
|
83
|
+
"↓": PhArrowDown,
|
|
84
|
+
down: PhArrowDown,
|
|
85
|
+
"←": PhArrowLeft,
|
|
86
|
+
left: PhArrowLeft,
|
|
87
|
+
"→": PhArrowRight,
|
|
88
|
+
right: PhArrowRight,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const ICON_SIZE: Record<ShortcutSize, number> = {
|
|
92
|
+
sm: 11,
|
|
93
|
+
md: 12,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/* 문자열 → 키 토큰 배열.
|
|
97
|
+
Array.from으로 code point 단위 분해 (⌘·⇧ 같은 심볼도 안전).
|
|
98
|
+
공백은 무시. */
|
|
99
|
+
function splitToKeys(input: string): string[] {
|
|
100
|
+
return Array.from(input).filter((ch) => ch !== " ");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function renderKey(
|
|
104
|
+
key: ShortcutKey,
|
|
105
|
+
size: ShortcutSize,
|
|
106
|
+
idx: number,
|
|
107
|
+
): ReactNode {
|
|
108
|
+
if (typeof key !== "string") {
|
|
109
|
+
return (
|
|
110
|
+
<span key={idx} className={styles.key}>
|
|
111
|
+
{key}
|
|
112
|
+
</span>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const lower = key.toLowerCase();
|
|
116
|
+
const IconComp = iconMap[key] ?? iconMap[lower];
|
|
117
|
+
if (IconComp) {
|
|
118
|
+
return (
|
|
119
|
+
<span key={idx} className={`${styles.key} ${styles.keyIcon}`}>
|
|
120
|
+
<IconComp size={ICON_SIZE[size]} />
|
|
121
|
+
</span>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return (
|
|
125
|
+
<span key={idx} className={styles.key}>
|
|
126
|
+
{key}
|
|
127
|
+
</span>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const Shortcut = forwardRef<HTMLElement, ShortcutProps>(
|
|
132
|
+
({ className, size = "sm", children, ...rest }, ref) => {
|
|
133
|
+
const cls = [styles.root, sizeClass[size], className ?? ""]
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.join(" ");
|
|
136
|
+
|
|
137
|
+
let content: ReactNode;
|
|
138
|
+
if (typeof children === "string") {
|
|
139
|
+
content = splitToKeys(children).map((k, i) => renderKey(k, size, i));
|
|
140
|
+
} else if (Array.isArray(children)) {
|
|
141
|
+
content = (children as ShortcutKey[]).map((k, i) => renderKey(k, size, i));
|
|
142
|
+
} else {
|
|
143
|
+
content = children;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<kbd ref={ref} className={cls} {...rest}>
|
|
148
|
+
{content}
|
|
149
|
+
</kbd>
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
Shortcut.displayName = "Shortcut";
|
|
155
|
+
|
|
156
|
+
export { Shortcut };
|