@nexus-cross/design-system 1.0.3-beta.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.
Files changed (256) hide show
  1. package/cursor-rules/nexus-project-setup.mdc +188 -0
  2. package/cursor-rules/nexus-ui-api.mdc +698 -0
  3. package/cursor-rules/nexus-ui-components.mdc +104 -0
  4. package/dist/accordion.d.mts +28 -0
  5. package/dist/accordion.d.ts +28 -0
  6. package/dist/accordion.js +28 -0
  7. package/dist/accordion.mjs +3 -0
  8. package/dist/avatar.d.mts +17 -0
  9. package/dist/avatar.d.ts +17 -0
  10. package/dist/avatar.js +16 -0
  11. package/dist/avatar.mjs +3 -0
  12. package/dist/button.d.mts +15 -0
  13. package/dist/button.d.ts +15 -0
  14. package/dist/button.js +16 -0
  15. package/dist/button.mjs +3 -0
  16. package/dist/carousel.d.mts +36 -0
  17. package/dist/carousel.d.ts +36 -0
  18. package/dist/carousel.js +32 -0
  19. package/dist/carousel.mjs +3 -0
  20. package/dist/checkbox.d.mts +21 -0
  21. package/dist/checkbox.d.ts +21 -0
  22. package/dist/checkbox.js +20 -0
  23. package/dist/checkbox.mjs +3 -0
  24. package/dist/chip.d.mts +16 -0
  25. package/dist/chip.d.ts +16 -0
  26. package/dist/chip.js +16 -0
  27. package/dist/chip.mjs +3 -0
  28. package/dist/chunks/chunk-22ULI3BF.js +21 -0
  29. package/dist/chunks/chunk-2JTPRBHZ.mjs +36 -0
  30. package/dist/chunks/chunk-377KBJQG.js +74 -0
  31. package/dist/chunks/chunk-3CHRUKSJ.mjs +120 -0
  32. package/dist/chunks/chunk-4ITJA3IS.mjs +83 -0
  33. package/dist/chunks/chunk-4J3GCZ7W.mjs +102 -0
  34. package/dist/chunks/chunk-53A2SL62.js +218 -0
  35. package/dist/chunks/chunk-54IA2P2Z.mjs +40 -0
  36. package/dist/chunks/chunk-5I2VRWWU.js +172 -0
  37. package/dist/chunks/chunk-5TBXVD56.js +88 -0
  38. package/dist/chunks/chunk-6DZVNFVY.js +82 -0
  39. package/dist/chunks/chunk-6FHK6CBR.js +117 -0
  40. package/dist/chunks/chunk-6H7V2I3X.mjs +270 -0
  41. package/dist/chunks/chunk-6MRM5K2N.js +106 -0
  42. package/dist/chunks/chunk-6NTASYZO.js +132 -0
  43. package/dist/chunks/chunk-76CY4STF.js +165 -0
  44. package/dist/chunks/chunk-76K6KXCT.js +100 -0
  45. package/dist/chunks/chunk-7MT3QYE6.js +92 -0
  46. package/dist/chunks/chunk-7OEK2KX3.mjs +81 -0
  47. package/dist/chunks/chunk-AKIBUO5A.mjs +83 -0
  48. package/dist/chunks/chunk-AOVU67NI.mjs +139 -0
  49. package/dist/chunks/chunk-AOXXE5UQ.mjs +14 -0
  50. package/dist/chunks/chunk-AWBGWBFS.js +135 -0
  51. package/dist/chunks/chunk-AZ2URLDD.js +39 -0
  52. package/dist/chunks/chunk-BEA727LO.mjs +108 -0
  53. package/dist/chunks/chunk-C3E7CSKG.mjs +115 -0
  54. package/dist/chunks/chunk-C6H2UNOX.js +83 -0
  55. package/dist/chunks/chunk-CVYXRSXT.mjs +8 -0
  56. package/dist/chunks/chunk-EIOP4DOE.mjs +292 -0
  57. package/dist/chunks/chunk-FA2OPP3U.mjs +140 -0
  58. package/dist/chunks/chunk-FHPHDK3O.mjs +89 -0
  59. package/dist/chunks/chunk-FKHW7QYG.js +725 -0
  60. package/dist/chunks/chunk-FKZI2HTI.js +104 -0
  61. package/dist/chunks/chunk-G4XJG7XI.js +66 -0
  62. package/dist/chunks/chunk-GMIGQ5VP.mjs +86 -0
  63. package/dist/chunks/chunk-GSLIY6WW.js +109 -0
  64. package/dist/chunks/chunk-HHXDOKXY.js +108 -0
  65. package/dist/chunks/chunk-HUPAHDJ7.js +273 -0
  66. package/dist/chunks/chunk-I252NERB.mjs +21 -0
  67. package/dist/chunks/chunk-I7YJB2F5.js +143 -0
  68. package/dist/chunks/chunk-IB5UCYQY.mjs +66 -0
  69. package/dist/chunks/chunk-IJG7J2VU.mjs +148 -0
  70. package/dist/chunks/chunk-INP2AH3B.js +27 -0
  71. package/dist/chunks/chunk-IUNNTSD2.mjs +195 -0
  72. package/dist/chunks/chunk-JNMCYWGY.js +10 -0
  73. package/dist/chunks/chunk-JZ3PWHKS.mjs +51 -0
  74. package/dist/chunks/chunk-KWPIEHD2.mjs +78 -0
  75. package/dist/chunks/chunk-LBKBCI2K.mjs +44 -0
  76. package/dist/chunks/chunk-LVTD2UQN.mjs +48 -0
  77. package/dist/chunks/chunk-MPKRXMCJ.js +93 -0
  78. package/dist/chunks/chunk-NCQDOPBR.mjs +86 -0
  79. package/dist/chunks/chunk-NHDGKOAM.js +104 -0
  80. package/dist/chunks/chunk-OTGS6BDQ.mjs +25 -0
  81. package/dist/chunks/chunk-Q2TMXHPK.js +178 -0
  82. package/dist/chunks/chunk-Q7GQVAYY.js +88 -0
  83. package/dist/chunks/chunk-Q7H6LCNN.js +169 -0
  84. package/dist/chunks/chunk-QJNQCLMV.js +25 -0
  85. package/dist/chunks/chunk-QK6NCII4.js +36 -0
  86. package/dist/chunks/chunk-RLP3U52D.mjs +153 -0
  87. package/dist/chunks/chunk-T2IY2TSR.js +43 -0
  88. package/dist/chunks/chunk-TLTEUIBY.js +112 -0
  89. package/dist/chunks/chunk-TPBKQ3WC.js +303 -0
  90. package/dist/chunks/chunk-TR5JBBEA.mjs +116 -0
  91. package/dist/chunks/chunk-TWHDXCKR.js +61 -0
  92. package/dist/chunks/chunk-U56AGSLE.mjs +106 -0
  93. package/dist/chunks/chunk-U6KOUYWX.mjs +66 -0
  94. package/dist/chunks/chunk-U76LT5GE.js +70 -0
  95. package/dist/chunks/chunk-UDQXLI5Y.mjs +81 -0
  96. package/dist/chunks/chunk-UH667FUK.mjs +712 -0
  97. package/dist/chunks/chunk-UR6JOKVB.mjs +65 -0
  98. package/dist/chunks/chunk-VH5FF6DZ.mjs +38 -0
  99. package/dist/chunks/chunk-WJ2OVQD3.mjs +105 -0
  100. package/dist/chunks/chunk-WNFJ4NJN.mjs +55 -0
  101. package/dist/chunks/chunk-WSWD5ZUJ.js +106 -0
  102. package/dist/chunks/chunk-XALPBGSC.mjs +23 -0
  103. package/dist/chunks/chunk-XEHFB62A.js +82 -0
  104. package/dist/chunks/chunk-YEGPB7A7.js +83 -0
  105. package/dist/chunks/chunk-YEWKPWK3.mjs +80 -0
  106. package/dist/chunks/chunk-Z7OKV6NW.mjs +59 -0
  107. package/dist/chunks/chunk-ZCMKIB5U.js +140 -0
  108. package/dist/client-only.d.mts +13 -0
  109. package/dist/client-only.d.ts +13 -0
  110. package/dist/client-only.js +11 -0
  111. package/dist/client-only.mjs +2 -0
  112. package/dist/countdown.d.mts +27 -0
  113. package/dist/countdown.d.ts +27 -0
  114. package/dist/countdown.js +16 -0
  115. package/dist/countdown.mjs +3 -0
  116. package/dist/counter.d.mts +15 -0
  117. package/dist/counter.d.ts +15 -0
  118. package/dist/counter.js +11 -0
  119. package/dist/counter.mjs +2 -0
  120. package/dist/divider.d.mts +14 -0
  121. package/dist/divider.d.ts +14 -0
  122. package/dist/divider.js +16 -0
  123. package/dist/divider.mjs +3 -0
  124. package/dist/drawer.d.mts +42 -0
  125. package/dist/drawer.d.ts +42 -0
  126. package/dist/drawer.js +44 -0
  127. package/dist/drawer.mjs +3 -0
  128. package/dist/ellipsis.d.mts +16 -0
  129. package/dist/ellipsis.d.ts +16 -0
  130. package/dist/ellipsis.js +12 -0
  131. package/dist/ellipsis.mjs +3 -0
  132. package/dist/error-boundary.d.mts +20 -0
  133. package/dist/error-boundary.d.ts +20 -0
  134. package/dist/error-boundary.js +11 -0
  135. package/dist/error-boundary.mjs +2 -0
  136. package/dist/hooks/useCheckDevice.d.mts +47 -0
  137. package/dist/hooks/useCheckDevice.d.ts +47 -0
  138. package/dist/hooks/useCheckDevice.js +8 -0
  139. package/dist/hooks/useCheckDevice.mjs +2 -0
  140. package/dist/hooks/useClickOutside.d.mts +12 -0
  141. package/dist/hooks/useClickOutside.d.ts +12 -0
  142. package/dist/hooks/useClickOutside.js +8 -0
  143. package/dist/hooks/useClickOutside.mjs +2 -0
  144. package/dist/hooks/useDraggableBottomSheet.d.mts +24 -0
  145. package/dist/hooks/useDraggableBottomSheet.d.ts +24 -0
  146. package/dist/hooks/useDraggableBottomSheet.js +11 -0
  147. package/dist/hooks/useDraggableBottomSheet.mjs +2 -0
  148. package/dist/hooks/useDraggableWindow.d.mts +21 -0
  149. package/dist/hooks/useDraggableWindow.d.ts +21 -0
  150. package/dist/hooks/useDraggableWindow.js +11 -0
  151. package/dist/hooks/useDraggableWindow.mjs +2 -0
  152. package/dist/hooks/useInView.d.mts +14 -0
  153. package/dist/hooks/useInView.d.ts +14 -0
  154. package/dist/hooks/useInView.js +17 -0
  155. package/dist/hooks/useInView.mjs +2 -0
  156. package/dist/hooks/useModal.d.mts +2 -0
  157. package/dist/hooks/useModal.d.ts +2 -0
  158. package/dist/hooks/useModal.js +11 -0
  159. package/dist/hooks/useModal.mjs +2 -0
  160. package/dist/index.d.mts +74 -0
  161. package/dist/index.d.ts +74 -0
  162. package/dist/index.js +633 -0
  163. package/dist/index.mjs +227 -0
  164. package/dist/infinite-scroll.d.mts +26 -0
  165. package/dist/infinite-scroll.d.ts +26 -0
  166. package/dist/infinite-scroll.js +12 -0
  167. package/dist/infinite-scroll.mjs +3 -0
  168. package/dist/marquee.d.mts +12 -0
  169. package/dist/marquee.d.ts +12 -0
  170. package/dist/marquee.js +12 -0
  171. package/dist/marquee.mjs +3 -0
  172. package/dist/modal/index.d.mts +87 -0
  173. package/dist/modal/index.d.ts +87 -0
  174. package/dist/modal/index.js +54 -0
  175. package/dist/modal/index.mjs +9 -0
  176. package/dist/number-input.d.mts +21 -0
  177. package/dist/number-input.d.ts +21 -0
  178. package/dist/number-input.js +16 -0
  179. package/dist/number-input.mjs +3 -0
  180. package/dist/pagination.d.mts +21 -0
  181. package/dist/pagination.d.ts +21 -0
  182. package/dist/pagination.js +20 -0
  183. package/dist/pagination.mjs +3 -0
  184. package/dist/popover.d.mts +25 -0
  185. package/dist/popover.d.ts +25 -0
  186. package/dist/popover.js +32 -0
  187. package/dist/popover.mjs +3 -0
  188. package/dist/radio-group.d.mts +29 -0
  189. package/dist/radio-group.d.ts +29 -0
  190. package/dist/radio-group.js +24 -0
  191. package/dist/radio-group.mjs +3 -0
  192. package/dist/select.d.mts +31 -0
  193. package/dist/select.d.ts +31 -0
  194. package/dist/select.js +24 -0
  195. package/dist/select.mjs +3 -0
  196. package/dist/spinner.d.mts +9 -0
  197. package/dist/spinner.d.ts +9 -0
  198. package/dist/spinner.js +12 -0
  199. package/dist/spinner.mjs +3 -0
  200. package/dist/styles.css +2 -0
  201. package/dist/styles.d.mts +3 -0
  202. package/dist/styles.d.ts +3 -0
  203. package/dist/styles.js +19 -0
  204. package/dist/styles.mjs +17 -0
  205. package/dist/switch.d.mts +15 -0
  206. package/dist/switch.d.ts +15 -0
  207. package/dist/switch.js +16 -0
  208. package/dist/switch.mjs +3 -0
  209. package/dist/tab.d.mts +36 -0
  210. package/dist/tab.d.ts +36 -0
  211. package/dist/tab.js +20 -0
  212. package/dist/tab.mjs +3 -0
  213. package/dist/table.d.mts +80 -0
  214. package/dist/table.d.ts +80 -0
  215. package/dist/table.js +32 -0
  216. package/dist/table.mjs +3 -0
  217. package/dist/text-area.d.mts +15 -0
  218. package/dist/text-area.d.ts +15 -0
  219. package/dist/text-area.js +16 -0
  220. package/dist/text-area.mjs +3 -0
  221. package/dist/text-input.d.mts +17 -0
  222. package/dist/text-input.d.ts +17 -0
  223. package/dist/text-input.js +16 -0
  224. package/dist/text-input.mjs +3 -0
  225. package/dist/theme-provider.d.mts +25 -0
  226. package/dist/theme-provider.d.ts +25 -0
  227. package/dist/theme-provider.js +15 -0
  228. package/dist/theme-provider.mjs +2 -0
  229. package/dist/toast.d.mts +42 -0
  230. package/dist/toast.d.ts +42 -0
  231. package/dist/toast.js +20 -0
  232. package/dist/toast.mjs +3 -0
  233. package/dist/tooltip.d.mts +24 -0
  234. package/dist/tooltip.d.ts +24 -0
  235. package/dist/tooltip.js +20 -0
  236. package/dist/tooltip.mjs +3 -0
  237. package/dist/typography.d.mts +19 -0
  238. package/dist/typography.d.ts +19 -0
  239. package/dist/typography.js +102 -0
  240. package/dist/typography.mjs +79 -0
  241. package/dist/useModal-BsGIcP8t.d.mts +128 -0
  242. package/dist/useModal-BsGIcP8t.d.ts +128 -0
  243. package/dist/utils/cn.d.mts +15 -0
  244. package/dist/utils/cn.d.ts +15 -0
  245. package/dist/utils/cn.js +11 -0
  246. package/dist/utils/cn.mjs +2 -0
  247. package/dist/utils/scroll.d.mts +4 -0
  248. package/dist/utils/scroll.d.ts +4 -0
  249. package/dist/utils/scroll.js +15 -0
  250. package/dist/utils/scroll.mjs +2 -0
  251. package/dist/virtual-scroll.d.mts +34 -0
  252. package/dist/virtual-scroll.d.ts +34 -0
  253. package/dist/virtual-scroll.js +16 -0
  254. package/dist/virtual-scroll.mjs +3 -0
  255. package/package.json +271 -0
  256. package/scripts/setup-cursor-rules.cjs +92 -0
@@ -0,0 +1,698 @@
1
+ ---
2
+ description: "@nexus-cross/design-system 컴포넌트 API 레퍼런스 — 모든 공용 컴포넌트의 props와 사용 예시"
3
+ globs: "**/*.tsx,**/*.jsx,**/*.ts"
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # @nexus-cross/design-system — Component API Reference
8
+
9
+ 모든 컴포넌트는 `@nexus-cross/design-system`에서 import.
10
+
11
+ ---
12
+
13
+ ## Button
14
+
15
+ 인터랙티브 버튼. `asChild`로 렌더링 요소 변경 가능.
16
+
17
+ | Prop | Type | Default | Description |
18
+ |---|---|---|---|
19
+ | `variant` | `'primary' \| 'secondary' \| 'outline' \| 'ghost' \| 'danger'` | `'primary'` | 시각 스타일 |
20
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
21
+ | `asChild` | `boolean` | `false` | 자식 요소로 렌더링 |
22
+ | `detectDoubleClick` | `boolean` | `false` | 500ms 더블 클릭 방지 |
23
+ | `disabled` | `boolean` | - | 비활성 (`aria-disabled` 자동) |
24
+ | `className` | `string` | - | 스타일 오버라이드 |
25
+
26
+ ```tsx
27
+ <Button variant="primary" size="lg">확인</Button>
28
+
29
+ <Button variant="outline" disabled>비활성</Button>
30
+
31
+ // 로딩 상태 (Spinner를 children에 직접 배치)
32
+ <Button disabled>
33
+ <Spinner size={16} /> 처리 중...
34
+ </Button>
35
+
36
+ // <a> 태그로 렌더링
37
+ <Button asChild variant="ghost">
38
+ <a href="/settings">설정</a>
39
+ </Button>
40
+ ```
41
+
42
+ ---
43
+
44
+ ## TextInput
45
+
46
+ 텍스트 입력 필드. prefix/suffix 아이콘 지원.
47
+
48
+ | Prop | Type | Default | Description |
49
+ |---|---|---|---|
50
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
51
+ | `error` | `boolean` | `false` | 에러 상태 (`aria-invalid` 자동) |
52
+ | `prefixIcon` | `ReactNode` | - | 앞쪽 아이콘 |
53
+ | `suffixIcon` | `ReactNode` | - | 뒤쪽 아이콘 |
54
+ | `onValueChange` | `(value: string) => void` | - | 값 변경 콜백 |
55
+
56
+ ```tsx
57
+ <TextInput placeholder="이메일" size="md" />
58
+
59
+ <TextInput
60
+ error
61
+ prefixIcon={<SearchIcon />}
62
+ onValueChange={(v) => setQuery(v)}
63
+ />
64
+ ```
65
+
66
+ ---
67
+
68
+ ## TextArea
69
+
70
+ 여러 줄 텍스트 입력. 글자 수 카운터 내장.
71
+
72
+ | Prop | Type | Default | Description |
73
+ |---|---|---|---|
74
+ | `error` | `boolean` | `false` | 에러 상태 (`aria-invalid` 자동) |
75
+ | `showCount` | `boolean` | `false` | 글자 수 표시 (`maxLength` 필요) |
76
+ | `maxLength` | `number` | - | 최대 글자 수 |
77
+ | `onValueChange` | `(value: string) => void` | - | 값 변경 콜백 |
78
+
79
+ ```tsx
80
+ <TextArea
81
+ placeholder="내용을 입력하세요"
82
+ maxLength={500}
83
+ showCount
84
+ rows={4}
85
+ />
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Select
91
+
92
+ 드롭다운 선택. Radix Select 기반.
93
+
94
+ | Prop | Type | Default | Description |
95
+ |---|---|---|---|
96
+ | `value` | `string` | - | 선택된 값 |
97
+ | `onValueChange` | `(value: string) => void` | - | 값 변경 콜백 |
98
+ | `placeholder` | `string` | - | 플레이스홀더 |
99
+ | `variant` | `'default' \| 'outline'` | `'default'` | 트리거 스타일 |
100
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'full'` | `'full'` | 너비 |
101
+ | `disabled` | `boolean` | - | 비활성 |
102
+ | `displayComponent` | `ReactNode` | - | 트리거에 커스텀 표시 |
103
+
104
+ ```tsx
105
+ <Select value={lang} onValueChange={setLang} placeholder="언어 선택">
106
+ <SelectItem value="ko">한국어</SelectItem>
107
+ <SelectItem value="en">English</SelectItem>
108
+ <SelectItem value="ja">日本語</SelectItem>
109
+ </Select>
110
+ ```
111
+
112
+ ---
113
+
114
+ ## CheckBox
115
+
116
+ 체크박스. 네이티브 `<input>` 기반, square/round 형태 지원.
117
+
118
+ | Prop | Type | Default | Description |
119
+ |---|---|---|---|
120
+ | `size` | `'sm' \| 'md'` | `'md'` | 크기 |
121
+ | `shape` | `'square' \| 'round'` | `'square'` | 형태 |
122
+ | `checked` | `boolean` | - | 체크 상태 |
123
+ | `indeterminate` | `boolean` | `false` | 불확정 상태 (`aria-checked="mixed"`) |
124
+ | `onCheckedChange` | `(checked: boolean) => void` | - | 체크 변경 콜백 |
125
+ | `label` | `ReactNode` | - | 라벨 텍스트 |
126
+
127
+ ```tsx
128
+ <CheckBox
129
+ checked={agreed}
130
+ onCheckedChange={setAgreed}
131
+ label="이용약관에 동의합니다"
132
+ />
133
+
134
+ <CheckBox shape="round" size="sm" checked indeterminate />
135
+ ```
136
+
137
+ ---
138
+
139
+ ## RadioGroup / RadioItem
140
+
141
+ 라디오 그룹. 네이티브 `<input type="radio">` 기반.
142
+
143
+ | RadioGroup Prop | Type | Default | Description |
144
+ |---|---|---|---|
145
+ | `name` | `string` (필수) | - | form name |
146
+ | `value` | `string` | - | 선택된 값 |
147
+ | `onValueChange` | `(value: string) => void` | - | 변경 콜백 |
148
+ | `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | 배치 방향 |
149
+ | `size` | `'sm' \| 'md'` | `'md'` | 크기 |
150
+ | `aria-label` | `string` | - | 접근성 라벨 |
151
+
152
+ | RadioItem Prop | Type | Description |
153
+ |---|---|---|
154
+ | `value` | `string` (필수) | 항목 값 |
155
+ | `label` | `ReactNode` | 라벨 텍스트 |
156
+
157
+ ```tsx
158
+ <RadioGroup
159
+ name="plan"
160
+ value={plan}
161
+ onValueChange={setPlan}
162
+ orientation="horizontal"
163
+ aria-label="요금제 선택"
164
+ >
165
+ <RadioItem value="free" label="무료" />
166
+ <RadioItem value="pro" label="프로" />
167
+ <RadioItem value="enterprise" label="엔터프라이즈" />
168
+ </RadioGroup>
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Switch
174
+
175
+ 토글 스위치. 네이티브 checkbox 기반, `role="switch"`.
176
+
177
+ | Prop | Type | Default | Description |
178
+ |---|---|---|---|
179
+ | `size` | `'sm' \| 'md'` | `'md'` | 크기 |
180
+ | `checked` | `boolean` | - | on/off 상태 |
181
+ | `onCheckedChange` | `(checked: boolean) => void` | - | 변경 콜백 |
182
+ | `disabled` | `boolean` | - | 비활성 |
183
+
184
+ ```tsx
185
+ <Switch checked={darkMode} onCheckedChange={setDarkMode} />
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Chip
191
+
192
+ 칩/태그/뱃지. `asChild`로 렌더링 요소 변경 가능.
193
+
194
+ | Prop | Type | Default | Description |
195
+ |---|---|---|---|
196
+ | `variant` | `'default' \| 'filled' \| 'outline' \| 'accent'` | `'default'` | 스타일 |
197
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 크기 |
198
+ | `asChild` | `boolean` | `false` | 자식 요소로 렌더링 |
199
+ | `disabled` | `boolean` | - | 비활성 (`aria-disabled` 자동) |
200
+ | `onClose` | `(e: MouseEvent) => void` | - | 닫기 버튼 표시 및 콜백 |
201
+
202
+ ```tsx
203
+ <Chip variant="accent" size="sm">New</Chip>
204
+
205
+ <Chip onClose={() => removeTag(id)}>React</Chip>
206
+
207
+ // <li>로 렌더링
208
+ <Chip asChild variant="filled">
209
+ <li>리스트 칩</li>
210
+ </Chip>
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Spinner
216
+
217
+ 로딩 인디케이터. SVG 기반. `role="status"` 내장.
218
+
219
+ | Prop | Type | Default | Description |
220
+ |---|---|---|---|
221
+ | `size` | `number` | `20` | px 크기 |
222
+ | `className` | `string` | - | 색상 등 오버라이드 |
223
+ | `aria-label` | `string` | `'Loading'` | 접근성 라벨 |
224
+
225
+ ```tsx
226
+ <Spinner size={24} />
227
+
228
+ <Spinner size={14} className="text-white" aria-label="로딩 중" />
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Divider
234
+
235
+ 구분선. 수평/수직 방향, 실선/점선/파선 지원.
236
+
237
+ | Prop | Type | Default | Description |
238
+ |---|---|---|---|
239
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | 방향 |
240
+ | `variant` | `'solid' \| 'dashed' \| 'dotted'` | `'solid'` | 선 스타일 |
241
+ | `color` | `string` | - | 커스텀 색상 (CSS 값) |
242
+
243
+ ```tsx
244
+ <Divider />
245
+ <Divider orientation="vertical" variant="dashed" />
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Tooltip
251
+
252
+ 툴팁. Radix Tooltip 기반. 단독 사용 가능 (Provider 내장).
253
+
254
+ | Prop | Type | Default | Description |
255
+ |---|---|---|---|
256
+ | `content` | `ReactNode` | - | 툴팁 내용 |
257
+ | `variant` | `'dark' \| 'light'` | `'dark'` | 스타일 |
258
+ | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` | 위치 |
259
+ | `align` | `'start' \| 'center' \| 'end'` | `'center'` | 정렬 |
260
+ | `delayDuration` | `number` | `200` | 표시 지연 (ms) |
261
+ | `disabled` | `boolean` | `false` | 비활성 |
262
+
263
+ ```tsx
264
+ <Tooltip content="복사됨!" side="bottom">
265
+ <button>📋</button>
266
+ </Tooltip>
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Popover
272
+
273
+ 팝오버. Radix Popover 기반. 컴포저블 패턴과 단일 패턴 모두 지원.
274
+
275
+ | Prop | Type | Default | Description |
276
+ |---|---|---|---|
277
+ | `trigger` | `ReactNode` | - | 트리거 요소 |
278
+ | `children` | `ReactNode` | - | 팝오버 내용 |
279
+ | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` | 위치 |
280
+ | `align` | `'start' \| 'center' \| 'end'` | `'center'` | 정렬 |
281
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 너비 |
282
+ | `open` | `boolean` | - | 제어 모드 |
283
+ | `onOpenChange` | `(open: boolean) => void` | - | 열림/닫힘 콜백 |
284
+
285
+ ```tsx
286
+ <Popover trigger={<Button variant="outline">메뉴</Button>}>
287
+ <div className="p-4">팝오버 내용</div>
288
+ </Popover>
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Accordion
294
+
295
+ 아코디언. 단순 `items` 배열 방식과 컴포저블 방식 모두 지원.
296
+
297
+ | Prop | Type | Default | Description |
298
+ |---|---|---|---|
299
+ | `items` | `AccordionItemData[]` | - | 항목 배열 |
300
+ | `type` | `'single' \| 'multiple'` | `'single'` | 단일/다중 열기 |
301
+ | `collapsible` | `boolean` | `true` | 전부 접기 가능 |
302
+ | `value` / `defaultValue` | `string \| string[]` | - | 제어/비제어 |
303
+ | `onValueChange` | `(value) => void` | - | 변경 콜백 |
304
+
305
+ ```tsx
306
+ <Accordion items={[
307
+ { id: '1', trigger: 'FAQ 1', content: '답변 1' },
308
+ { id: '2', trigger: 'FAQ 2', content: '답변 2' },
309
+ ]} />
310
+
311
+ // 컴포저블 방식
312
+ <AccordionRoot type="single" collapsible>
313
+ <AccordionItem value="item-1">
314
+ <AccordionTrigger>제목</AccordionTrigger>
315
+ <AccordionContent>내용</AccordionContent>
316
+ </AccordionItem>
317
+ </AccordionRoot>
318
+ ```
319
+
320
+ ---
321
+
322
+ ## Drawer
323
+
324
+ 드로어/바텀시트. Vaul 기반. 4방향 지원.
325
+
326
+ | Prop | Type | Default | Description |
327
+ |---|---|---|---|
328
+ | `direction` | `'bottom' \| 'top' \| 'left' \| 'right'` | `'bottom'` | 방향 |
329
+ | `showHandle` | `boolean` | `true` | 핸들 바 표시 (top/bottom만) |
330
+ | `blur` | `'none' \| 'sm' \| 'md'` | `'none'` | 오버레이 블러 |
331
+
332
+ ```tsx
333
+ <Drawer direction="bottom">
334
+ <Drawer.Trigger asChild>
335
+ <Button>열기</Button>
336
+ </Drawer.Trigger>
337
+ <Drawer.Content>
338
+ <Drawer.Title>제목</Drawer.Title>
339
+ <Drawer.Description>설명</Drawer.Description>
340
+ <Drawer.Close asChild>
341
+ <Button variant="ghost">닫기</Button>
342
+ </Drawer.Close>
343
+ </Drawer.Content>
344
+ </Drawer>
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Modal (함수형 API)
350
+
351
+ 명령형 모달. 함수 호출로 열고 닫는다.
352
+
353
+ **중요: 모달 컴포넌트는 반드시 `ModalTemplate`으로 감싸야 한다.**
354
+
355
+ ### 모달 컴포넌트 작성법
356
+
357
+ ```tsx
358
+ import { ModalTemplate } from '@nexus-cross/design-system/modal';
359
+
360
+ // 모달 컴포넌트는 반드시 close와 resolve를 props로 받는다
361
+ function MyModal({ close, resolve }: { close: () => void; resolve: (value: any) => void }) {
362
+ return (
363
+ <ModalTemplate
364
+ title="모달 제목"
365
+ desc="모달 설명 (선택)"
366
+ close={close}
367
+ layout="default" // 'default' | 'bottom-sheet' | 'slide-left' | 'slide-right' | 'full-page' | 'draggable'
368
+ hideFooter // footer 없으면 하단 패딩 자동
369
+ >
370
+ <div className="space-y-4">
371
+ <p className="text-text-secondary">모달 내용</p>
372
+ <Button onClick={() => resolve({ confirmed: true })}>확인</Button>
373
+ </div>
374
+ </ModalTemplate>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ### ModalTemplate Props
380
+
381
+ | Prop | Type | Default | Description |
382
+ |---|---|---|---|
383
+ | `title` | `ReactNode` | - | 헤더 제목 |
384
+ | `desc` | `ReactNode` | - | 헤더 설명 |
385
+ | `close` | `() => void` (필수) | - | 닫기 함수 (props로 자동 주입됨) |
386
+ | `layout` | `'default' \| 'bottom-sheet' \| 'slide-left' \| 'slide-right' \| 'full-page' \| 'full-page-reverse' \| 'draggable'` | `'default'` | 레이아웃 |
387
+ | `showDim` | `boolean` | `true` | 딤 배경 표시 |
388
+ | `dimClose` | `boolean` | `true` | 딤 클릭 시 닫기 |
389
+ | `hideHeader` | `boolean` | `false` | 헤더 숨김 |
390
+ | `hideFooter` | `boolean` | `true` | 푸터 숨김 |
391
+ | `footer` | `ReactElement` | - | 커스텀 푸터 |
392
+ | `enableDrag` | `boolean` | `false` | 드래그 활성화 (draggable 레이아웃) |
393
+ | `dragPersistKey` | `string` | - | 드래그 위치 저장 키 |
394
+ | `innerClassName` | `string` | - | 모달 본체 스타일 오버라이드 |
395
+ | `bodyClassName` | `string` | - | 바디 영역 스타일 오버라이드 |
396
+
397
+ ### 모달 호출
398
+
399
+ ```tsx
400
+ import { modal, useModal, ModalContainer } from '@nexus-cross/design-system/modal';
401
+
402
+ // 앱 루트에 ModalContainer 배치 필수
403
+ <ModalContainer />
404
+
405
+ // 방법 1: modal() 함수
406
+ const result = await modal({
407
+ component: MyModal,
408
+ props: { /* 추가 props */ },
409
+ });
410
+
411
+ // 방법 2: useModal() 훅
412
+ const { modal: openModal } = useModal();
413
+ openModal({
414
+ component: MyModal,
415
+ options: { isAlone: true }, // 단독 모달 (다른 모달 위에 안 쌓임)
416
+ });
417
+ ```
418
+
419
+ ### 금지 사항
420
+
421
+ - ModalTemplate 없이 `<div>`만으로 모달 컴포넌트를 만들지 않는다
422
+ - `close` prop을 직접 정의하지 않는다 (시스템이 자동 주입)
423
+ - 모달 내에서 별도 dim/overlay를 구현하지 않는다
424
+
425
+ ---
426
+
427
+ ## Toast (함수형 API)
428
+
429
+ 토스트 알림. Sonner 기반.
430
+
431
+ ```tsx
432
+ import { toast, Toaster } from '@nexus-cross/design-system';
433
+
434
+ // 앱 루트에 Toaster 배치
435
+ <Toaster position="top-right" />
436
+
437
+ // 사용
438
+ toast('저장되었습니다');
439
+ toast.success('성공!');
440
+ toast.error('오류가 발생했습니다');
441
+ toast.loading('처리 중...');
442
+ ```
443
+
444
+ ---
445
+
446
+ ## InfiniteScroll
447
+
448
+ 무한 스크롤. IntersectionObserver 기반.
449
+
450
+ | Prop | Type | Description |
451
+ |---|---|---|
452
+ | `list` | `unknown[]` | 현재 데이터 배열 |
453
+ | `totalCount` | `number` | 전체 개수 (또는 `hasMore` 사용) |
454
+ | `hasMore` | `boolean` | 더 있는지 (또는 `totalCount` 사용) |
455
+ | `handleLoadMore` | `() => void` | 추가 로드 콜백 |
456
+ | `loading` | `boolean` | 로딩 상태 |
457
+
458
+ ```tsx
459
+ <InfiniteScroll
460
+ list={items}
461
+ totalCount={100}
462
+ loading={isLoading}
463
+ handleLoadMore={fetchMore}
464
+ >
465
+ {items.map(item => <Card key={item.id} {...item} />)}
466
+ </InfiniteScroll>
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Ellipsis
472
+
473
+ 텍스트 말줄임. 더보기/접기 토글 내장.
474
+
475
+ | Prop | Type | Default | Description |
476
+ |---|---|---|---|
477
+ | `content` | `ReactNode` | - | 내용 |
478
+ | `lineClamp` | `number` | `2` | 줄 수 제한 |
479
+ | `triggerMore` | `ReactNode` | `'more'` | 더보기 텍스트 |
480
+ | `triggerLess` | `ReactNode` | `'less'` | 접기 텍스트 |
481
+
482
+ ```tsx
483
+ <Ellipsis content={longText} lineClamp={3} triggerMore="더보기" triggerLess="접기" />
484
+ ```
485
+
486
+ ---
487
+
488
+ ## Hooks
489
+
490
+ ### useModal
491
+
492
+ ```tsx
493
+ const { open, close } = useModal();
494
+ open(MyComponent, { title: '제목' });
495
+ ```
496
+
497
+ ### useInView
498
+
499
+ ```tsx
500
+ const { ref, inView } = useInView({ threshold: 0.5 });
501
+ <div ref={ref}>{inView && <LazyContent />}</div>
502
+ ```
503
+
504
+ ### useCheckDevice
505
+
506
+ ```tsx
507
+ const { isMobile, isTablet, isDesktop } = useCheckDevice();
508
+ ```
509
+
510
+ ### useClickOutside
511
+
512
+ ```tsx
513
+ const ref = useClickOutside<HTMLDivElement>(() => setOpen(false));
514
+ <div ref={ref}>드롭다운 내용</div>
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Pagination
520
+
521
+ 페이지네이션. 이전/다음 + 번호 버튼.
522
+
523
+ | Prop | Type | Default | Description |
524
+ |---|---|---|---|
525
+ | `currentPage` | `number` | - | 현재 페이지 (1부터) |
526
+ | `totalPages` | `number` | - | 전체 페이지 수 |
527
+ | `siblingCount` | `number` | `1` | 현재 페이지 양옆 표시 개수 |
528
+ | `onPageChange` | `(page: number) => void` | - | 페이지 변경 콜백 |
529
+ | `size` | `'sm' \| 'md'` | `'md'` | 크기 |
530
+
531
+ ```tsx
532
+ <Pagination currentPage={2} totalPages={10} onPageChange={setPage} />
533
+ ```
534
+
535
+ ---
536
+
537
+ ## Avatar
538
+
539
+ 아바타. 이미지, 폴백 텍스트, children 지원.
540
+
541
+ | Prop | Type | Default | Description |
542
+ |---|---|---|---|
543
+ | `src` | `string` | - | 이미지 URL |
544
+ | `alt` | `string` | - | 대체 텍스트 |
545
+ | `fallback` | `ReactNode` | - | 이미지 로드 실패 시 표시 |
546
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | 크기 |
547
+ | `shape` | `'circle' \| 'square'` | `'circle'` | 형태 |
548
+ | `onImageError` | `() => void` | - | 이미지 에러 콜백 |
549
+
550
+ ```tsx
551
+ <Avatar src="/user.png" alt="User" size="lg" />
552
+ <Avatar fallback="JD" shape="square" size="sm" />
553
+ ```
554
+
555
+ ---
556
+
557
+ ## Counter
558
+
559
+ 숫자 카운트 애니메이션.
560
+
561
+ | Prop | Type | Default | Description |
562
+ |---|---|---|---|
563
+ | `endValue` | `number` (필수) | - | 목표 값 |
564
+ | `startValue` | `number` | `0` | 시작 값 |
565
+ | `duration` | `number` | `1500` | 애니메이션 시간 (ms) |
566
+ | `delay` | `number` | `0` | 시작 지연 (ms) |
567
+ | `separator` | `boolean` | `true` | 천 단위 구분 |
568
+ | `digits` | `number` | `0` | 소수 자릿수 |
569
+ | `triggerOnView` | `boolean` | `false` | 뷰포트 진입 시 시작 |
570
+ | `onEnd` | `() => void` | - | 완료 콜백 |
571
+
572
+ ```tsx
573
+ <Counter endValue={1234} duration={2000} separator />
574
+ ```
575
+
576
+ ---
577
+
578
+ ## Countdown
579
+
580
+ 카운트다운 타이머.
581
+
582
+ | Prop | Type | Default | Description |
583
+ |---|---|---|---|
584
+ | `endTimestamp` | `number` (필수) | - | 종료 시각 (Unix ms) |
585
+ | `separator` | `ReactNode` | `':'` | 구분자 |
586
+ | `showDays` | `boolean` | `true` | 일 단위 표시 |
587
+ | `labels` | `{ days?, hours?, minutes?, seconds? }` | - | 단위 라벨 |
588
+ | `onEnd` | `() => void` | - | 완료 콜백 |
589
+ | `render` | `(timeLeft: TimeLeft) => ReactNode` | - | 커스텀 렌더링 |
590
+
591
+ ```tsx
592
+ <Countdown endTimestamp={Date.now() + 60_000} showDays={false} onEnd={handleEnd} />
593
+ ```
594
+
595
+ ---
596
+
597
+ ## Marquee
598
+
599
+ 마퀴(흐르는 텍스트/요소).
600
+
601
+ | Prop | Type | Default | Description |
602
+ |---|---|---|---|
603
+ | `direction` | `'left' \| 'right' \| 'up' \| 'down'` | `'left'` | 방향 |
604
+ | `speed` | `number` | `40` | 애니메이션 속도 (초) |
605
+ | `pauseOnHover` | `boolean` | `false` | 호버 시 일시정지 |
606
+ | `gap` | `number` | `16` | 아이템 간격 (px) |
607
+
608
+ ```tsx
609
+ <Marquee direction="left" speed={30} pauseOnHover>
610
+ <span>흐르는 텍스트</span>
611
+ </Marquee>
612
+ ```
613
+
614
+ ---
615
+
616
+ ## Tab
617
+
618
+ 탭 네비게이션. line/pill 변형.
619
+
620
+ | Prop | Type | Default | Description |
621
+ |---|---|---|---|
622
+ | `items` | `TabItem[]` (필수) | - | 탭 항목 배열 |
623
+ | `activeKey` | `string` | - | 제어 모드 활성 키 |
624
+ | `defaultActiveKey` | `string` | - | 비제어 모드 초기 키 |
625
+ | `variant` | `'line' \| 'pill'` | `'line'` | 스타일 |
626
+ | `size` | `'sm' \| 'md'` | `'md'` | 크기 |
627
+ | `destroyInactive` | `boolean` | `false` | 비활성 패널 언마운트 |
628
+ | `onTabChange` | `(key: string) => void` | - | 탭 변경 콜백 |
629
+
630
+ ```tsx
631
+ <Tab
632
+ items={[
633
+ { key: 'a', label: 'Tab A', children: <p>A</p> },
634
+ { key: 'b', label: 'Tab B', children: <p>B</p> },
635
+ ]}
636
+ defaultActiveKey="a"
637
+ variant="pill"
638
+ />
639
+ ```
640
+
641
+ ---
642
+
643
+ ## Carousel
644
+
645
+ 캐러셀. Embla Carousel 기반. 컴포저블 패턴.
646
+
647
+ | Prop | Type | Default | Description |
648
+ |---|---|---|---|
649
+ | `opts` | `EmblaOptionsType` | `{ loop: false }` | Embla 옵션 |
650
+ | `plugins` | `EmblaPluginType[]` | - | Embla 플러그인 |
651
+ | `onApiChange` | `(api: CarouselApi) => void` | - | API 변경 콜백 |
652
+
653
+ 서브 컴포넌트: `CarouselSlide`, `CarouselPrev`, `CarouselNext`, `CarouselDots`
654
+
655
+ ```tsx
656
+ <Carousel opts={{ loop: true }}>
657
+ <CarouselSlide>슬라이드 1</CarouselSlide>
658
+ <CarouselSlide>슬라이드 2</CarouselSlide>
659
+ <CarouselPrev />
660
+ <CarouselNext />
661
+ <CarouselDots />
662
+ </Carousel>
663
+ ```
664
+
665
+ ---
666
+
667
+ ## VirtualList / VirtualGrid
668
+
669
+ 가상 스크롤 리스트/그리드. @tanstack/react-virtual 기반.
670
+
671
+ ### VirtualList
672
+
673
+ | Prop | Type | Default | Description |
674
+ |---|---|---|---|
675
+ | `items` | `T[]` (필수) | - | 데이터 배열 |
676
+ | `estimateSize` | `number \| (index: number) => number` (필수) | - | 예상 아이템 높이 |
677
+ | `renderItem` | `(item: T, index: number) => ReactNode` (필수) | - | 렌더러 |
678
+ | `overscan` | `number` | `5` | 오버스캔 |
679
+ | `gap` | `number` | `0` | 아이템 간격 |
680
+ | `onEndReached` | `() => void` | - | 끝 도달 콜백 |
681
+ | `endReachedThreshold` | `number` | `200` | 끝 감지 임계값 (px) |
682
+
683
+ ```tsx
684
+ <VirtualList
685
+ items={data}
686
+ estimateSize={48}
687
+ renderItem={(item) => <div>{item.name}</div>}
688
+ onEndReached={loadMore}
689
+ />
690
+ ```
691
+
692
+ ### VirtualGrid
693
+
694
+ VirtualList와 동일 + `columns: number` (필수).
695
+
696
+ ```tsx
697
+ <VirtualGrid items={data} estimateSize={120} columns={3} renderItem={(item) => <Card {...item} />} />
698
+ ```