@nexus-cross/design-system 1.0.13 → 1.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.
Files changed (285) hide show
  1. package/claude-rules/nexus/CLAUDE.md +85 -0
  2. package/claude-rules/nexus/commands/nexus-audit.md +79 -0
  3. package/claude-rules/nexus/commands/nexus-component-map.md +85 -0
  4. package/claude-rules/nexus/commands/nexus-token-check.md +68 -0
  5. package/claude-rules/nexus/skills/nexus-design-system/SKILL.md +92 -0
  6. package/cursor-rules/nexus-ui-api.mdc +824 -41
  7. package/cursor-rules/nexus-ui-decisions.mdc +259 -0
  8. package/dist/accordion.js +0 -1
  9. package/dist/accordion.mjs +0 -1
  10. package/dist/alert.js +0 -1
  11. package/dist/alert.mjs +0 -1
  12. package/dist/avatar.js +0 -1
  13. package/dist/avatar.mjs +0 -1
  14. package/dist/badge.js +0 -1
  15. package/dist/badge.mjs +0 -1
  16. package/dist/breadcrumb.js +0 -1
  17. package/dist/breadcrumb.mjs +0 -1
  18. package/dist/button.js +0 -1
  19. package/dist/button.mjs +0 -1
  20. package/dist/carousel.js +0 -1
  21. package/dist/carousel.mjs +0 -1
  22. package/dist/checkbox.js +0 -1
  23. package/dist/checkbox.mjs +0 -1
  24. package/dist/chip.js +0 -1
  25. package/dist/chip.mjs +0 -1
  26. package/dist/chunks/chunk-2Z52NPWB.js +78 -0
  27. package/dist/chunks/chunk-46P52MFM.mjs +56 -0
  28. package/dist/chunks/{chunk-X3CTJ7TD.js → chunk-4KBFVIKX.js} +41 -11
  29. package/dist/chunks/chunk-56ZOOQFE.mjs +514 -0
  30. package/dist/chunks/chunk-5ASTWFJW.js +538 -0
  31. package/dist/chunks/{chunk-33UFQJIO.mjs → chunk-BJMXZJWO.mjs} +16 -5
  32. package/dist/chunks/chunk-EILXBLEV.mjs +5 -0
  33. package/dist/chunks/chunk-G3RLK2HS.js +7 -0
  34. package/dist/chunks/{chunk-YZV6FWE7.js → chunk-JLDQNDFT.js} +16 -5
  35. package/dist/chunks/{chunk-K574BYHQ.js → chunk-K3CK7NTP.js} +22 -4
  36. package/dist/chunks/{chunk-Z4YM7LU3.mjs → chunk-PIGHBDK5.mjs} +22 -4
  37. package/dist/chunks/{chunk-PEIEVKD5.js → chunk-RCIBLLSF.js} +11 -12
  38. package/dist/chunks/{chunk-MMCA33FW.mjs → chunk-RSFLNWOM.mjs} +41 -11
  39. package/dist/chunks/{chunk-K2TBLM3F.mjs → chunk-THBE27U3.mjs} +11 -12
  40. package/dist/client-only.js +0 -1
  41. package/dist/client-only.mjs +0 -1
  42. package/dist/combobox.js +16 -0
  43. package/dist/combobox.mjs +3 -0
  44. package/dist/components/Combobox.d.ts +48 -0
  45. package/dist/components/Combobox.d.ts.map +1 -0
  46. package/dist/components/DataGrid.d.ts +44 -0
  47. package/dist/components/DataGrid.d.ts.map +1 -0
  48. package/dist/components/DataList.d.ts +3 -1
  49. package/dist/components/DataList.d.ts.map +1 -1
  50. package/dist/components/RadioGroup.d.ts +4 -0
  51. package/dist/components/RadioGroup.d.ts.map +1 -1
  52. package/dist/components/Stepper.d.ts.map +1 -1
  53. package/dist/components/ToggleGroup.d.ts +2 -1
  54. package/dist/components/ToggleGroup.d.ts.map +1 -1
  55. package/dist/countdown.js +0 -1
  56. package/dist/countdown.mjs +0 -1
  57. package/dist/counter.js +0 -1
  58. package/dist/counter.mjs +0 -1
  59. package/dist/data-grid.js +14 -0
  60. package/dist/data-grid.mjs +5 -0
  61. package/dist/data-list.js +2 -3
  62. package/dist/data-list.mjs +1 -2
  63. package/dist/date-picker.js +0 -1
  64. package/dist/date-picker.mjs +0 -1
  65. package/dist/divider.js +0 -1
  66. package/dist/divider.mjs +0 -1
  67. package/dist/drawer.js +0 -1
  68. package/dist/drawer.mjs +0 -1
  69. package/dist/dropdown-menu.js +0 -1
  70. package/dist/dropdown-menu.mjs +0 -1
  71. package/dist/ellipsis.js +0 -1
  72. package/dist/ellipsis.mjs +0 -1
  73. package/dist/empty-state.js +0 -1
  74. package/dist/empty-state.mjs +0 -1
  75. package/dist/error-boundary.js +0 -1
  76. package/dist/error-boundary.mjs +0 -1
  77. package/dist/hooks/useCheckDevice.js +0 -1
  78. package/dist/hooks/useCheckDevice.mjs +0 -1
  79. package/dist/hooks/useClickOutside.js +0 -1
  80. package/dist/hooks/useClickOutside.mjs +0 -1
  81. package/dist/hooks/useDraggableBottomSheet.js +0 -1
  82. package/dist/hooks/useDraggableBottomSheet.mjs +0 -1
  83. package/dist/hooks/useDraggableWindow.js +0 -1
  84. package/dist/hooks/useDraggableWindow.mjs +0 -1
  85. package/dist/hooks/useInView.js +0 -1
  86. package/dist/hooks/useInView.mjs +0 -1
  87. package/dist/hooks/useModal.js +0 -1
  88. package/dist/hooks/useModal.mjs +0 -1
  89. package/dist/image-upload.js +0 -1
  90. package/dist/image-upload.mjs +0 -1
  91. package/dist/index.d.ts +4 -2
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +92 -88
  94. package/dist/index.mjs +12 -12
  95. package/dist/infinite-scroll.js +0 -1
  96. package/dist/infinite-scroll.mjs +0 -1
  97. package/dist/marquee.js +0 -1
  98. package/dist/marquee.mjs +0 -1
  99. package/dist/modal/index.js +11 -12
  100. package/dist/modal/index.mjs +2 -3
  101. package/dist/number-input.js +0 -1
  102. package/dist/number-input.mjs +0 -1
  103. package/dist/nx-image.js +0 -1
  104. package/dist/nx-image.mjs +0 -1
  105. package/dist/pagination.js +0 -1
  106. package/dist/pagination.mjs +0 -1
  107. package/dist/popover.js +0 -1
  108. package/dist/popover.mjs +0 -1
  109. package/dist/price-input.js +0 -1
  110. package/dist/price-input.mjs +0 -1
  111. package/dist/progress.js +0 -1
  112. package/dist/progress.mjs +0 -1
  113. package/dist/radio-group.js +5 -6
  114. package/dist/radio-group.mjs +1 -2
  115. package/dist/schemas/_all.json +308 -117
  116. package/dist/schemas/accordion.d.ts.map +1 -1
  117. package/dist/schemas/accordion.json +1 -1
  118. package/dist/schemas/alert.d.ts.map +1 -1
  119. package/dist/schemas/alert.json +1 -1
  120. package/dist/schemas/avatar.d.ts.map +1 -1
  121. package/dist/schemas/avatar.json +1 -1
  122. package/dist/schemas/badge.d.ts.map +1 -1
  123. package/dist/schemas/badge.json +1 -1
  124. package/dist/schemas/breadcrumb.d.ts.map +1 -1
  125. package/dist/schemas/breadcrumb.json +1 -1
  126. package/dist/schemas/button.d.ts.map +1 -1
  127. package/dist/schemas/button.json +1 -1
  128. package/dist/schemas/carousel.d.ts.map +1 -1
  129. package/dist/schemas/carousel.json +1 -1
  130. package/dist/schemas/checkBox.json +1 -1
  131. package/dist/schemas/checkbox.d.ts.map +1 -1
  132. package/dist/schemas/chip.d.ts.map +1 -1
  133. package/dist/schemas/chip.json +1 -1
  134. package/dist/schemas/client-only.d.ts.map +1 -1
  135. package/dist/schemas/clientOnly.json +1 -1
  136. package/dist/schemas/combobox.d.ts +85 -0
  137. package/dist/schemas/combobox.d.ts.map +1 -0
  138. package/dist/schemas/combobox.json +98 -0
  139. package/dist/schemas/comboboxOption.json +30 -0
  140. package/dist/schemas/countdown.d.ts.map +1 -1
  141. package/dist/schemas/countdown.json +1 -1
  142. package/dist/schemas/counter.d.ts.map +1 -1
  143. package/dist/schemas/counter.json +1 -1
  144. package/dist/schemas/data-grid.d.ts +74 -0
  145. package/dist/schemas/data-grid.d.ts.map +1 -0
  146. package/dist/schemas/data-list.d.ts.map +1 -1
  147. package/dist/schemas/dataGrid.json +102 -0
  148. package/dist/schemas/dataList.json +1 -1
  149. package/dist/schemas/date-picker.d.ts.map +1 -1
  150. package/dist/schemas/datePicker.json +1 -1
  151. package/dist/schemas/divider.d.ts.map +1 -1
  152. package/dist/schemas/divider.json +1 -1
  153. package/dist/schemas/drawer.d.ts.map +1 -1
  154. package/dist/schemas/drawer.json +1 -1
  155. package/dist/schemas/dropdown-menu.d.ts.map +1 -1
  156. package/dist/schemas/dropdownMenu.json +1 -1
  157. package/dist/schemas/ellipsis.d.ts.map +1 -1
  158. package/dist/schemas/ellipsis.json +1 -1
  159. package/dist/schemas/empty-state.d.ts.map +1 -1
  160. package/dist/schemas/emptyState.json +1 -1
  161. package/dist/schemas/error-boundary.d.ts.map +1 -1
  162. package/dist/schemas/errorBoundary.json +1 -1
  163. package/dist/schemas/image-upload.d.ts.map +1 -1
  164. package/dist/schemas/imageUpload.json +1 -1
  165. package/dist/schemas/index.d.ts +2 -1
  166. package/dist/schemas/index.d.ts.map +1 -1
  167. package/dist/schemas/infinite-scroll.d.ts.map +1 -1
  168. package/dist/schemas/infiniteScroll.json +1 -1
  169. package/dist/schemas/marquee.d.ts.map +1 -1
  170. package/dist/schemas/marquee.json +1 -1
  171. package/dist/schemas/modal.d.ts.map +1 -1
  172. package/dist/schemas/modalTemplate.json +1 -1
  173. package/dist/schemas/number-input.d.ts.map +1 -1
  174. package/dist/schemas/numberInput.json +1 -1
  175. package/dist/schemas/nx-image.d.ts.map +1 -1
  176. package/dist/schemas/nxImage.json +1 -1
  177. package/dist/schemas/pagination.d.ts.map +1 -1
  178. package/dist/schemas/pagination.json +1 -1
  179. package/dist/schemas/popover.d.ts.map +1 -1
  180. package/dist/schemas/popover.json +1 -1
  181. package/dist/schemas/price-input.d.ts.map +1 -1
  182. package/dist/schemas/priceInput.json +1 -1
  183. package/dist/schemas/progress.d.ts.map +1 -1
  184. package/dist/schemas/progress.json +1 -1
  185. package/dist/schemas/radio-group.d.ts +9 -0
  186. package/dist/schemas/radio-group.d.ts.map +1 -1
  187. package/dist/schemas/radioGroup.json +10 -1
  188. package/dist/schemas/radioItem.json +11 -0
  189. package/dist/schemas/select.d.ts.map +1 -1
  190. package/dist/schemas/select.json +1 -1
  191. package/dist/schemas/skeleton.d.ts.map +1 -1
  192. package/dist/schemas/skeleton.json +1 -1
  193. package/dist/schemas/slider.d.ts.map +1 -1
  194. package/dist/schemas/slider.json +1 -1
  195. package/dist/schemas/spinner.d.ts.map +1 -1
  196. package/dist/schemas/spinner.json +1 -1
  197. package/dist/schemas/stepper.d.ts.map +1 -1
  198. package/dist/schemas/stepper.json +1 -1
  199. package/dist/schemas/switch.d.ts.map +1 -1
  200. package/dist/schemas/switch.json +1 -1
  201. package/dist/schemas/tab.d.ts.map +1 -1
  202. package/dist/schemas/tab.json +1 -1
  203. package/dist/schemas/table.d.ts.map +1 -1
  204. package/dist/schemas/table.json +1 -1
  205. package/dist/schemas/tableRow.json +1 -1
  206. package/dist/schemas/tag-input.d.ts.map +1 -1
  207. package/dist/schemas/tagInput.json +1 -1
  208. package/dist/schemas/tdColumn.json +1 -1
  209. package/dist/schemas/text-area.d.ts.map +1 -1
  210. package/dist/schemas/text-input.d.ts +2 -2
  211. package/dist/schemas/text-input.d.ts.map +1 -1
  212. package/dist/schemas/textArea.json +1 -1
  213. package/dist/schemas/textInput.json +1 -1
  214. package/dist/schemas/toast.d.ts.map +1 -1
  215. package/dist/schemas/toastOptions.json +1 -1
  216. package/dist/schemas/toaster.json +1 -1
  217. package/dist/schemas/toggle-group.d.ts +6 -3
  218. package/dist/schemas/toggle-group.d.ts.map +1 -1
  219. package/dist/schemas/toggleGroup.json +9 -3
  220. package/dist/schemas/tooltip.d.ts.map +1 -1
  221. package/dist/schemas/tooltip.json +1 -1
  222. package/dist/schemas/virtual-scroll.d.ts.map +1 -1
  223. package/dist/schemas/virtualGrid.json +1 -1
  224. package/dist/schemas/virtualList.json +1 -1
  225. package/dist/schemas.js +867 -66
  226. package/dist/schemas.mjs +865 -66
  227. package/dist/select.js +0 -1
  228. package/dist/select.mjs +0 -1
  229. package/dist/skeleton.js +0 -1
  230. package/dist/skeleton.mjs +0 -1
  231. package/dist/slider.js +0 -1
  232. package/dist/slider.mjs +0 -1
  233. package/dist/spinner.js +0 -1
  234. package/dist/spinner.mjs +0 -1
  235. package/dist/stepper.js +3 -4
  236. package/dist/stepper.mjs +1 -2
  237. package/dist/styles/.generated/built.d.ts +1 -1
  238. package/dist/styles/.generated/built.d.ts.map +1 -1
  239. package/dist/styles/layer.js +2 -3
  240. package/dist/styles/layer.mjs +1 -2
  241. package/dist/styles.css +554 -51
  242. package/dist/styles.js +2 -3
  243. package/dist/styles.layered.css +554 -51
  244. package/dist/styles.mjs +1 -2
  245. package/dist/switch.js +0 -1
  246. package/dist/switch.mjs +0 -1
  247. package/dist/tab.js +0 -1
  248. package/dist/tab.mjs +0 -1
  249. package/dist/table.js +0 -1
  250. package/dist/table.mjs +0 -1
  251. package/dist/tag-input.js +0 -1
  252. package/dist/tag-input.mjs +0 -1
  253. package/dist/text-area.js +0 -1
  254. package/dist/text-area.mjs +0 -1
  255. package/dist/text-input.js +0 -1
  256. package/dist/text-input.mjs +0 -1
  257. package/dist/toast.js +0 -1
  258. package/dist/toast.mjs +0 -1
  259. package/dist/toggle-group.js +3 -4
  260. package/dist/toggle-group.mjs +1 -2
  261. package/dist/tooltip.js +0 -1
  262. package/dist/tooltip.mjs +0 -1
  263. package/dist/utils/cn.js +0 -1
  264. package/dist/utils/cn.mjs +0 -1
  265. package/dist/utils/scroll.js +0 -1
  266. package/dist/utils/scroll.mjs +0 -1
  267. package/dist/virtual-scroll.js +0 -1
  268. package/dist/virtual-scroll.mjs +0 -1
  269. package/package.json +14 -8
  270. package/scripts/setup-cursor-rules.cjs +164 -27
  271. package/dist/chunks/chunk-22ULI3BF.js +0 -21
  272. package/dist/chunks/chunk-6ECGMUT6.mjs +0 -5
  273. package/dist/chunks/chunk-CVYXRSXT.mjs +0 -8
  274. package/dist/chunks/chunk-I252NERB.mjs +0 -21
  275. package/dist/chunks/chunk-JNMCYWGY.js +0 -10
  276. package/dist/chunks/chunk-V35IEPRL.js +0 -7
  277. package/dist/components/ThemeProvider.d.ts +0 -25
  278. package/dist/components/ThemeProvider.d.ts.map +0 -1
  279. package/dist/schemas/theme-provider.d.ts +0 -36
  280. package/dist/schemas/theme-provider.d.ts.map +0 -1
  281. package/dist/schemas/themeProvider.json +0 -65
  282. package/dist/theme-provider.js +0 -15
  283. package/dist/theme-provider.mjs +0 -2
  284. package/dist/chunks/{chunk-CWMLTXOH.mjs → chunk-5ZVPTIL3.mjs} +1 -1
  285. package/dist/chunks/{chunk-HFBTS42N.js → chunk-7F4SOLAC.js} +1 -1
@@ -0,0 +1,259 @@
1
+ ---
2
+ description: "@nexus-cross/design-system component selection guide — when to use which component, anti-patterns, and decision matrices for ambiguous cases (Combobox vs Select, Drawer vs Modal, Tooltip vs Popover, etc.)"
3
+ globs: "**/*.tsx,**/*.jsx"
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # NEXUS Component Selection Guide
8
+
9
+ UI 작업 시 어떤 컴포넌트를 골라야 할지 결정하는 가이드. **`ui-components.md`로 1차 매핑 → 모호하면 이 문서 참조.**
10
+
11
+ ## Selection vs. Action
12
+
13
+ 옵션 중 **값을 고르는** 컴포넌트와 **액션을 트리거하는** 컴포넌트는 다릅니다.
14
+
15
+ | 사용자 의도 | 컴포넌트 |
16
+ |---|---|
17
+ | 1개 값 선택 (form field) | Select / Combobox / RadioGroup |
18
+ | 여러 값 선택 (form field) | CheckBox / Combobox(multiple) / TagInput |
19
+ | 액션 메뉴 (저장/삭제/공유 등) | DropdownMenu |
20
+ | 두 상태 토글 (on/off) | Switch |
21
+ | n개 중 하나 선택 (즉시 적용) | ToggleGroup (single) |
22
+ | n개 중 여러개 선택 (즉시 적용) | ToggleGroup (multiple) |
23
+
24
+ > **핵심 차이**: form field는 "값"을 보유하고 submit 시 전송. DropdownMenu는 클릭 시 함수가 실행되는 액션. Switch/ToggleGroup은 즉시 상태 변경 (저장 버튼 없음).
25
+
26
+ ---
27
+
28
+ ## Single-select 입력: Select vs Combobox vs RadioGroup vs ToggleGroup
29
+
30
+ | 상황 | 컴포넌트 | 이유 |
31
+ |---|---|---|
32
+ | 옵션 ≤ 4개, 모두 보여주고 싶음 | **RadioGroup** 또는 **ToggleGroup** | 한눈에 비교, 클릭 1회 |
33
+ | 옵션 ≤ 7개, 공간 절약 필요 | **Select** | 가벼움, 빠른 마운트 |
34
+ | 옵션 ≥ 7개 또는 라벨이 긺 | **Combobox** | 검색·필터·키보드 탐색 |
35
+ | 옵션이 서버에서 비동기로 옴 | **Combobox** (`loading`, `onSearch`) | async 지원 |
36
+ | 옵션이 동적 (사용자가 추가 가능) | **Combobox** + `creatable` 패턴 | 자유 입력 |
37
+ | 시각적 비교가 핵심 (정렬·뷰모드 등) | **ToggleGroup** | 즉시 적용 + 시각 피드백 |
38
+
39
+ ```tsx
40
+ // ❌ 옵션 20개에 Select
41
+ <Select><SelectItem>...</SelectItem>...</Select>
42
+
43
+ // ✅ 옵션 20개에 Combobox
44
+ <Combobox options={options} placeholder="검색하여 선택" />
45
+
46
+ // ❌ 옵션 3개에 Select
47
+ <Select><SelectItem>S</SelectItem><SelectItem>M</SelectItem><SelectItem>L</SelectItem></Select>
48
+
49
+ // ✅ 옵션 3개에 RadioGroup 또는 ToggleGroup
50
+ <RadioGroup><RadioItem value="S">S</RadioItem>...</RadioGroup>
51
+ <ToggleGroup type="single" items={[{value:'S',label:'S'},...]} />
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Multi-select 입력: CheckBox vs Combobox(multiple) vs TagInput vs ToggleGroup(multiple)
57
+
58
+ | 상황 | 컴포넌트 | 이유 |
59
+ |---|---|---|
60
+ | 옵션 ≤ 7개 모두 보여주기 | **CheckBox** 그룹 | 한눈에 체크 상태 비교 |
61
+ | 옵션 ≥ 7개 검색 필요 | **Combobox** (`multiple`) | 칩 + 검색 |
62
+ | 사용자가 자유 입력 (이메일·태그 등) | **TagInput** | enter로 토큰 생성 |
63
+ | 즉시 필터 적용 (검색 페이지의 카테고리 필터) | **ToggleGroup** (`multiple`) | 토글 UI |
64
+
65
+ ---
66
+
67
+ ## 오버레이: Modal vs Drawer vs Popover vs Tooltip vs DropdownMenu
68
+
69
+ | 상황 | 컴포넌트 | 이유 |
70
+ |---|---|---|
71
+ | 사용자 결정 강제 (확인/삭제/저장) | **Modal** | 백드롭 + focus trap |
72
+ | 부수 정보 또는 부가 작업 | **Drawer** | 메인 콘텐츠 가림 적음 |
73
+ | trigger 옆에 작은 컨텍스트 정보 | **Popover** | 위치 anchor, 클릭 dismiss |
74
+ | trigger hover 시 짧은 힌트 | **Tooltip** | hover/focus만, 클릭 X |
75
+ | 액션 메뉴 (저장/삭제/공유) | **DropdownMenu** | role=menu, 화살표 키 탐색 |
76
+
77
+ ```tsx
78
+ // ❌ 단순 도움말에 Modal
79
+ <Modal title="도움말">짧은 설명...</Modal>
80
+
81
+ // ✅ 단순 도움말에 Tooltip
82
+ <Tooltip content="짧은 설명...">
83
+ <button><HelpIcon /></button>
84
+ </Tooltip>
85
+
86
+ // ❌ 액션 메뉴에 Popover 안에 button 직접 작성
87
+ <Popover><button onClick={save}>저장</button>...</Popover>
88
+
89
+ // ✅ 액션 메뉴는 DropdownMenu (a11y 자동)
90
+ <DropdownMenu items={[{label:'저장', onSelect: save}, ...]} />
91
+ ```
92
+
93
+ **판단 기준**:
94
+ - "사용자가 메인 작업으로부터 잠깐 분리되어야 하는가?" → Modal
95
+ - "사용자가 사이드 패널에서 보조 작업을 하는가?" → Drawer
96
+ - "trigger 가까이에 작은 보조 UI가 필요한가?" → Popover
97
+ - "텍스트만 잠깐 보여주면 되는가?" → Tooltip
98
+
99
+ ---
100
+
101
+ ## 알림: toast vs Alert vs Modal vs EmptyState
102
+
103
+ | 상황 | 컴포넌트 | 이유 |
104
+ |---|---|---|
105
+ | 일시적 피드백 (저장 완료, 복사됨 등) | **toast()** | 자동 dismiss, non-blocking |
106
+ | 영구적 인라인 경고 (form 위 에러 요약) | **Alert** | 페이지 영역에 고정 |
107
+ | 파괴적 액션 확인 ("정말 삭제?") | **modal.confirm()** | blocking, 명확한 결정 강제 |
108
+ | 데이터 0개 상태 (목록·표) | **EmptyState** | 다음 액션 유도 |
109
+ | 페이지 전체 에러 | **EmptyState** (variant: error) | 회복 액션 제공 |
110
+
111
+ ```tsx
112
+ // ❌ "저장됨" 알림에 Alert
113
+ <Alert>저장되었습니다</Alert>
114
+
115
+ // ✅ "저장됨"은 toast
116
+ toast.success('저장되었습니다');
117
+
118
+ // ❌ 삭제 확인에 toast (사용자가 못 보고 지나칠 수 있음)
119
+ toast('삭제하시겠습니까?', { action: { ... }});
120
+
121
+ // ✅ 삭제 확인은 modal.confirm
122
+ const ok = await modal.confirm({ title: '정말 삭제하시겠습니까?', danger: true });
123
+ ```
124
+
125
+ ---
126
+
127
+ ## 진행 상태: Spinner vs Skeleton vs Progress
128
+
129
+ | 상황 | 컴포넌트 |
130
+ |---|---|
131
+ | 짧은 로딩 (< 1초), 결과만 기다림 | **Spinner** |
132
+ | 긴 로딩, 레이아웃 미리 보여주기 | **Skeleton** (구조 그대로) |
133
+ | 진행률을 알 수 있음 (업로드·동기화) | **Progress** |
134
+ | 무한 스트림 더 불러오기 | (UI 없음) **InfiniteScroll** + 하단 Spinner |
135
+
136
+ > Skeleton은 항상 **실제 컴포넌트와 동일한 형태**여야 함. 다른 모양이면 의미 없음.
137
+
138
+ ---
139
+
140
+ ## 리스트: DataList vs DataGrid vs VirtualList vs Carousel vs InfiniteScroll
141
+
142
+ | 상황 | 컴포넌트 |
143
+ |---|---|
144
+ | 단순 카드/항목 그리드 | **DataList** (간단), **DataGrid** (정렬·필터) |
145
+ | 항목 수 ≥ 100, 모두 같은 높이 | **VirtualList** |
146
+ | 가로 슬라이드 (배너·갤러리) | **Carousel** |
147
+ | 무한 스크롤 페이지네이션 | **InfiniteScroll** |
148
+ | 페이지 단위 명시적 이동 | **Pagination** |
149
+
150
+ ---
151
+
152
+ ## 입력: TextInput vs TextArea vs NumberInput vs PriceInput vs DatePicker
153
+
154
+ | 데이터 타입 | 컴포넌트 |
155
+ |---|---|
156
+ | 짧은 텍스트 (이름·이메일·검색) | **TextInput** |
157
+ | 긴 텍스트 (메모·설명) | **TextArea** |
158
+ | 정수·소수 (수량·점수) | **NumberInput** |
159
+ | 통화 (천 단위 콤마·원/달러) | **PriceInput** |
160
+ | 날짜·기간 | **DatePicker** |
161
+ | 파일 (이미지) | **ImageUpload** |
162
+
163
+ > ❌ `TextInput type="number"` 금지. `NumberInput` 쓸 것 (단위·step·키보드 ↑↓ 자동).
164
+
165
+ ---
166
+
167
+ ## Tab vs ToggleGroup vs RadioGroup
168
+
169
+ 세 개 다 "n개 중 하나 선택"인데 의미가 다릅니다.
170
+
171
+ | 컴포넌트 | 의미 | 예시 |
172
+ |---|---|---|
173
+ | **Tab** | 영역 전환 (콘텐츠 스왑) | 설정 페이지의 General/Security/Billing |
174
+ | **ToggleGroup** | 즉시 적용되는 옵션 | 정렬: 최신순/인기순, 뷰: 그리드/리스트 |
175
+ | **RadioGroup** | form 필드의 단일 선택 | 결제 방법: 카드/계좌이체 |
176
+
177
+ ```tsx
178
+ // ❌ 페이지 영역 전환에 RadioGroup
179
+ <RadioGroup onValueChange={setView}>...</RadioGroup>
180
+ {view === 'general' && <GeneralPanel />}
181
+
182
+ // ✅ 페이지 영역 전환은 Tab
183
+ <Tab.Root>
184
+ <Tab.List>
185
+ <Tab.Trigger value="general">General</Tab.Trigger>
186
+ ...
187
+ </Tab.List>
188
+ <Tab.Content value="general"><GeneralPanel /></Tab.Content>
189
+ </Tab.Root>
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Anti-patterns 자주 발견
195
+
196
+ ```tsx
197
+ // ❌ raw 버튼 + 임의 색상
198
+ <button className="bg-blue-500 text-white px-4 py-2">저장</button>
199
+ // ✅
200
+ <Button semantic="primary">저장</Button>
201
+
202
+ // ❌ raw input + 라벨 따로
203
+ <label>이메일<input type="email" /></label>
204
+ // ✅
205
+ <TextInput type="email" label="이메일" />
206
+
207
+ // ❌ Tooltip 안에 클릭 가능한 콘텐츠
208
+ <Tooltip content={<button onClick={...}>액션</button>}>...</Tooltip>
209
+ // ✅ 클릭 가능 콘텐츠는 Popover 또는 DropdownMenu
210
+ <Popover content={<Button onClick={...}>액션</Button>}>...</Popover>
211
+
212
+ // ❌ 토글 스위치에 CheckBox
213
+ <CheckBox label="알림 받기" />
214
+ // ✅ 즉시 적용되는 binary 설정은 Switch
215
+ <Switch label="알림 받기" />
216
+
217
+ // ❌ 동일 form 안에 비슷한 모양 컴포넌트 혼용
218
+ <RadioGroup>...</RadioGroup>
219
+ <ToggleGroup type="single">...</ToggleGroup>
220
+ // ✅ form field 통일 — 같은 form은 Radio 또는 Select 한 가지로
221
+
222
+ // ❌ 직접 만든 dropdown
223
+ <div onClick={() => setOpen(!open)}>{open && <div>...</div>}</div>
224
+ // ✅
225
+ <DropdownMenu items={...} />
226
+
227
+ // ❌ !important로 NEXUS 스타일 강제 변경
228
+ <Button className="!bg-red-500">...</Button>
229
+ // ✅ semantic prop 사용
230
+ <Button semantic="danger">...</Button>
231
+ ```
232
+
233
+ ---
234
+
235
+ ## 결정 트리 (가장 자주 묻는 케이스)
236
+
237
+ ```
238
+ "드롭다운이 필요해"
239
+ ├─ 값 선택? ─ 옵션 ≤7? ─ Select
240
+ │ ─ 옵션 >7? ─ Combobox
241
+ └─ 액션 메뉴? ─ DropdownMenu
242
+
243
+ "체크 같은 거 필요해"
244
+ ├─ 즉시 적용? ─ Switch (1개) / ToggleGroup (n개)
245
+ ├─ form field 다중 선택? ─ CheckBox 그룹
246
+ └─ form field 단일 선택? ─ RadioGroup
247
+
248
+ "팝업 띄워줘"
249
+ ├─ 결정 강제? ─ Modal
250
+ ├─ 보조 작업? ─ Drawer
251
+ ├─ 짧은 힌트? ─ Tooltip
252
+ ├─ trigger 옆 정보? ─ Popover
253
+ └─ 액션 메뉴? ─ DropdownMenu
254
+
255
+ "알림 보여줘"
256
+ ├─ 일시적? ─ toast()
257
+ ├─ 인라인 영구? ─ Alert
258
+ └─ 확인 필요? ─ modal.confirm()
259
+ ```
package/dist/accordion.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkCSJDDREF_js = require('./chunks/chunk-CSJDDREF.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
@@ -1,3 +1,2 @@
1
1
  export { Accordion, AccordionContent, AccordionItem, AccordionRoot, AccordionTrigger } from './chunks/chunk-XG6QG65W.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/alert.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunk5PQ3UCKF_js = require('./chunks/chunk-5PQ3UCKF.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/alert.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { Alert, alertVariants } from './chunks/chunk-2UPGFY6E.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/avatar.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkMA2VCCIY_js = require('./chunks/chunk-MA2VCCIY.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/avatar.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { Avatar, avatarVariants } from './chunks/chunk-YLO4UKSC.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/badge.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkS2GMEC43_js = require('./chunks/chunk-S2GMEC43.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/badge.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { Badge, badgeVariants } from './chunks/chunk-CUTMLBC3.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkWATCVNBT_js = require('./chunks/chunk-WATCVNBT.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
@@ -1,3 +1,2 @@
1
1
  export { Breadcrumb, BreadcrumbItem } from './chunks/chunk-DDY7ENHX.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/button.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkP3DZKXG4_js = require('./chunks/chunk-P3DZKXG4.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/button.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { Button, buttonVariants } from './chunks/chunk-VVXQZ4XH.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/carousel.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkYCG4FZC3_js = require('./chunks/chunk-YCG4FZC3.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/carousel.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { Carousel, CarouselDots, CarouselNext, CarouselPrev, CarouselSlide, useCarousel } from './chunks/chunk-3HHJORN7.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/checkbox.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkJ5ZKGPBY_js = require('./chunks/chunk-J5ZKGPBY.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/checkbox.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { CheckBox, boxVariants, checkBoxVariants } from './chunks/chunk-MRRKW5QN.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
package/dist/chip.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var chunkWKCXACMZ_js = require('./chunks/chunk-WKCXACMZ.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
- require('./chunks/chunk-JNMCYWGY.js');
6
5
 
7
6
 
8
7
 
package/dist/chip.mjs CHANGED
@@ -1,3 +1,2 @@
1
1
  export { Chip, chipVariants } from './chunks/chunk-RX5UKRYK.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
3
- import './chunks/chunk-CVYXRSXT.mjs';
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ var chunkRCIBLLSF_js = require('./chunk-RCIBLLSF.js');
4
+ var chunkCZC76ZD5_js = require('./chunk-CZC76ZD5.js');
5
+ var React = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
+
28
+ var BREAKPOINT_VAR_MAP = {
29
+ base: "--nexus-dg-cols",
30
+ sm: "--nexus-dg-cols-sm",
31
+ md: "--nexus-dg-cols-md",
32
+ lg: "--nexus-dg-cols-lg",
33
+ xl: "--nexus-dg-cols-xl",
34
+ "2xl": "--nexus-dg-cols-2xl"
35
+ };
36
+ function buildColumnsStyle(columns) {
37
+ const style = {};
38
+ if (typeof columns === "number") {
39
+ style[BREAKPOINT_VAR_MAP.base] = columns;
40
+ return style;
41
+ }
42
+ for (const key of Object.keys(BREAKPOINT_VAR_MAP)) {
43
+ const value = columns[key];
44
+ if (typeof value === "number") {
45
+ style[BREAKPOINT_VAR_MAP[key]] = value;
46
+ }
47
+ }
48
+ return style;
49
+ }
50
+ function DataGrid({
51
+ list,
52
+ columns,
53
+ gap,
54
+ className,
55
+ ...rest
56
+ }) {
57
+ const gridStyle = React__namespace.useMemo(() => {
58
+ const style = {
59
+ ...buildColumnsStyle(columns)
60
+ };
61
+ if (gap !== void 0) {
62
+ style["--nexus-dg-gap"] = typeof gap === "number" ? `${gap}px` : gap;
63
+ }
64
+ return style;
65
+ }, [columns, gap]);
66
+ return /* @__PURE__ */ jsxRuntime.jsx(
67
+ chunkRCIBLLSF_js.DataList,
68
+ {
69
+ list,
70
+ className: chunkCZC76ZD5_js.cn("nexus-data-grid", className),
71
+ style: gridStyle,
72
+ ...rest
73
+ }
74
+ );
75
+ }
76
+ DataGrid.displayName = "DataGrid";
77
+
78
+ exports.DataGrid = DataGrid;
@@ -0,0 +1,56 @@
1
+ import { DataList } from './chunk-THBE27U3.mjs';
2
+ import { cn } from './chunk-MCKOWMLS.mjs';
3
+ import * as React from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var BREAKPOINT_VAR_MAP = {
7
+ base: "--nexus-dg-cols",
8
+ sm: "--nexus-dg-cols-sm",
9
+ md: "--nexus-dg-cols-md",
10
+ lg: "--nexus-dg-cols-lg",
11
+ xl: "--nexus-dg-cols-xl",
12
+ "2xl": "--nexus-dg-cols-2xl"
13
+ };
14
+ function buildColumnsStyle(columns) {
15
+ const style = {};
16
+ if (typeof columns === "number") {
17
+ style[BREAKPOINT_VAR_MAP.base] = columns;
18
+ return style;
19
+ }
20
+ for (const key of Object.keys(BREAKPOINT_VAR_MAP)) {
21
+ const value = columns[key];
22
+ if (typeof value === "number") {
23
+ style[BREAKPOINT_VAR_MAP[key]] = value;
24
+ }
25
+ }
26
+ return style;
27
+ }
28
+ function DataGrid({
29
+ list,
30
+ columns,
31
+ gap,
32
+ className,
33
+ ...rest
34
+ }) {
35
+ const gridStyle = React.useMemo(() => {
36
+ const style = {
37
+ ...buildColumnsStyle(columns)
38
+ };
39
+ if (gap !== void 0) {
40
+ style["--nexus-dg-gap"] = typeof gap === "number" ? `${gap}px` : gap;
41
+ }
42
+ return style;
43
+ }, [columns, gap]);
44
+ return /* @__PURE__ */ jsx(
45
+ DataList,
46
+ {
47
+ list,
48
+ className: cn("nexus-data-grid", className),
49
+ style: gridStyle,
50
+ ...rest
51
+ }
52
+ );
53
+ }
54
+ DataGrid.displayName = "DataGrid";
55
+
56
+ export { DataGrid };
@@ -41,10 +41,38 @@ var stepperVariants = classVarianceAuthority.cva("nexus-stepper", {
41
41
  size: "md"
42
42
  }
43
43
  });
44
- var CheckIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.5 8.5l3 3 6-6", strokeLinecap: "round", strokeLinejoin: "round" }) });
44
+ var CheckIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx(
45
+ "svg",
46
+ {
47
+ className,
48
+ viewBox: "0 0 14 14",
49
+ fill: "none",
50
+ stroke: "currentColor",
51
+ strokeWidth: "2",
52
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2.5 7.5l3 3 6-6", strokeLinecap: "round", strokeLinejoin: "round" })
53
+ }
54
+ );
45
55
  var Stepper = React__namespace.forwardRef(
46
- ({ className, orientation, size, steps, current = 0, status = "process", ...props }, ref) => {
56
+ ({
57
+ className,
58
+ orientation,
59
+ size,
60
+ steps,
61
+ current = 0,
62
+ status = "process",
63
+ ...props
64
+ }, ref) => {
47
65
  const isHorizontal = orientation !== "vertical";
66
+ const prevCurrentRef = React__namespace.useRef(current);
67
+ const [animatingIdx, setAnimatingIdx] = React__namespace.useState(null);
68
+ React__namespace.useEffect(() => {
69
+ if (prevCurrentRef.current !== current) {
70
+ setAnimatingIdx(current);
71
+ prevCurrentRef.current = current;
72
+ const timer = setTimeout(() => setAnimatingIdx(null), 600);
73
+ return () => clearTimeout(timer);
74
+ }
75
+ }, [current]);
48
76
  return /* @__PURE__ */ jsxRuntime.jsx(
49
77
  "div",
50
78
  {
@@ -56,12 +84,15 @@ var Stepper = React__namespace.forwardRef(
56
84
  const state = i < current ? "completed" : i === current ? status === "error" ? "error" : "active" : "pending";
57
85
  const isFirst = i === 0;
58
86
  const isLast = i === steps.length - 1;
59
- const prevCompleted = i <= current;
60
- const nextCompleted = i < current;
87
+ const isAnimating = animatingIdx === i;
61
88
  return /* @__PURE__ */ jsxRuntime.jsxs(
62
89
  "div",
63
90
  {
64
- className: chunkCZC76ZD5_js.cn("nexus-stepper__step", `nexus-stepper__step--${state}`),
91
+ className: chunkCZC76ZD5_js.cn(
92
+ "nexus-stepper__step",
93
+ `nexus-stepper__step--${state}`,
94
+ isAnimating && "nexus-stepper__step--animating"
95
+ ),
65
96
  children: [
66
97
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nexus-stepper__indicator-wrap", children: isHorizontal ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
67
98
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -69,8 +100,7 @@ var Stepper = React__namespace.forwardRef(
69
100
  {
70
101
  className: chunkCZC76ZD5_js.cn(
71
102
  "nexus-stepper__connector",
72
- isFirst && "nexus-stepper__connector--hidden",
73
- prevCompleted && "nexus-stepper__connector--completed"
103
+ isFirst && "nexus-stepper__connector--hidden"
74
104
  )
75
105
  }
76
106
  ),
@@ -80,14 +110,14 @@ var Stepper = React__namespace.forwardRef(
80
110
  {
81
111
  className: chunkCZC76ZD5_js.cn(
82
112
  "nexus-stepper__connector",
83
- isLast && "nexus-stepper__connector--hidden",
84
- nextCompleted && "nexus-stepper__connector--completed"
113
+ isLast && "nexus-stepper__connector--hidden"
85
114
  )
86
115
  }
87
116
  )
88
117
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
89
- !isFirst && /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkCZC76ZD5_js.cn("nexus-stepper__connector", prevCompleted && "nexus-stepper__connector--completed") }),
90
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nexus-stepper__indicator", children: state === "completed" ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "nexus-stepper__check" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: i + 1 }) })
118
+ !isFirst && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nexus-stepper__connector" }),
119
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nexus-stepper__indicator", children: state === "completed" ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "nexus-stepper__check" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: i + 1 }) }),
120
+ !isLast && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "nexus-stepper__connector" })
91
121
  ] }) }),
92
122
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nexus-stepper__content", children: [
93
123
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "nexus-stepper__label", children: step.label }),