@reopt-ai/opt-ui 1.4.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 (64) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/COMPONENT_CATALOG.md +3000 -0
  3. package/LICENSE +21 -0
  4. package/README.md +244 -0
  5. package/dist/chunk-3GWWZKX7.js +38 -0
  6. package/dist/chunk-AFF2HPE5.cjs +5008 -0
  7. package/dist/chunk-ELWICXYY.js +4745 -0
  8. package/dist/chunk-N4NDU5ET.cjs +4745 -0
  9. package/dist/chunk-ONE3C5RV.cjs +38 -0
  10. package/dist/chunk-QWBHD54V.js +3218 -0
  11. package/dist/chunk-RBM2RNC2.js +5008 -0
  12. package/dist/chunk-X5WCXJAF.cjs +3218 -0
  13. package/dist/core/index.cjs +324 -0
  14. package/dist/core/index.d.cts +5 -0
  15. package/dist/core/index.d.ts +5 -0
  16. package/dist/core/index.js +324 -0
  17. package/dist/docs/01-getting-started.md +129 -0
  18. package/dist/docs/02-components/01-core.md +1841 -0
  19. package/dist/docs/02-components/02-visuals.md +11 -0
  20. package/dist/docs/02-components/03-shells.md +1361 -0
  21. package/dist/docs/02-components/04-surfaces.md +11 -0
  22. package/dist/docs/02-components/index.md +106 -0
  23. package/dist/docs/03-recipes/01-forms.md +471 -0
  24. package/dist/docs/03-recipes/02-dashboards.md +397 -0
  25. package/dist/docs/03-recipes/03-layouts.md +424 -0
  26. package/dist/docs/04-theming.md +232 -0
  27. package/dist/docs/05-migration/01-breaking-changes.md +117 -0
  28. package/dist/docs/05-migration/02-formstore.md +336 -0
  29. package/dist/docs/06-troubleshooting.md +119 -0
  30. package/dist/docs/index.md +71 -0
  31. package/dist/id-registry.cjs +1875 -0
  32. package/dist/id-registry.d.cts +27 -0
  33. package/dist/id-registry.d.ts +27 -0
  34. package/dist/id-registry.js +1875 -0
  35. package/dist/id-registry.json +3799 -0
  36. package/dist/index-BZ_lBlO1.d.ts +474 -0
  37. package/dist/index-BuvxoWHf.d.cts +474 -0
  38. package/dist/index-DlAcuvQp.d.cts +1686 -0
  39. package/dist/index-Slu5hOj1.d.ts +1686 -0
  40. package/dist/index.cjs +9959 -0
  41. package/dist/index.d.cts +2351 -0
  42. package/dist/index.d.ts +2351 -0
  43. package/dist/index.js +9959 -0
  44. package/dist/meta.cjs +6898 -0
  45. package/dist/meta.d.cts +36 -0
  46. package/dist/meta.d.ts +36 -0
  47. package/dist/meta.js +6869 -0
  48. package/dist/shells/index.cjs +65 -0
  49. package/dist/shells/index.d.cts +5 -0
  50. package/dist/shells/index.d.ts +5 -0
  51. package/dist/shells/index.js +65 -0
  52. package/dist/tailwind.css +401 -0
  53. package/dist/theme/presets/default.css +355 -0
  54. package/dist/theme/presets/minimal.css +354 -0
  55. package/dist/theme/presets/mono-dark.css +354 -0
  56. package/dist/theme/presets/natural.css +181 -0
  57. package/dist/theme/presets/pro.css +354 -0
  58. package/dist/types-D4-0lwaE.d.cts +298 -0
  59. package/dist/types-D4-0lwaE.d.ts +298 -0
  60. package/dist/visuals/index.cjs +4 -0
  61. package/dist/visuals/index.d.cts +1 -0
  62. package/dist/visuals/index.d.ts +1 -0
  63. package/dist/visuals/index.js +4 -0
  64. package/package.json +165 -0
@@ -0,0 +1,1841 @@
1
+ ---
2
+ title: "Core Components"
3
+ description: "59 Core layer components — props, examples, keyboard shortcuts."
4
+ version: "1.4.1"
5
+ generated: true
6
+ ---
7
+
8
+ # Core Components (59)
9
+
10
+ ## Input
11
+
12
+ | Component | Description | Key Exports |
13
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
14
+ | Input | 독립적인 텍스트 입력 컴포넌트. 레이블, 에러, 힌트, 아이콘 지원. | `Input` |
15
+ | Textarea | 멀티라인 텍스트 입력. Input과 동일한 API (label, error, hint). | `Textarea` |
16
+ | NumberInput | 숫자 입력 컴포넌트. +/- 버튼, nullable/clear, unit suffix, invalid draft alert, min/max, step, 키보드 화살표를 지원합니다. | `NumberInput` |
17
+ | Checkbox | 독립적인 체크박스 컴포넌트. 레이블, 설명, 그룹과 DOM property까지 동기화되는 indeterminate 상태를 지원합니다. | `Checkbox`, `CheckboxGroup` |
18
+ | RadioGroup | 독립적인 라디오 그룹 컴포넌트. 그룹 설명과 항목별 설명을 분리 연결하고 다양한 크기를 지원합니다. | `RadioGroup`, `Radio` |
19
+ | Switch | 토글 스위치 컴포넌트. 3가지 크기와 레이블/설명의 명시적 접근성 연결을 지원합니다. | `Switch` |
20
+ | Select | 드롭다운 선택 컴포넌트. 5-part anatomy: Root, Label, Trigger, Popover, Item. | `SelectRoot`, `SelectLabel`, `SelectTrigger`, `SelectPopover`, `SelectItem` |
21
+ | Combobox | 자동완성 검색 입력. 7 exports: Root, Input, Popover, Item, Group, GroupLabel, Empty. | `ComboboxRoot`, `ComboboxInput`, `ComboboxPopover`, `ComboboxItem`, `ComboboxGroup`, `ComboboxGroupLabel`, `ComboboxEmpty` |
22
+ | DateRangePicker | 날짜 범위 선택 컴포넌트. 프리셋, 달력 UI, 포커스 관리와 RTL 방향 탐색 아이콘을 지원합니다. | `DateRangePicker` |
23
+ | TagInput | 태그 입력 컴포넌트. 텍스트를 입력하고 Enter로 태그를 추가, Backspace로 삭제합니다. | `TagInput` |
24
+ | ColorPicker | 색상 선택 컴포넌트. 프리셋 팔레트와 hex 입력을 지원합니다. | `ColorPicker` |
25
+ | IconPicker | 아이콘 선택 컴포넌트. 검색, 그룹별 분류, semantic key 기반 Lucide 아이콘과 커스텀 ReactNode를 지원합니다. | `IconPicker` |
26
+ | Slider | 범위 슬라이더 입력. native range input 래핑, 범위로 정규화된 값/ARIA 상태, 값 표시와 포맷 함수를 지원합니다. | `Slider` |
27
+
28
+ ### Input
29
+
30
+ > 독립적인 텍스트 입력 컴포넌트. 레이블, 에러, 힌트, 아이콘 지원.
31
+
32
+ **Import:** `import { Input } from "@reopt-ai/opt-ui"`
33
+
34
+ | Prop | Type | Required | Default | Description |
35
+ | ----------- | ----------- | -------- | ------- | ------------------------------------------- |
36
+ | `label` | `string` | | | 입력 필드 레이블 |
37
+ | `error` | `string` | | | 에러 메시지. 표시 시 aria-invalid=true 설정 |
38
+ | `hint` | `string` | | | 힌트 텍스트 (에러 없을 때만 표시) |
39
+ | `leftIcon` | `ReactNode` | | | 입력 필드 왼쪽에 표시할 아이콘 |
40
+ | `rightIcon` | `ReactNode` | | | 입력 필드 오른쪽에 표시할 아이콘 |
41
+ | `fullWidth` | `boolean` | | false | 전체 너비 사용 여부 |
42
+
43
+ **Examples:**
44
+
45
+ _기본 사용:_
46
+
47
+ ```tsx
48
+ import { Input } from "@reopt-ai/opt-ui";
49
+
50
+ <Input
51
+ label="이메일"
52
+ hint="업무용 이메일을 입력하세요"
53
+ placeholder="user@example.com"
54
+ value={email}
55
+ onChange={(e) => setEmail(e.target.value)}
56
+ />;
57
+ ```
58
+
59
+ _FormStore 연동:_
60
+
61
+ ```tsx
62
+ import {
63
+ useFormStore,
64
+ FormRoot,
65
+ FormField,
66
+ FormControl,
67
+ FormLabel,
68
+ Input,
69
+ } from "@reopt-ai/opt-ui";
70
+
71
+ const store = useFormStore({ defaultValues: { name: "" } });
72
+
73
+ <FormRoot store={store}>
74
+ <FormField name="name">
75
+ <FormLabel name="name">이름</FormLabel>
76
+ <FormControl name="name">
77
+ <Input />
78
+ </FormControl>
79
+ </FormField>
80
+ </FormRoot>;
81
+ ```
82
+
83
+ ### Textarea
84
+
85
+ > 멀티라인 텍스트 입력. Input과 동일한 API (label, error, hint).
86
+
87
+ **Import:** `import { Textarea } from "@reopt-ai/opt-ui"`
88
+
89
+ | Prop | Type | Required | Default | Description |
90
+ | ----------- | ------------------------------------------------ | -------- | ---------- | ---------------- |
91
+ | `label` | `string` | | | 입력 필드 레이블 |
92
+ | `error` | `string` | | | 에러 메시지 |
93
+ | `hint` | `string` | | | 힌트 텍스트 |
94
+ | `fullWidth` | `boolean` | | false | 전체 너비 사용 |
95
+ | `resize` | `"none" \| "vertical" \| "horizontal" \| "both"` | | "vertical" | 크기 조절 방향 |
96
+ | `rows` | `number` | | 4 | 표시 줄 수 |
97
+
98
+ ### NumberInput
99
+
100
+ > 숫자 입력 컴포넌트. +/- 버튼, nullable/clear, unit suffix, invalid draft alert, min/max, step, 키보드 화살표를 지원합니다.
101
+
102
+ **Import:** `import { NumberInput } from "@reopt-ai/opt-ui"`
103
+
104
+ | Prop | Type | Required | Default | Description |
105
+ | ---------------- | --------------------------------- | -------- | ------- | ------------------------------------------------ |
106
+ | `value` | `number \| null` | | | 제어 모드 값. nullable=true일 때 null 허용 |
107
+ | `defaultValue` | `number \| null` | | 0 | 비제어 모드 초기값. nullable=true일 때 null 허용 |
108
+ | `onChange` | `(value: number \| null) => void` | | | 값 변경 핸들러 |
109
+ | `nullable` | `boolean` | | false | 빈 값을 null로 커밋할지 여부 |
110
+ | `clearable` | `boolean` | | false | nullable 값 지우기 버튼 표시 |
111
+ | `min` | `number` | | | 최솟값 |
112
+ | `max` | `number` | | | 최댓값 |
113
+ | `step` | `number` | | 1 | 증감 단위 |
114
+ | `label` | `string` | | | 라벨 텍스트 |
115
+ | `unit` | `ReactNode` | | | 입력값 단위 suffix |
116
+ | `disabled` | `boolean` | | | 비활성 상태 |
117
+ | `disabledReason` | `string` | | | aria-disabled 상태에서 노출할 비활성 사유 |
118
+ | `labels` | `NumberInputLabels` | | | 입력/증감/clear/invalid 접근성 문구 |
119
+
120
+ ### Checkbox
121
+
122
+ > 독립적인 체크박스 컴포넌트. 레이블, 설명, 그룹과 DOM property까지 동기화되는 indeterminate 상태를 지원합니다.
123
+
124
+ **Import:** `import { Checkbox } from "@reopt-ai/opt-ui"`
125
+
126
+ **Checkbox:**
127
+
128
+ | Prop | Type | Required | Default | Description |
129
+ | ---------------- | ---------------------------- | -------- | ------- | ---------------------------------- |
130
+ | `label` | `string` | | | 체크박스 레이블 |
131
+ | `description` | `string` | | | 레이블 아래 설명 텍스트 |
132
+ | `checked` | `boolean` | | | 제어 모드: 체크 상태 |
133
+ | `defaultChecked` | `boolean` | | | 비제어 모드: 초기 체크 상태 |
134
+ | `onChange` | `(checked: boolean) => void` | | | 상태 변경 핸들러 |
135
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 체크박스 크기 |
136
+ | `value` | `string` | | | CheckboxGroup 내에서 사용할 값 |
137
+ | `indeterminate` | `boolean` | | false | 불확정 상태 표시 (마이너스 아이콘) |
138
+
139
+ **CheckboxGroup:**
140
+
141
+ | Prop | Type | Required | Default | Description |
142
+ | -------------- | ---------------------------- | -------- | ---------- | ------------------------------ |
143
+ | `value` | `string[]` | | | 제어 모드: 선택된 값 배열 |
144
+ | `defaultValue` | `string[]` | | [] | 비제어 모드: 초기 선택 값 배열 |
145
+ | `onChange` | `(value: string[]) => void` | | | 선택 변경 핸들러 |
146
+ | `orientation` | `"horizontal" \| "vertical"` | | "vertical" | 레이아웃 방향 |
147
+ | `label` | `string` | | | 그룹 레이블 |
148
+
149
+ ### RadioGroup
150
+
151
+ > 독립적인 라디오 그룹 컴포넌트. 그룹 설명과 항목별 설명을 분리 연결하고 다양한 크기를 지원합니다.
152
+
153
+ **Import:** `import { RadioGroup } from "@reopt-ai/opt-ui"`
154
+
155
+ **RadioGroup:**
156
+
157
+ | Prop | Type | Required | Default | Description |
158
+ | -------------- | ---------------------------- | -------- | ---------- | ------------------------- |
159
+ | `value` | `string` | | | 제어 모드: 선택된 값 |
160
+ | `defaultValue` | `string` | | "" | 비제어 모드: 초기 선택 값 |
161
+ | `onChange` | `(value: string) => void` | | | 선택 변경 핸들러 |
162
+ | `orientation` | `"horizontal" \| "vertical"` | | "vertical" | 레이아웃 방향 |
163
+ | `label` | `string` | | | 그룹 레이블 |
164
+ | `name` | `string` | | | 폼 제출 시 사용할 이름 |
165
+
166
+ **Radio:**
167
+
168
+ | Prop | Type | Required | Default | Description |
169
+ | ------------- | ---------------------- | -------- | ------- | ---------------------------------------------------------- |
170
+ | `value` | `string` | | | 라디오 값 (RadioGroup 안에서는 부모가 관리하므로 optional) |
171
+ | `label` | `string` | | | 라디오 레이블 |
172
+ | `description` | `string` | | | 레이블 아래 설명 텍스트 |
173
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 라디오 크기 |
174
+
175
+ ### Switch
176
+
177
+ > 토글 스위치 컴포넌트. 3가지 크기와 레이블/설명의 명시적 접근성 연결을 지원합니다.
178
+
179
+ **Import:** `import { Switch } from "@reopt-ai/opt-ui"`
180
+
181
+ | Prop | Type | Required | Default | Description |
182
+ | ---------------- | ---------------------------- | -------- | ------- | --------------------------- |
183
+ | `label` | `string` | | | 스위치 레이블 |
184
+ | `description` | `string` | | | 레이블 아래 설명 텍스트 |
185
+ | `checked` | `boolean` | | | 제어 모드: 체크 상태 |
186
+ | `defaultChecked` | `boolean` | | | 비제어 모드: 초기 체크 상태 |
187
+ | `onChange` | `(checked: boolean) => void` | | | 상태 변경 핸들러 |
188
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 스위치 크기 |
189
+ | `disabled` | `boolean` | | | 비활성화 상태 |
190
+
191
+ ### Select
192
+
193
+ > 드롭다운 선택 컴포넌트. 5-part anatomy: Root, Label, Trigger, Popover, Item.
194
+
195
+ **Import:** `import { SelectRoot } from "@reopt-ai/opt-ui"`
196
+
197
+ **SelectRoot:**
198
+
199
+ | Prop | Type | Required | Default | Description |
200
+ | --------------- | ------------------------------------- | -------- | ------- | -------------------------------------------------------- |
201
+ | `value` | `string \| string[]` | | | 제어 모드: 현재 선택 값 (multiple이면 string[]) |
202
+ | `onValueChange` | `(value: string \| string[]) => void` | | | 제어 모드: 값 변경 핸들러 |
203
+ | `defaultValue` | `string \| string[]` | | | 비제어 모드: 초기값 |
204
+ | `multiple` | `boolean` | | | 다중 선택 허용 (값이 string[]이 됨) |
205
+ | `name` | `string` | | | 폼 필드명. 선택값마다 hidden input을 렌더해 폼 전송 지원 |
206
+
207
+ **SelectLabel:**
208
+
209
+ | Prop | Type | Required | Default | Description |
210
+ | ---------- | ----------- | -------- | ------- | ------------- |
211
+ | `children` | `ReactNode` | Yes | | 레이블 텍스트 |
212
+
213
+ **SelectTrigger:**
214
+
215
+ | Prop | Type | Required | Default | Description |
216
+ | ----------- | ----------- | -------- | ------- | ---------------------------- |
217
+ | `children` | `ReactNode` | Yes | | 트리거 내용 (선택된 값 표시) |
218
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
219
+
220
+ **SelectPopover:**
221
+
222
+ | Prop | Type | Required | Default | Description |
223
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
224
+ | `children` | `ReactNode` | Yes | | SelectItem 목록 |
225
+ | `sameWidth` | `boolean` | | true | 트리거와 같은 너비 |
226
+ | `gutter` | `number` | | 4 | 트리거와의 간격 (px) |
227
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
228
+
229
+ **SelectItem:**
230
+
231
+ | Prop | Type | Required | Default | Description |
232
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
233
+ | `value` | `string` | Yes | | 옵션 값 |
234
+ | `children` | `ReactNode` | Yes | | 옵션 레이블 |
235
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
236
+
237
+ **Examples:**
238
+
239
+ _기본 선택:_
240
+
241
+ ```tsx
242
+ import {
243
+ SelectRoot,
244
+ SelectLabel,
245
+ SelectTrigger,
246
+ SelectPopover,
247
+ SelectItem,
248
+ } from "@reopt-ai/opt-ui";
249
+
250
+ <SelectRoot value={role} onValueChange={setRole}>
251
+ <SelectLabel>역할</SelectLabel>
252
+ <SelectTrigger>{role || "선택하세요"}</SelectTrigger>
253
+ <SelectPopover>
254
+ <SelectItem value="admin">관리자</SelectItem>
255
+ <SelectItem value="editor">편집자</SelectItem>
256
+ <SelectItem value="viewer">뷰어</SelectItem>
257
+ </SelectPopover>
258
+ </SelectRoot>;
259
+ ```
260
+
261
+ ### Combobox
262
+
263
+ > 자동완성 검색 입력. 7 exports: Root, Input, Popover, Item, Group, GroupLabel, Empty.
264
+
265
+ **Import:** `import { ComboboxRoot } from "@reopt-ai/opt-ui"`
266
+
267
+ **ComboboxRoot:**
268
+
269
+ | Prop | Type | Required | Default | Description |
270
+ | ------------------ | ------------------------- | -------- | ------- | -------------------------- |
271
+ | `setValue` | `(value: string) => void` | | | 입력값 변경 핸들러 |
272
+ | `resetValueOnHide` | `boolean` | | false | 팝오버 닫힐 때 입력값 리셋 |
273
+
274
+ **ComboboxInput:**
275
+
276
+ | Prop | Type | Required | Default | Description |
277
+ | ------------- | --------- | -------- | ------- | ---------------------- |
278
+ | `placeholder` | `string` | | | 플레이스홀더 텍스트 |
279
+ | `autoSelect` | `boolean` | | false | 첫 항목 자동 선택 |
280
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
281
+
282
+ **ComboboxPopover:**
283
+
284
+ | Prop | Type | Required | Default | Description |
285
+ | ----------- | ----------- | -------- | ------- | ----------------------- |
286
+ | `children` | `ReactNode` | Yes | | ComboboxItem/Group 목록 |
287
+ | `sameWidth` | `boolean` | | true | 입력과 같은 너비 |
288
+ | `gutter` | `number` | | 4 | 입력과의 간격 (px) |
289
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
290
+
291
+ **ComboboxItem:**
292
+
293
+ | Prop | Type | Required | Default | Description |
294
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
295
+ | `value` | `string` | Yes | | 항목 값 |
296
+ | `children` | `ReactNode` | Yes | | 항목 내용 |
297
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
298
+
299
+ **ComboboxGroup:**
300
+
301
+ | Prop | Type | Required | Default | Description |
302
+ | ---------- | ----------- | -------- | ------- | ---------------------- |
303
+ | `children` | `ReactNode` | Yes | | GroupLabel + Item 그룹 |
304
+
305
+ **ComboboxGroupLabel:**
306
+
307
+ | Prop | Type | Required | Default | Description |
308
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
309
+ | `children` | `ReactNode` | Yes | | 그룹 레이블 |
310
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
311
+
312
+ **ComboboxEmpty:**
313
+
314
+ | Prop | Type | Required | Default | Description |
315
+ | ----------- | ----------- | -------- | ------------------ | ---------------------- |
316
+ | `children` | `ReactNode` | | "결과가 없습니다." | 빈 상태 메시지 |
317
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
318
+
319
+ ### DateRangePicker
320
+
321
+ > 날짜 범위 선택 컴포넌트. 프리셋, 달력 UI, 포커스 관리와 RTL 방향 탐색 아이콘을 지원합니다.
322
+
323
+ **Import:** `import { DateRangePicker } from "@reopt-ai/opt-ui"`
324
+
325
+ | Prop | Type | Required | Default | Description |
326
+ | --------------------- | ----------------------------------------------------- | -------- | ------------------- | ------------------------------------------------------ |
327
+ | `value` | `DateRange` | | | 제어 모드: { start: Date \| null, end: Date \| null } |
328
+ | `onChange` | `(range: DateRange) => void` | | | 범위 변경 핸들러 |
329
+ | `presets` | `DateRangePreset[]` | | | 빠른 선택 프리셋 배열 (기본: Today, Last 7/30/90 days) |
330
+ | `placeholder` | `string` | | "Select date range" | 미선택 시 표시 텍스트 |
331
+ | `label` | `string` | | | 입력 필드 레이블 |
332
+ | `disabled` | `boolean` | | false | 비활성화 상태 |
333
+ | `format` | `(date: Date) => string` | | | 날짜 포맷 함수 |
334
+ | `comparison` | `boolean` | | false | 비교 모드 활성화 |
335
+ | `comparisonMode` | `"previous" \| "year-over-year"` | | "previous" | 비교 기준 (이전 기간 / 전년 동기) |
336
+ | `onComparisonChange` | `(comparison: boolean, mode: ComparisonMode) => void` | | | 비교 모드 변경 콜백 |
337
+ | `showGranularity` | `boolean` | | false | 시간 단위 선택기 표시 |
338
+ | `granularity` | `"hour" \| "day" \| "week" \| "month"` | | "day" | 시간 단위 |
339
+ | `onGranularityChange` | `(granularity: DateGranularity) => void` | | | 시간 단위 변경 콜백 |
340
+
341
+ ### TagInput
342
+
343
+ > 태그 입력 컴포넌트. 텍스트를 입력하고 Enter로 태그를 추가, Backspace로 삭제합니다.
344
+
345
+ **Import:** `import { TagInput } from "@reopt-ai/opt-ui"`
346
+
347
+ | Prop | Type | Required | Default | Description |
348
+ | ----------------- | -------------------------- | -------- | ---------------- | ----------------------------------------- |
349
+ | `value` | `string[]` | | | 제어 모드: 현재 태그 배열 |
350
+ | `onChange` | `(tags: string[]) => void` | | | 태그 변경 핸들러 |
351
+ | `placeholder` | `string` | | "Add tag..." | 입력 플레이스홀더 |
352
+ | `label` | `string` | | | 입력 필드 레이블 |
353
+ | `maxTags` | `number` | | | 최대 태그 수 |
354
+ | `disabled` | `boolean` | | false | 비활성화 상태 |
355
+ | `suggestions` | `string[]` | | | 자동완성 드롭다운에 표시할 추천 태그 목록 |
356
+ | `delimiters` | `(string \| RegExp)[]` | | [",", ";", "\n"] | 붙여넣기 시 태그를 분할할 구분자 목록 |
357
+ | `allowDuplicates` | `boolean` | | false | 중복 태그 허용 여부 |
358
+
359
+ ### ColorPicker
360
+
361
+ > 색상 선택 컴포넌트. 프리셋 팔레트와 hex 입력을 지원합니다.
362
+
363
+ **Import:** `import { ColorPicker } from "@reopt-ai/opt-ui"`
364
+
365
+ | Prop | Type | Required | Default | Description |
366
+ | ---------- | ------------------------- | -------- | ------- | -------------------------------------- |
367
+ | `value` | `string` | | | 제어 모드: hex 색상 값 (예: '#3b82f6') |
368
+ | `onChange` | `(color: string) => void` | | | 색상 변경 핸들러 |
369
+ | `presets` | `string[]` | | | 프리셋 색상 배열 (기본 16색) |
370
+ | `label` | `string` | | | 입력 필드 레이블 |
371
+ | `disabled` | `boolean` | | false | 비활성화 상태 |
372
+
373
+ ### IconPicker
374
+
375
+ > 아이콘 선택 컴포넌트. 검색, 그룹별 분류, semantic key 기반 Lucide 아이콘과 커스텀 ReactNode를 지원합니다.
376
+
377
+ **Import:** `import { IconPicker } from "@reopt-ai/opt-ui"`
378
+
379
+ | Prop | Type | Required | Default | Description |
380
+ | ---------- | -------------------------- | -------- | ------- | ------------------------------------------ |
381
+ | `value` | `string` | | | 제어 모드: 선택된 아이콘 ID |
382
+ | `onChange` | `(iconId: string) => void` | | | 아이콘 변경 핸들러 |
383
+ | `icons` | `IconDef[]` | | | 아이콘 목록 (기본 24개 Lucide 아이콘 제공) |
384
+ | `label` | `string` | | | 입력 필드 레이블 |
385
+ | `disabled` | `boolean` | | false | 비활성화 상태 |
386
+
387
+ ### Slider
388
+
389
+ > 범위 슬라이더 입력. native range input 래핑, 범위로 정규화된 값/ARIA 상태, 값 표시와 포맷 함수를 지원합니다.
390
+
391
+ **Import:** `import { Slider } from "@reopt-ai/opt-ui"`
392
+
393
+ | Prop | Type | Required | Default | Description |
394
+ | -------------- | --------------------------- | -------- | ------- | ------------------ |
395
+ | `value` | `number` | | | 제어 모드 값 |
396
+ | `defaultValue` | `number` | | 0 | 비제어 모드 초기값 |
397
+ | `onChange` | `(value: number) => void` | | | 값 변경 핸들러 |
398
+ | `min` | `number` | | 0 | 최솟값 |
399
+ | `max` | `number` | | 100 | 최댓값 |
400
+ | `step` | `number` | | 1 | 증감 단위 |
401
+ | `label` | `string` | | | 레이블 텍스트 |
402
+ | `showValue` | `boolean` | | false | 현재 값 표시 여부 |
403
+ | `formatValue` | `(value: number) => string` | | | 값 포맷 함수 |
404
+ | `disabled` | `boolean` | | | 비활성화 |
405
+ | `className` | `string` | | | 최외곽 CSS 클래스 |
406
+
407
+ ## Form Integration
408
+
409
+ | Component | Description | Key Exports |
410
+ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
411
+ | Form | 접근성 폼 시스템. opt-ui-primitives Form API 기반. 31 exports: FormRoot, FormProvider, FormGroup, FormGroupLabel, FormDescription, FormField, FormControl, FormInput, FormTextarea, FormSelect, FormSelectPopover, FormSelectItem, FormSwitch, FormLabel, FormError, FormSubmit, FormCheckbox, FormRadioGroup, FormRadio, FormPush, FormRemove, FormReset, FormNumberInput, FormSlider, FormStarRating, FormColorPicker, FormIconPicker, FormTagInput, FormDateRangePicker + useFormStore, useFormContext hooks. | `FormRoot`, `FormProvider`, `FormGroup`, `FormGroupLabel`, `FormDescription`, `FormField`, `FormControl`, `FormInput`, `FormTextarea`, `FormSelect`, `FormSelectPopover`, `FormSelectItem`, `FormSwitch`, `FormLabel`, `FormError`, `FormSubmit`, `FormCheckbox`, `FormRadioGroup`, `FormRadio`, `FormPush`, `FormRemove`, `FormReset`, `FormNumberInput`, `FormSlider`, `FormStarRating`, `FormColorPicker`, `FormIconPicker`, `FormTagInput`, `FormDateRangePicker`, `useFormStore`, `useFormContext` |
412
+
413
+ ### Form
414
+
415
+ > 접근성 폼 시스템. opt-ui-primitives Form API 기반. 31 exports: FormRoot, FormProvider, FormGroup, FormGroupLabel, FormDescription, FormField, FormControl, FormInput, FormTextarea, FormSelect, FormSelectPopover, FormSelectItem, FormSwitch, FormLabel, FormError, FormSubmit, FormCheckbox, FormRadioGroup, FormRadio, FormPush, FormRemove, FormReset, FormNumberInput, FormSlider, FormStarRating, FormColorPicker, FormIconPicker, FormTagInput, FormDateRangePicker + useFormStore, useFormContext hooks.
416
+
417
+ **Import:** `import { FormRoot } from "@reopt-ai/opt-ui"`
418
+
419
+ **FormRoot:**
420
+
421
+ | Prop | Type | Required | Default | Description |
422
+ | ----------- | ----------- | -------- | ------- | ------------------------------------------------------------------------ |
423
+ | `store` | `FormStore` | | | useFormStore()로 생성한 폼 스토어. 미지정 시 자식의 FormProvider가 제공. |
424
+ | `children` | `ReactNode` | Yes | | 폼 필드들 |
425
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
426
+
427
+ **FormInput:**
428
+
429
+ | Prop | Type | Required | Default | Description |
430
+ | ------------- | -------- | -------- | ------- | ------------------------------- |
431
+ | `name` | `string` | Yes | | 폼 필드 이름 (스토어 키와 매칭) |
432
+ | `type` | `string` | | "text" | 입력 타입 |
433
+ | `placeholder` | `string` | | | 플레이스홀더 |
434
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
435
+
436
+ **FormTextarea:**
437
+
438
+ | Prop | Type | Required | Default | Description |
439
+ | ------------- | ------------------------------------------------ | -------- | ---------- | --------------- |
440
+ | `name` | `string` | Yes | | 폼 필드 이름 |
441
+ | `rows` | `number` | | 4 | 표시 줄 수 |
442
+ | `resize` | `"none" \| "vertical" \| "horizontal" \| "both"` | | "vertical" | 크기 조절 방향 |
443
+ | `placeholder` | `string` | | | 플레이스홀더 |
444
+ | `className` | `string` | | | 추가 CSS 클래스 |
445
+
446
+ **FormSelect:**
447
+
448
+ | Prop | Type | Required | Default | Description |
449
+ | ------------- | ----------- | -------- | ------- | ------------------------------ |
450
+ | `name` | `string` | Yes | | 폼 필드 이름 |
451
+ | `children` | `ReactNode` | Yes | | FormSelectPopover 등 자식 요소 |
452
+ | `placeholder` | `string` | | | 미선택 시 표시 텍스트 |
453
+ | `className` | `string` | | | 추가 CSS 클래스 |
454
+
455
+ **FormSelectPopover:**
456
+
457
+ | Prop | Type | Required | Default | Description |
458
+ | ----------- | ----------- | -------- | ------- | -------------------- |
459
+ | `children` | `ReactNode` | Yes | | FormSelectItem 목록 |
460
+ | `sameWidth` | `boolean` | | true | 트리거와 같은 너비 |
461
+ | `gutter` | `number` | | 4 | 트리거와의 간격 (px) |
462
+
463
+ **FormSelectItem:**
464
+
465
+ | Prop | Type | Required | Default | Description |
466
+ | ---------- | ----------- | -------- | ------- | ----------- |
467
+ | `value` | `string` | Yes | | 옵션 값 |
468
+ | `children` | `ReactNode` | Yes | | 옵션 레이블 |
469
+
470
+ **FormSwitch:**
471
+
472
+ | Prop | Type | Required | Default | Description |
473
+ | ---------- | ---------------------- | -------- | ------- | ------------- |
474
+ | `name` | `string` | Yes | | 폼 필드 이름 |
475
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 스위치 크기 |
476
+ | `disabled` | `boolean` | | | 비활성화 상태 |
477
+
478
+ **FormLabel:**
479
+
480
+ | Prop | Type | Required | Default | Description |
481
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
482
+ | `name` | `string` | Yes | | 연결된 필드 이름 |
483
+ | `children` | `ReactNode` | Yes | | 레이블 텍스트 |
484
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
485
+
486
+ **FormError:**
487
+
488
+ | Prop | Type | Required | Default | Description |
489
+ | ----------- | -------- | -------- | ------- | ---------------------- |
490
+ | `name` | `string` | Yes | | 연결된 필드 이름 |
491
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
492
+
493
+ **FormSubmit:**
494
+
495
+ | Prop | Type | Required | Default | Description |
496
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
497
+ | `children` | `ReactNode` | Yes | | 버튼 텍스트 |
498
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
499
+
500
+ **FormProvider:**
501
+
502
+ | Prop | Type | Required | Default | Description |
503
+ | ---------- | ----------- | -------- | ------- | --------------------------------- |
504
+ | `store` | `FormStore` | Yes | | useFormStore()로 생성한 폼 스토어 |
505
+ | `children` | `ReactNode` | Yes | | 자식 폼 요소 |
506
+
507
+ **FormGroup:**
508
+
509
+ | Prop | Type | Required | Default | Description |
510
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
511
+ | `children` | `ReactNode` | Yes | | 그룹 내 필드들 |
512
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
513
+
514
+ **FormGroupLabel:**
515
+
516
+ | Prop | Type | Required | Default | Description |
517
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
518
+ | `children` | `ReactNode` | Yes | | 그룹 레이블 텍스트 |
519
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
520
+
521
+ **FormDescription:**
522
+
523
+ | Prop | Type | Required | Default | Description |
524
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
525
+ | `name` | `string` | Yes | | 연결된 필드 이름 |
526
+ | `children` | `ReactNode` | Yes | | 도움말 텍스트 |
527
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
528
+
529
+ **FormField:**
530
+
531
+ | Prop | Type | Required | Default | Description |
532
+ | ----------- | ----------- | -------- | ------- | ------------------------ |
533
+ | `name` | `string` | Yes | | 필드 이름 |
534
+ | `children` | `ReactNode` | Yes | | label + input + error 등 |
535
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
536
+
537
+ **FormControl:**
538
+
539
+ | Prop | Type | Required | Default | Description |
540
+ | ----------- | -------- | -------- | ------- | ---------------------- |
541
+ | `name` | `string` | Yes | | 연결된 필드 이름 |
542
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
543
+
544
+ **FormCheckbox:**
545
+
546
+ | Prop | Type | Required | Default | Description |
547
+ | ----------- | -------- | -------- | ------- | ------------------------ |
548
+ | `name` | `string` | Yes | | 폼 필드 이름 (스토어 키) |
549
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
550
+
551
+ **FormRadioGroup:**
552
+
553
+ | Prop | Type | Required | Default | Description |
554
+ | ----------- | ----------- | -------- | ------- | ------------------------ |
555
+ | `name` | `string` | Yes | | 폼 필드 이름 (스토어 키) |
556
+ | `children` | `ReactNode` | Yes | | FormRadio 목록 |
557
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
558
+
559
+ **FormRadio:**
560
+
561
+ | Prop | Type | Required | Default | Description |
562
+ | ----------- | -------- | -------- | ------- | ------------------------ |
563
+ | `name` | `string` | Yes | | 폼 필드 이름 (스토어 키) |
564
+ | `value` | `string` | Yes | | 라디오 값 |
565
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
566
+
567
+ **FormPush:**
568
+
569
+ | Prop | Type | Required | Default | Description |
570
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
571
+ | `name` | `string` | Yes | | 배열 필드 이름 |
572
+ | `value` | `unknown` | Yes | | 추가할 기본값 |
573
+ | `children` | `ReactNode` | Yes | | 버튼 텍스트 |
574
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
575
+
576
+ **FormRemove:**
577
+
578
+ | Prop | Type | Required | Default | Description |
579
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
580
+ | `name` | `string` | Yes | | 배열 필드 이름 |
581
+ | `index` | `number` | Yes | | 제거할 인덱스 |
582
+ | `children` | `ReactNode` | Yes | | 버튼 텍스트 |
583
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
584
+
585
+ **FormReset:**
586
+
587
+ | Prop | Type | Required | Default | Description |
588
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
589
+ | `children` | `ReactNode` | Yes | | 버튼 텍스트 |
590
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
591
+
592
+ **useFormContext:**
593
+
594
+ | Prop | Type | Required | Default | Description |
595
+ | ---- | ---- | -------- | ------- | ----------- |
596
+
597
+ **useFormStore:**
598
+
599
+ | Prop | Type | Required | Default | Description |
600
+ | --------------- | ------------------------- | -------- | ------- | ----------- |
601
+ | `defaultValues` | `Record<string, unknown>` | | | 초기 폼 값 |
602
+
603
+ **Examples:**
604
+
605
+ _기본 폼:_
606
+
607
+ ```tsx
608
+ import {
609
+ useFormStore,
610
+ FormRoot,
611
+ FormField,
612
+ FormControl,
613
+ FormLabel,
614
+ FormError,
615
+ FormSubmit,
616
+ Input,
617
+ } from "@reopt-ai/opt-ui";
618
+
619
+ const store = useFormStore({ defaultValues: { name: "", email: "" } });
620
+
621
+ <FormRoot store={store}>
622
+ <FormField name="name">
623
+ <FormLabel name="name">이름</FormLabel>
624
+ <FormControl name="name">
625
+ <Input />
626
+ </FormControl>
627
+ <FormError name="name" />
628
+ </FormField>
629
+ <FormSubmit>저장</FormSubmit>
630
+ </FormRoot>;
631
+ ```
632
+
633
+ _검증 + 비동기 제출:_
634
+
635
+ ```tsx
636
+ import {
637
+ useFormStore,
638
+ FormRoot,
639
+ FormField,
640
+ FormControl,
641
+ FormLabel,
642
+ FormError,
643
+ FormSubmit,
644
+ Input,
645
+ } from "@reopt-ai/opt-ui";
646
+
647
+ const store = useFormStore({
648
+ defaultValues: { email: "" },
649
+ validate: (values) => {
650
+ const errors: Record<string, string> = {};
651
+ if (!values.email) errors.email = "이메일을 입력하세요";
652
+ return errors;
653
+ },
654
+ onSubmit: async (values) => {
655
+ await fetch("/api/submit", {
656
+ method: "POST",
657
+ body: JSON.stringify(values),
658
+ });
659
+ },
660
+ });
661
+ ```
662
+
663
+ _schemaValidator (Zod):_
664
+
665
+ ```tsx
666
+ import { useFormStore, schemaValidator } from "@reopt-ai/opt-ui";
667
+ import { z } from "zod";
668
+
669
+ const schema = z.object({
670
+ name: z.string().min(1, "필수 항목"),
671
+ age: z.number().min(0, "0 이상"),
672
+ });
673
+
674
+ const store = useFormStore({
675
+ defaultValues: { name: "", age: 0 },
676
+ validate: schemaValidator(schema),
677
+ });
678
+ ```
679
+
680
+ ## Display
681
+
682
+ | Component | Description | Key Exports |
683
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
684
+ | Kbd | 키보드 단축키를 시각적으로 표시하는 인라인 뱃지 컴포넌트. | `Kbd` |
685
+ | Button | 접근성 버튼 컴포넌트. 4가지 변형(primary, secondary, ghost, danger), 3가지 크기, 로딩/아이콘과 aria-busy 상태를 지원합니다. | `Button` |
686
+ | Badge | 인라인 상태 뱃지. 5가지 색상 변형, 상태 점(dot) 옵션 지원. | `Badge` |
687
+ | Alert | 알림 메시지 컴포넌트. 4가지 변형(info, success, warning, error), 제목/본문/닫기 버튼 지원. | `Alert` |
688
+ | Spinner | 로딩 상태 표시 스피너. 3가지 크기와 장식용 aria-hidden 모드를 지원합니다. | `Spinner` |
689
+ | LoadingOverlay | 로딩 오버레이. 중복 status 없이 하나의 aria-busy 라이브 영역으로 콘텐츠 위 로딩 상태를 표시합니다. | `LoadingOverlay` |
690
+ | Progress | 진행률 표시. Linear/Circular, determinate/indeterminate 변형과 안전하게 보정된 ARIA 값을 지원합니다. | `Progress`, `CircularProgress` |
691
+ | Avatar | 사용자 아바타. 이미지, 이니셜 자동 생성, 스크린 리더에 노출되는 상태 인디케이터, 그룹을 지원합니다. | `Avatar`, `AvatarGroup` |
692
+ | Skeleton | 로딩 플레이스홀더. text/circular/rectangular 변형. 프리셋 컴포넌트 포함. | `Skeleton`, `SkeletonText`, `SkeletonAvatar`, `SkeletonCard`, `SkeletonTable` |
693
+ | Card | 범용 카드 컨테이너. Header, Title, Description, Content, Footer 하위 컴포넌트 포함. | `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter` |
694
+ | Breadcrumb | 현재 위치를 표시하고 계층 탐색을 제공하는 컴포넌트. | `Breadcrumb` |
695
+ | EmptyState | 데이터가 없을 때 표시하는 빈 상태 컴포넌트. 아이콘, 제목, 설명, 액션 버튼 지원. | `EmptyState` |
696
+ | StatCard | 트렌드 방향과 변화량을 표시하는 통계 카드. | `StatCard` |
697
+ | Logo | 아이콘+텍스트 조합, 이미지, 링크 래핑을 지원하는 로고 컴포넌트. Sidebar 등에서 활용. | `Logo` |
698
+
699
+ ### Kbd
700
+
701
+ > 키보드 단축키를 시각적으로 표시하는 인라인 뱃지 컴포넌트.
702
+
703
+ **Import:** `import { Kbd } from "@reopt-ai/opt-ui"`
704
+
705
+ | Prop | Type | Required | Default | Description |
706
+ | ---------- | ----------- | -------- | ------- | -------------------- |
707
+ | `children` | `ReactNode` | Yes | | 표시할 단축키 텍스트 |
708
+
709
+ ### Button
710
+
711
+ > 접근성 버튼 컴포넌트. 4가지 변형(primary, secondary, ghost, danger), 3가지 크기, 로딩/아이콘과 aria-busy 상태를 지원합니다.
712
+
713
+ **Import:** `import { Button } from "@reopt-ai/opt-ui"`
714
+
715
+ | Prop | Type | Required | Default | Description |
716
+ | ----------- | ------------------------------------------------- | -------- | --------- | ----------------------------------------- |
717
+ | `variant` | `"primary" \| "secondary" \| "ghost" \| "danger"` | | "primary" | 버튼 스타일 변형 |
718
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 버튼 크기 |
719
+ | `loading` | `boolean` | | false | 로딩 상태. true면 스피너 표시 및 비활성화 |
720
+ | `leftIcon` | `ReactNode` | | | 버튼 텍스트 왼쪽에 표시할 아이콘 |
721
+ | `rightIcon` | `ReactNode` | | | 버튼 텍스트 오른쪽에 표시할 아이콘 |
722
+ | `disabled` | `boolean` | | | 비활성화 상태 |
723
+
724
+ **Examples:**
725
+
726
+ _버튼 변형:_
727
+
728
+ ```tsx
729
+ import { Button } from "@reopt-ai/opt-ui";
730
+
731
+ <Button variant="primary">Primary</Button>
732
+ <Button variant="secondary">Secondary</Button>
733
+ <Button variant="ghost">Ghost</Button>
734
+ <Button variant="danger" disabled>Danger</Button>
735
+ <Button variant="primary" loading>저장 중...</Button>
736
+ ```
737
+
738
+ ### Badge
739
+
740
+ > 인라인 상태 뱃지. 5가지 색상 변형, 상태 점(dot) 옵션 지원.
741
+
742
+ **Import:** `import { Badge } from "@reopt-ai/opt-ui"`
743
+
744
+ | Prop | Type | Required | Default | Description |
745
+ | -------------- | ---------------------------------------------------------- | -------- | --------- | -------------------------------------------- |
746
+ | `variant` | `"default" \| "success" \| "warning" \| "error" \| "info"` | | "default" | 뱃지 색상 변형 |
747
+ | `size` | `"sm" \| "md"` | | "md" | 뱃지 크기 |
748
+ | `dot` | `boolean` | | false | 상태 점 표시 여부 |
749
+ | `progress` | `number` | | | 원형 프로그레스 값 (0-100). 설정 시 dot 무시 |
750
+ | `progressSize` | `number` | | 16 | 프로그레스 링 SVG 크기 (px) |
751
+
752
+ ### Alert
753
+
754
+ > 알림 메시지 컴포넌트. 4가지 변형(info, success, warning, error), 제목/본문/닫기 버튼 지원.
755
+
756
+ **Import:** `import { Alert } from "@reopt-ai/opt-ui"`
757
+
758
+ | Prop | Type | Required | Default | Description |
759
+ | ------------- | --------------------------------------------- | -------- | ------- | ----------------------------------------------------- |
760
+ | `variant` | `"info" \| "success" \| "warning" \| "error"` | | "info" | 알림 변형 (색상 및 아이콘 결정) |
761
+ | `title` | `string` | | | 알림 제목 |
762
+ | `icon` | `ReactNode` | | | 커스텀 아이콘. 미지정 시 변형에 따른 기본 아이콘 사용 |
763
+ | `dismissible` | `boolean` | | false | 닫기 버튼 표시 여부 |
764
+ | `onDismiss` | `() => void` | | | 닫기 버튼 클릭 핸들러 |
765
+
766
+ ### Spinner
767
+
768
+ > 로딩 상태 표시 스피너. 3가지 크기와 장식용 aria-hidden 모드를 지원합니다.
769
+
770
+ **Import:** `import { Spinner } from "@reopt-ai/opt-ui"`
771
+
772
+ | Prop | Type | Required | Default | Description |
773
+ | ------ | ---------------------- | -------- | ------- | ----------- |
774
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 스피너 크기 |
775
+
776
+ ### LoadingOverlay
777
+
778
+ > 로딩 오버레이. 중복 status 없이 하나의 aria-busy 라이브 영역으로 콘텐츠 위 로딩 상태를 표시합니다.
779
+
780
+ **Import:** `import { LoadingOverlay } from "@reopt-ai/opt-ui"`
781
+ **Dependencies:** spinner (core)
782
+
783
+ | Prop | Type | Required | Default | Description |
784
+ | ---------- | ----------- | -------- | ---------- | ------------- |
785
+ | `loading` | `boolean` | Yes | | 로딩 상태 |
786
+ | `children` | `ReactNode` | Yes | | 감싸는 콘텐츠 |
787
+ | `label` | `string` | | "Loading…" | 로딩 텍스트 |
788
+
789
+ ### Progress
790
+
791
+ > 진행률 표시. Linear/Circular, determinate/indeterminate 변형과 안전하게 보정된 ARIA 값을 지원합니다.
792
+
793
+ **Import:** `import { Progress } from "@reopt-ai/opt-ui"`
794
+
795
+ **Progress:**
796
+
797
+ | Prop | Type | Required | Default | Description |
798
+ | --------------- | ------------------------------------------------ | -------- | --------- | ------------------------------------ |
799
+ | `value` | `number` | | | 현재 값. 생략하면 indeterminate 상태 |
800
+ | `max` | `number` | | 100 | 최대 값 |
801
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 진행바 높이 |
802
+ | `variant` | `"default" \| "success" \| "warning" \| "error"` | | "default" | 색상 변형 |
803
+ | `showLabel` | `boolean` | | false | 퍼센트 표시 |
804
+ | `label` | `string` | | | 레이블 텍스트 |
805
+ | `animated` | `boolean` | | true | 애니메이션 활성화 |
806
+ | `indeterminate` | `boolean` | | false | 값을 알 수 없는 진행 상태 |
807
+
808
+ **CircularProgress:**
809
+
810
+ | Prop | Type | Required | Default | Description |
811
+ | --------------- | ------------------------------------------------ | -------- | --------- | ------------------------------------ |
812
+ | `value` | `number` | | | 현재 값. 생략하면 indeterminate 상태 |
813
+ | `max` | `number` | | 100 | 최대 값 |
814
+ | `size` | `number` | | 48 | 원 크기 (px) |
815
+ | `strokeWidth` | `number` | | 4 | 선 두께 |
816
+ | `variant` | `"default" \| "success" \| "warning" \| "error"` | | "default" | 색상 변형 |
817
+ | `showLabel` | `boolean` | | false | 중앙에 퍼센트 표시 |
818
+ | `label` | `string` | | | 접근성 레이블 |
819
+ | `indeterminate` | `boolean` | | false | 값을 알 수 없는 진행 상태 |
820
+
821
+ ### Avatar
822
+
823
+ > 사용자 아바타. 이미지, 이니셜 자동 생성, 스크린 리더에 노출되는 상태 인디케이터, 그룹을 지원합니다.
824
+
825
+ **Import:** `import { Avatar } from "@reopt-ai/opt-ui"`
826
+
827
+ **Avatar:**
828
+
829
+ | Prop | Type | Required | Default | Description |
830
+ | ---------- | ------------------------------------------- | -------- | ------- | -------------------------------------- |
831
+ | `src` | `string` | | | 이미지 URL |
832
+ | `alt` | `string` | | | 이미지 대체 텍스트 |
833
+ | `name` | `string` | | | 사용자 이름 (이니셜 생성 및 색상 결정) |
834
+ | `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | | "md" | 아바타 크기 |
835
+ | `fallback` | `ReactNode` | | | 이미지 실패 시 표시할 콘텐츠 |
836
+ | `status` | `"online" \| "offline" \| "busy" \| "away"` | | | 상태 인디케이터 |
837
+
838
+ **AvatarGroup:**
839
+
840
+ | Prop | Type | Required | Default | Description |
841
+ | ---------- | -------------------------------------- | -------- | ------- | -------------- |
842
+ | `max` | `number` | | 4 | 최대 표시 개수 |
843
+ | `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | | "md" | 아바타 크기 |
844
+ | `children` | `ReactNode` | Yes | | Avatar 요소들 |
845
+
846
+ ### Skeleton
847
+
848
+ > 로딩 플레이스홀더. text/circular/rectangular 변형. 프리셋 컴포넌트 포함.
849
+
850
+ **Import:** `import { Skeleton } from "@reopt-ai/opt-ui"`
851
+
852
+ **Skeleton:**
853
+
854
+ | Prop | Type | Required | Default | Description |
855
+ | ---------- | --------------------------------------- | -------- | ------- | ----------------------- |
856
+ | `variant` | `"text" \| "circular" \| "rectangular"` | | "text" | 스켈레톤 형태 |
857
+ | `width` | `string \| number` | | | 너비 (px 또는 CSS 값) |
858
+ | `height` | `string \| number` | | | 높이 (px 또는 CSS 값) |
859
+ | `lines` | `number` | | 1 | text variant일 때 줄 수 |
860
+ | `animated` | `boolean` | | true | 펄스 애니메이션 활성화 |
861
+
862
+ **SkeletonText:**
863
+
864
+ | Prop | Type | Required | Default | Description |
865
+ | ------- | -------- | -------- | ------- | ------------ |
866
+ | `lines` | `number` | | 3 | 텍스트 줄 수 |
867
+
868
+ **SkeletonAvatar:**
869
+
870
+ | Prop | Type | Required | Default | Description |
871
+ | ------ | -------- | -------- | ------- | ---------------- |
872
+ | `size` | `number` | | 40 | 아바타 크기 (px) |
873
+
874
+ **SkeletonTable:**
875
+
876
+ | Prop | Type | Required | Default | Description |
877
+ | --------- | -------- | -------- | ------- | ------------ |
878
+ | `rows` | `number` | | 5 | 테이블 행 수 |
879
+ | `columns` | `number` | | 4 | 테이블 열 수 |
880
+
881
+ ### Card
882
+
883
+ > 범용 카드 컨테이너. Header, Title, Description, Content, Footer 하위 컴포넌트 포함.
884
+
885
+ **Import:** `import { Card } from "@reopt-ai/opt-ui"`
886
+
887
+ **Card:**
888
+
889
+ | Prop | Type | Required | Default | Description |
890
+ | ------------------ | ----------------------------------- | -------- | --------- | ------------------------------ |
891
+ | `variant` | `"default" \| "outline" \| "ghost"` | | "default" | 카드 스타일 변형 |
892
+ | `padding` | `"none" \| "sm" \| "md" \| "lg"` | | "md" | 내부 패딩 크기 |
893
+ | `selectable` | `boolean` | | false | 선택 가능 모드 활성화 |
894
+ | `selected` | `boolean` | | | 선택 상태 (selectable=true 시) |
895
+ | `onSelectedChange` | `(selected: boolean) => void` | | | 선택 상태 변경 콜백 |
896
+
897
+ **CardHeader:**
898
+
899
+ | Prop | Type | Required | Default | Description |
900
+ | ---- | ---- | -------- | ------- | ----------- |
901
+
902
+ **CardTitle:**
903
+
904
+ | Prop | Type | Required | Default | Description |
905
+ | ---- | ---- | -------- | ------- | ----------- |
906
+
907
+ **CardDescription:**
908
+
909
+ | Prop | Type | Required | Default | Description |
910
+ | ---- | ---- | -------- | ------- | ----------- |
911
+
912
+ **CardContent:**
913
+
914
+ | Prop | Type | Required | Default | Description |
915
+ | ---- | ---- | -------- | ------- | ----------- |
916
+
917
+ **CardFooter:**
918
+
919
+ | Prop | Type | Required | Default | Description |
920
+ | ---- | ---- | -------- | ------- | ----------- |
921
+
922
+ **Examples:**
923
+
924
+ _카드 레이아웃:_
925
+
926
+ ```tsx
927
+ import {
928
+ Card,
929
+ CardHeader,
930
+ CardTitle,
931
+ CardDescription,
932
+ CardContent,
933
+ } from "@reopt-ai/opt-ui";
934
+
935
+ <Card>
936
+ <CardHeader>
937
+ <CardTitle>프로젝트 요약</CardTitle>
938
+ <CardDescription>최근 7일간 활동 현황</CardDescription>
939
+ </CardHeader>
940
+ <CardContent>
941
+ <p>활성 사용자: 1,234명</p>
942
+ </CardContent>
943
+ </Card>;
944
+ ```
945
+
946
+ ### Breadcrumb
947
+
948
+ > 현재 위치를 표시하고 계층 탐색을 제공하는 컴포넌트.
949
+
950
+ **Import:** `import { Breadcrumb } from "@reopt-ai/opt-ui"`
951
+
952
+ | Prop | Type | Required | Default | Description |
953
+ | ------------ | -------------------------------- | -------- | ------- | ------------------------------------------- |
954
+ | `items` | `BreadcrumbItem[]` | Yes | | 경로 항목 배열. { id, label, href?, icon? } |
955
+ | `separator` | `ReactNode` | | "/" | 항목 간 구분자 |
956
+ | `onNavigate` | `(item: BreadcrumbItem) => void` | | | 항목 클릭 핸들러 |
957
+
958
+ ### EmptyState
959
+
960
+ > 데이터가 없을 때 표시하는 빈 상태 컴포넌트. 아이콘, 제목, 설명, 액션 버튼 지원.
961
+
962
+ **Import:** `import { EmptyState } from "@reopt-ai/opt-ui"`
963
+
964
+ | Prop | Type | Required | Default | Description |
965
+ | ------------- | -------------------- | -------- | ------- | -------------------------------------------- |
966
+ | `title` | `string` | Yes | | 빈 상태 제목 |
967
+ | `description` | `string` | | | 빈 상태 설명 |
968
+ | `icon` | `ReactNode` | | | 표시할 아이콘 |
969
+ | `actions` | `EmptyStateAction[]` | | | 액션 버튼 배열. { label, onClick, variant? } |
970
+
971
+ ### StatCard
972
+
973
+ > 트렌드 방향과 변화량을 표시하는 통계 카드.
974
+
975
+ **Import:** `import { StatCard } from "@reopt-ai/opt-ui"`
976
+
977
+ | Prop | Type | Required | Default | Description |
978
+ | -------- | ----------------------------- | -------- | ------- | ------------------------------- |
979
+ | `title` | `string` | Yes | | 통계 제목 |
980
+ | `value` | `string` | Yes | | 통계 값 |
981
+ | `change` | `string` | Yes | | 변화량 텍스트 (예: '+12.5%') |
982
+ | `trend` | `"up" \| "down" \| "neutral"` | Yes | | 트렌드 방향. 색상과 아이콘 결정 |
983
+
984
+ ### Logo
985
+
986
+ > 아이콘+텍스트 조합, 이미지, 링크 래핑을 지원하는 로고 컴포넌트. Sidebar 등에서 활용.
987
+
988
+ **Import:** `import { Logo } from "@reopt-ai/opt-ui"`
989
+
990
+ | Prop | Type | Required | Default | Description |
991
+ | ----------- | ---------------------- | -------- | ------- | -------------------------------- |
992
+ | `src` | `string` | | | 로고 이미지 URL |
993
+ | `icon` | `ReactNode` | | | 로고 아이콘 — src 미설정 시 표시 |
994
+ | `text` | `string` | | | 텍스트 레이블 |
995
+ | `alt` | `string` | | "Logo" | 이미지 alt 텍스트 |
996
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 크기 (sm=24px, md=32px, lg=40px) |
997
+ | `href` | `string` | | | 링크 URL — 설정 시 <a>로 래핑 |
998
+ | `onClick` | `() => void` | | | 클릭 핸들러 |
999
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1000
+
1001
+ ## Navigation & Overlay
1002
+
1003
+ | Component | Description | Key Exports |
1004
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1005
+ | Menu | 메뉴바 + 개별 메뉴 시스템. 10 exports: MenubarRoot, MenubarContainer, MenuRoot, MenuTrigger, MenuButtonArrow, MenuPopover, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuSeparator. | `MenubarRoot`, `MenubarContainer`, `MenuRoot`, `MenuTrigger`, `MenuButtonArrow`, `MenuPopover`, `MenuItem`, `MenuItemCheckbox`, `MenuItemRadio`, `MenuSeparator` |
1006
+ | DropdownMenu | Menu Core 위의 간편 래퍼. 단일 트리거 드롭다운 메뉴에 특화되었습니다. | `DropdownMenu`, `DropdownTrigger`, `DropdownContent`, `DropdownItem`, `DropdownSeparator` |
1007
+ | Popover | Popover 프리미티브 래퍼. 트리거 요소에 연결된 플로팅 콘텐츠를 표시합니다. | `PopoverRoot`, `PopoverTrigger`, `PopoverContent`, `PopoverClose` |
1008
+ | Tabs | 탭 패널 시스템. 4-part anatomy: Root, TabList, Tab, TabPanel. | `TabsRoot`, `TabList`, `Tab`, `TabPanel` |
1009
+ | Disclosure | 접기/펼치기 콘텐츠 영역. 3-part anatomy: Root, Trigger, Content. | `DisclosureRoot`, `DisclosureTrigger`, `DisclosureContent` |
1010
+ | Dialog | 모달 다이얼로그. 9-part anatomy: Root, Disclosure, Panel, Heading, Description, Dismiss, Header, Body, Footer. | `DialogRoot`, `DialogDisclosure`, `DialogPanel`, `AlertDialogPanel`, `DialogHeading`, `DialogDescription`, `DialogDismiss`, `DialogHeader`, `DialogBody`, `DialogFooter` |
1011
+ | Drawer | 사이드/하단 슬라이드 패널. Dialog 기반 compound 패턴으로 접근성을 보장합니다. | `DrawerRoot`, `DrawerPanel`, `DrawerClose` |
1012
+ | Toolbar | 툴바 컨테이너 + 툴팁. 7 exports: ToolbarRoot, ToolbarContainer, ToolbarButton, ToolbarSeparator, TooltipProvider, TooltipAnchor, Tooltip. | `ToolbarRoot`, `ToolbarContainer`, `ToolbarButton`, `ToolbarSeparator`, `TooltipProvider`, `TooltipAnchor`, `Tooltip` |
1013
+ | Pagination | 페이지 넘김 네비게이션. ellipsis 포함 페이지 번호, compact/count variant, totalPages/cursor pagination, Prev/Next 버튼을 제공합니다. | `Pagination` |
1014
+
1015
+ ### Menu
1016
+
1017
+ > 메뉴바 + 개별 메뉴 시스템. 10 exports: MenubarRoot, MenubarContainer, MenuRoot, MenuTrigger, MenuButtonArrow, MenuPopover, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuSeparator.
1018
+
1019
+ **Import:** `import { MenubarRoot } from "@reopt-ai/opt-ui"`
1020
+
1021
+ **MenubarRoot:**
1022
+
1023
+ | Prop | Type | Required | Default | Description |
1024
+ | ---- | ---- | -------- | ------- | ----------- |
1025
+
1026
+ **MenubarContainer:**
1027
+
1028
+ | Prop | Type | Required | Default | Description |
1029
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1030
+ | `children` | `ReactNode` | Yes | | MenuRoot 요소들 |
1031
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1032
+
1033
+ **MenuRoot:**
1034
+
1035
+ | Prop | Type | Required | Default | Description |
1036
+ | ---- | ---- | -------- | ------- | ----------- |
1037
+
1038
+ **MenuTrigger:**
1039
+
1040
+ | Prop | Type | Required | Default | Description |
1041
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1042
+ | `children` | `ReactNode` | Yes | | 메뉴 버튼 레이블 |
1043
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1044
+
1045
+ **MenuButtonArrow:**
1046
+
1047
+ | Prop | Type | Required | Default | Description |
1048
+ | ----------- | -------- | -------- | ------- | ----------------- |
1049
+ | `className` | `string` | | | 화살표 CSS 클래스 |
1050
+
1051
+ **MenuPopover:**
1052
+
1053
+ | Prop | Type | Required | Default | Description |
1054
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1055
+ | `children` | `ReactNode` | Yes | | MenuItem 목록 |
1056
+ | `gutter` | `number` | | 8 | 트리거와의 간격 (px) |
1057
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1058
+
1059
+ **MenuItem:**
1060
+
1061
+ | Prop | Type | Required | Default | Description |
1062
+ | ----------- | ------------ | -------- | ------- | ---------------------- |
1063
+ | `children` | `ReactNode` | Yes | | 메뉴 항목 내용 |
1064
+ | `onClick` | `() => void` | | | 클릭 핸들러 |
1065
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1066
+
1067
+ **MenuItemCheckbox:**
1068
+
1069
+ | Prop | Type | Required | Default | Description |
1070
+ | ----------- | -------------------------- | -------- | ------- | ---------------------- |
1071
+ | `name` | `string` | Yes | | 체크박스 이름 |
1072
+ | `value` | `string` | Yes | | 체크박스 값 |
1073
+ | `checked` | `boolean` | | | 체크 상태 |
1074
+ | `onChange` | `(e: ChangeEvent) => void` | | | 변경 핸들러 |
1075
+ | `children` | `ReactNode` | Yes | | 항목 내용 |
1076
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1077
+
1078
+ **MenuItemRadio:**
1079
+
1080
+ | Prop | Type | Required | Default | Description |
1081
+ | ----------- | -------------------------- | -------- | ------- | ---------------------- |
1082
+ | `name` | `string` | Yes | | 라디오 그룹 이름 |
1083
+ | `value` | `string` | Yes | | 라디오 값 |
1084
+ | `checked` | `boolean` | | | 선택 상태 |
1085
+ | `onChange` | `(e: ChangeEvent) => void` | | | 변경 핸들러 |
1086
+ | `children` | `ReactNode` | Yes | | 항목 내용 |
1087
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1088
+
1089
+ **MenuSeparator:**
1090
+
1091
+ | Prop | Type | Required | Default | Description |
1092
+ | ----------- | -------- | -------- | ------- | ---------------------- |
1093
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1094
+
1095
+ ### DropdownMenu
1096
+
1097
+ > Menu Core 위의 간편 래퍼. 단일 트리거 드롭다운 메뉴에 특화되었습니다.
1098
+
1099
+ **Import:** `import { DropdownMenu } from "@reopt-ai/opt-ui"`
1100
+
1101
+ **DropdownContent:**
1102
+
1103
+ | Prop | Type | Required | Default | Description |
1104
+ | -------- | -------- | -------- | ------- | -------------------- |
1105
+ | `gutter` | `number` | | 4 | 트리거와의 간격 (px) |
1106
+
1107
+ **DropdownItem:**
1108
+
1109
+ | Prop | Type | Required | Default | Description |
1110
+ | ---------- | ---------------------------- | -------- | --------- | --------------------------------------------------- |
1111
+ | `variant` | `"default" \| "destructive"` | | "default" | 메뉴 아이템 시각 변형. destructive는 위험 작업 강조 |
1112
+ | `onClick` | `() => void` | | | 항목 클릭 핸들러 |
1113
+ | `disabled` | `boolean` | | | 비활성 상태 |
1114
+
1115
+ ### Popover
1116
+
1117
+ > Popover 프리미티브 래퍼. 트리거 요소에 연결된 플로팅 콘텐츠를 표시합니다.
1118
+
1119
+ **Import:** `import { PopoverRoot } from "@reopt-ai/opt-ui"`
1120
+
1121
+ | Prop | Type | Required | Default | Description |
1122
+ | ----------- | -------- | -------- | ------- | -------------------- |
1123
+ | `gutter` | `number` | | 8 | 트리거와의 간격 (px) |
1124
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1125
+
1126
+ ### Tabs
1127
+
1128
+ > 탭 패널 시스템. 4-part anatomy: Root, TabList, Tab, TabPanel.
1129
+
1130
+ **Import:** `import { TabsRoot } from "@reopt-ai/opt-ui"`
1131
+
1132
+ **TabsRoot:**
1133
+
1134
+ | Prop | Type | Required | Default | Description |
1135
+ | ------------------- | ------------------------------ | -------- | ------- | ------------------------- |
1136
+ | `defaultSelectedId` | `string` | | | 초기 선택된 탭 ID |
1137
+ | `selectedId` | `string \| null` | | | 제어 모드: 선택된 탭 ID |
1138
+ | `setSelectedId` | `(id: string \| null) => void` | | | 제어 모드: 탭 변경 핸들러 |
1139
+
1140
+ **TabList:**
1141
+
1142
+ | Prop | Type | Required | Default | Description |
1143
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1144
+ | `children` | `ReactNode` | Yes | | Tab 요소들 |
1145
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1146
+
1147
+ **Tab:**
1148
+
1149
+ | Prop | Type | Required | Default | Description |
1150
+ | ----------- | ----------- | -------- | ------- | ------------------------------------ |
1151
+ | `id` | `string` | Yes | | 탭 고유 ID (TabPanel의 tabId와 매칭) |
1152
+ | `children` | `ReactNode` | Yes | | 탭 레이블 |
1153
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1154
+
1155
+ **TabPanel:**
1156
+
1157
+ | Prop | Type | Required | Default | Description |
1158
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1159
+ | `tabId` | `string` | Yes | | 연결된 Tab의 ID |
1160
+ | `children` | `ReactNode` | Yes | | 패널 콘텐츠 |
1161
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1162
+
1163
+ ### Disclosure
1164
+
1165
+ > 접기/펼치기 콘텐츠 영역. 3-part anatomy: Root, Trigger, Content.
1166
+
1167
+ **Import:** `import { DisclosureRoot } from "@reopt-ai/opt-ui"`
1168
+
1169
+ **DisclosureRoot:**
1170
+
1171
+ | Prop | Type | Required | Default | Description |
1172
+ | ------------- | ------------------------- | -------- | ------- | --------------------------- |
1173
+ | `open` | `boolean` | | | 제어 모드: 열림 상태 |
1174
+ | `setOpen` | `(open: boolean) => void` | | | 제어 모드: 상태 변경 핸들러 |
1175
+ | `defaultOpen` | `boolean` | | false | 비제어 모드: 초기 열림 상태 |
1176
+
1177
+ **DisclosureTrigger:**
1178
+
1179
+ | Prop | Type | Required | Default | Description |
1180
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1181
+ | `children` | `ReactNode` | Yes | | 트리거 내용 |
1182
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1183
+
1184
+ **DisclosureContent:**
1185
+
1186
+ | Prop | Type | Required | Default | Description |
1187
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1188
+ | `children` | `ReactNode` | Yes | | 접히는 콘텐츠 |
1189
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1190
+
1191
+ ### Dialog
1192
+
1193
+ > 모달 다이얼로그. 9-part anatomy: Root, Disclosure, Panel, Heading, Description, Dismiss, Header, Body, Footer.
1194
+
1195
+ **Import:** `import { DialogRoot } from "@reopt-ai/opt-ui"`
1196
+
1197
+ **DialogRoot:**
1198
+
1199
+ | Prop | Type | Required | Default | Description |
1200
+ | --------- | ------------------------- | -------- | ------- | --------------------------- |
1201
+ | `open` | `boolean` | | | 제어 모드: 열림 상태 |
1202
+ | `setOpen` | `(open: boolean) => void` | | | 제어 모드: 상태 변경 핸들러 |
1203
+
1204
+ **DialogDisclosure:**
1205
+
1206
+ | Prop | Type | Required | Default | Description |
1207
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1208
+ | `children` | `ReactNode` | Yes | | 트리거 버튼 내용 |
1209
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1210
+
1211
+ **DialogPanel:**
1212
+
1213
+ | Prop | Type | Required | Default | Description |
1214
+ | ----------- | ------------------------------------------------ | -------- | ------------------- | ---------------------- |
1215
+ | `children` | `ReactNode` | Yes | | 다이얼로그 콘텐츠 |
1216
+ | `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl" \| "full"` | | "md" | 최대 너비 프리셋 |
1217
+ | `backdrop` | `ReactElement` | | <div> (반투명 배경) | 배경 오버레이 요소 |
1218
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1219
+
1220
+ **DialogHeading:**
1221
+
1222
+ | Prop | Type | Required | Default | Description |
1223
+ | ---------- | ----------- | -------- | ------- | --------------------------------------- |
1224
+ | `children` | `ReactNode` | Yes | | 제목 텍스트 (자동 aria-labelledby 연결) |
1225
+
1226
+ **DialogDescription:**
1227
+
1228
+ | Prop | Type | Required | Default | Description |
1229
+ | ---------- | ----------- | -------- | ------- | ---------------------------------------- |
1230
+ | `children` | `ReactNode` | Yes | | 설명 텍스트 (자동 aria-describedby 연결) |
1231
+
1232
+ **DialogDismiss:**
1233
+
1234
+ | Prop | Type | Required | Default | Description |
1235
+ | ---------- | ----------- | -------- | ------- | -------------- |
1236
+ | `children` | `ReactNode` | | | 닫기 버튼 내용 |
1237
+
1238
+ **DialogHeader:**
1239
+
1240
+ | Prop | Type | Required | Default | Description |
1241
+ | ---------- | ----------- | -------- | ------- | -------------------------------------------- |
1242
+ | `children` | `ReactNode` | Yes | | Heading, Description, Dismiss 등 헤더 콘텐츠 |
1243
+
1244
+ **DialogBody:**
1245
+
1246
+ | Prop | Type | Required | Default | Description |
1247
+ | ---------- | ----------- | -------- | ------- | ------------------------- |
1248
+ | `children` | `ReactNode` | Yes | | 스크롤 가능한 본문 콘텐츠 |
1249
+
1250
+ **DialogFooter:**
1251
+
1252
+ | Prop | Type | Required | Default | Description |
1253
+ | ---------- | ----------- | -------- | ------- | -------------- |
1254
+ | `children` | `ReactNode` | Yes | | 액션 버튼 영역 |
1255
+
1256
+ **Examples:**
1257
+
1258
+ _모달 대화상자:_
1259
+
1260
+ ```tsx
1261
+ import {
1262
+ DialogRoot,
1263
+ DialogDisclosure,
1264
+ DialogPanel,
1265
+ DialogHeading,
1266
+ DialogDismiss,
1267
+ } from "@reopt-ai/opt-ui";
1268
+
1269
+ <DialogRoot>
1270
+ <DialogDisclosure>열기</DialogDisclosure>
1271
+ <DialogPanel size="md">
1272
+ <DialogHeading>확인</DialogHeading>
1273
+ <p>변경 사항을 저장하시겠습니까?</p>
1274
+ <DialogDismiss>닫기</DialogDismiss>
1275
+ </DialogPanel>
1276
+ </DialogRoot>;
1277
+ ```
1278
+
1279
+ ### Drawer
1280
+
1281
+ > 사이드/하단 슬라이드 패널. Dialog 기반 compound 패턴으로 접근성을 보장합니다.
1282
+
1283
+ **Import:** `import { DrawerRoot } from "@reopt-ai/opt-ui"`
1284
+
1285
+ **DrawerRoot:**
1286
+
1287
+ | Prop | Type | Required | Default | Description |
1288
+ | --------- | ------------ | -------- | ------- | ------------------- |
1289
+ | `open` | `boolean` | | | 제어 모드 열림 상태 |
1290
+ | `onClose` | `() => void` | | | 닫힘 콜백 |
1291
+
1292
+ **DrawerPanel:**
1293
+
1294
+ | Prop | Type | Required | Default | Description |
1295
+ | ----------------------- | -------------------------------- | -------- | ------- | -------------------------------------- |
1296
+ | `side` | `"left" \| "right" \| "bottom"` | | "right" | 슬라이드 방향 |
1297
+ | `size` | `"sm" \| "md" \| "lg" \| "full"` | | "md" | 패널 크기 |
1298
+ | `swipeToDismiss` | `boolean` | | true | 가장자리 방향으로 드래그해 닫기 활성화 |
1299
+ | `swipeDismissThreshold` | `number` | | 80 | 닫힘 커밋에 필요한 드래그 거리(px) |
1300
+
1301
+ ### Toolbar
1302
+
1303
+ > 툴바 컨테이너 + 툴팁. 7 exports: ToolbarRoot, ToolbarContainer, ToolbarButton, ToolbarSeparator, TooltipProvider, TooltipAnchor, Tooltip.
1304
+
1305
+ **Import:** `import { ToolbarRoot } from "@reopt-ai/opt-ui"`
1306
+
1307
+ **ToolbarRoot:**
1308
+
1309
+ | Prop | Type | Required | Default | Description |
1310
+ | ----------- | --------- | -------- | ------- | ---------------- |
1311
+ | `focusLoop` | `boolean` | | false | 포커스 순환 여부 |
1312
+
1313
+ **ToolbarContainer:**
1314
+
1315
+ | Prop | Type | Required | Default | Description |
1316
+ | ----------- | ----------- | -------- | ------- | ------------------------------ |
1317
+ | `children` | `ReactNode` | Yes | | ToolbarButton/Separator 요소들 |
1318
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1319
+
1320
+ **ToolbarButton:**
1321
+
1322
+ | Prop | Type | Required | Default | Description |
1323
+ | ----------- | ------------ | -------- | ------- | ---------------------- |
1324
+ | `children` | `ReactNode` | Yes | | 버튼 내용 |
1325
+ | `onClick` | `() => void` | | | 클릭 핸들러 |
1326
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1327
+
1328
+ **ToolbarSeparator:**
1329
+
1330
+ | Prop | Type | Required | Default | Description |
1331
+ | ----------- | -------- | -------- | ------- | ---------------------- |
1332
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1333
+
1334
+ **TooltipProvider:**
1335
+
1336
+ | Prop | Type | Required | Default | Description |
1337
+ | ---- | ---- | -------- | ------- | ----------- |
1338
+
1339
+ **TooltipAnchor:**
1340
+
1341
+ | Prop | Type | Required | Default | Description |
1342
+ | ---------- | -------------- | -------- | ------- | ------------------ |
1343
+ | `render` | `ReactElement` | | | 앵커로 사용할 요소 |
1344
+ | `children` | `ReactNode` | | | 앵커 내용 |
1345
+
1346
+ **Tooltip:**
1347
+
1348
+ | Prop | Type | Required | Default | Description |
1349
+ | ----------- | ----------- | -------- | ------- | ---------------------- |
1350
+ | `children` | `ReactNode` | Yes | | 툴팁 내용 |
1351
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1352
+
1353
+ ### Pagination
1354
+
1355
+ > 페이지 넘김 네비게이션. ellipsis 포함 페이지 번호, compact/count variant, totalPages/cursor pagination, Prev/Next 버튼을 제공합니다.
1356
+
1357
+ **Import:** `import { Pagination } from "@reopt-ai/opt-ui"`
1358
+
1359
+ | Prop | Type | Required | Default | Description |
1360
+ | ----------------- | ------------------------------------------- | -------- | ------------ | ------------------------------------------- |
1361
+ | `totalItems` | `number` | | | 전체 항목 수 |
1362
+ | `totalPages` | `number` | | | 전체 페이지 수. 전체 항목 수를 모를 때 사용 |
1363
+ | `hasMore` | `boolean` | | | cursor pagination에서 다음 페이지 존재 여부 |
1364
+ | `pageSize` | `number` | | | 페이지당 항목 수 |
1365
+ | `currentPage` | `number` | Yes | | 현재 페이지 (1부터 시작) |
1366
+ | `onPageChange` | `(page: number) => void` | Yes | | 페이지 변경 콜백 |
1367
+ | `showPageNumbers` | `boolean` | | true | 페이지 번호 표시 여부 |
1368
+ | `siblingCount` | `number` | | 1 | 현재 페이지 좌우 표시할 페이지 수 |
1369
+ | `variant` | `"pages" \| "count" \| "compact" \| "none"` | | "pages" | 중앙 표시 방식 |
1370
+ | `isDisabled` | `boolean` | | false | 전체 pagination 비활성화 |
1371
+ | `label` | `string` | | "Pagination" | navigation landmark 접근성 라벨 |
1372
+ | `labels` | `PaginationLabels` | | | Prev/Next/page/count 접근성 및 표시 문구 |
1373
+
1374
+ ## Layout
1375
+
1376
+ | Component | Description | Key Exports |
1377
+ | ------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------- |
1378
+ | CompositeZone | Composite 기반 2D 키보드 탐색 컨테이너. 화살표 키로 자식 요소 간 이동 가능. | `CompositeZone` |
1379
+ | PageHeader | 페이지 제목, 설명, 액션 슬롯을 제공하는 레이아웃 헤더 컴포넌트. | `PageHeader` |
1380
+ | ScrollArea | 커스텀 스크롤바를 제공하는 스크롤 컨테이너. 수직/수평/양방향 지원. | `ScrollArea` |
1381
+ | Container | 최대 너비 제약과 중앙 정렬을 제공하는 래퍼. 페이지 콘텐츠 폭 제한용. | `Container` |
1382
+ | Section | 시맨틱 스페이싱 블록. 제목이 있으면 aria-labelledby를 자동 설정합니다. | `Section` |
1383
+ | Resizable | react-resizable-panels 래퍼. 드래그로 크기를 조절할 수 있는 패널 분할 레이아웃. | `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle` |
1384
+ | VirtualList | 가상 스크롤 리스트. 대량 데이터를 윈도잉 기법으로 효율적으로 렌더링합니다. | `VirtualList` |
1385
+
1386
+ ### CompositeZone
1387
+
1388
+ > Composite 기반 2D 키보드 탐색 컨테이너. 화살표 키로 자식 요소 간 이동 가능.
1389
+
1390
+ **Import:** `import { CompositeZone } from "@reopt-ai/opt-ui"`
1391
+
1392
+ | Prop | Type | Required | Default | Description |
1393
+ | ----------- | ----------- | -------- | ------- | --------------------------------- |
1394
+ | `children` | `ReactNode` | Yes | | CompositeRow + CompositeItem 자식 |
1395
+ | `label` | `string` | Yes | | aria-label 값 |
1396
+ | `focusLoop` | `boolean` | | true | 포커스가 끝에서 처음으로 순환 |
1397
+ | `focusWrap` | `boolean` | | true | 행 끝에서 다음 행으로 이동 |
1398
+ | `className` | `string` | | | 컨테이너 CSS 클래스 |
1399
+
1400
+ ### PageHeader
1401
+
1402
+ > 페이지 제목, 설명, 액션 슬롯을 제공하는 레이아웃 헤더 컴포넌트.
1403
+
1404
+ **Import:** `import { PageHeader } from "@reopt-ai/opt-ui"`
1405
+
1406
+ | Prop | Type | Required | Default | Description |
1407
+ | ------------- | ----------- | -------- | ------- | ----------------------------------- |
1408
+ | `title` | `string` | Yes | | 페이지 제목 |
1409
+ | `description` | `string` | | | 제목 아래 설명 텍스트 |
1410
+ | `actions` | `ReactNode` | | | 오른쪽에 표시할 액션 슬롯 (버튼 등) |
1411
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1412
+
1413
+ ### ScrollArea
1414
+
1415
+ > 커스텀 스크롤바를 제공하는 스크롤 컨테이너. 수직/수평/양방향 지원.
1416
+
1417
+ **Import:** `import { ScrollArea } from "@reopt-ai/opt-ui"`
1418
+
1419
+ | Prop | Type | Required | Default | Description |
1420
+ | ------------- | -------------------------------------- | -------- | ---------- | ----------------- |
1421
+ | `children` | `ReactNode` | Yes | | 스크롤할 콘텐츠 |
1422
+ | `orientation` | `"vertical" \| "horizontal" \| "both"` | | "vertical" | 스크롤 방향 |
1423
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1424
+
1425
+ ### Container
1426
+
1427
+ > 최대 너비 제약과 중앙 정렬을 제공하는 래퍼. 페이지 콘텐츠 폭 제한용.
1428
+
1429
+ **Import:** `import { Container } from "@reopt-ai/opt-ui"`
1430
+
1431
+ | Prop | Type | Required | Default | Description |
1432
+ | ----------- | ------------------------------------------------- | -------- | ------- | ------------------------ |
1433
+ | `children` | `ReactNode` | Yes | | 래핑할 콘텐츠 |
1434
+ | `maxWidth` | `"sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "full"` | | "xl" | 최대 너비 크기 |
1435
+ | `center` | `boolean` | | true | 중앙 정렬 여부 (mx-auto) |
1436
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1437
+
1438
+ ### Section
1439
+
1440
+ > 시맨틱 스페이싱 블록. 제목이 있으면 aria-labelledby를 자동 설정합니다.
1441
+
1442
+ **Import:** `import { Section } from "@reopt-ai/opt-ui"`
1443
+
1444
+ | Prop | Type | Required | Default | Description |
1445
+ | ------------- | ----------- | -------- | ------- | --------------------- |
1446
+ | `children` | `ReactNode` | Yes | | 섹션 콘텐츠 |
1447
+ | `title` | `string` | | | 섹션 제목 (h3) |
1448
+ | `description` | `string` | | | 제목 아래 설명 텍스트 |
1449
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1450
+
1451
+ ### Resizable
1452
+
1453
+ > react-resizable-panels 래퍼. 드래그로 크기를 조절할 수 있는 패널 분할 레이아웃.
1454
+
1455
+ **Import:** `import { ResizablePanelGroup } from "@reopt-ai/opt-ui"`
1456
+
1457
+ **ResizablePanelGroup:**
1458
+
1459
+ | Prop | Type | Required | Default | Description |
1460
+ | ------------ | ---------------------------- | -------- | ------- | ---------------------------------------- |
1461
+ | `direction` | `"horizontal" \| "vertical"` | Yes | | 패널 분할 방향 |
1462
+ | `children` | `ReactNode` | Yes | | ResizablePanel + ResizableHandle 조합 |
1463
+ | `autoSaveId` | `string` | | | 로컬 스토리지에 크기 상태 저장 (고유 ID) |
1464
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1465
+
1466
+ **ResizablePanel:**
1467
+
1468
+ | Prop | Type | Required | Default | Description |
1469
+ | ------------- | ----------- | -------- | ------- | ----------------------------- |
1470
+ | `defaultSize` | `number` | | | 초기 크기 (퍼센트, 0-100) |
1471
+ | `minSize` | `number` | | | 최소 크기 (퍼센트) |
1472
+ | `maxSize` | `number` | | | 최대 크기 (퍼센트) |
1473
+ | `collapsible` | `boolean` | | false | 최소 크기 이하 시 접히기 허용 |
1474
+ | `children` | `ReactNode` | Yes | | 패널 콘텐츠 |
1475
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1476
+
1477
+ **ResizableHandle:**
1478
+
1479
+ | Prop | Type | Required | Default | Description |
1480
+ | ------------ | --------- | -------- | ------- | ----------------------- |
1481
+ | `withHandle` | `boolean` | | false | 시각적 드래그 핸들 표시 |
1482
+ | `className` | `string` | | | 커스텀 CSS 클래스 |
1483
+
1484
+ ### VirtualList
1485
+
1486
+ > 가상 스크롤 리스트. 대량 데이터를 윈도잉 기법으로 효율적으로 렌더링합니다.
1487
+
1488
+ **Import:** `import { VirtualList } from "@reopt-ai/opt-ui"`
1489
+
1490
+ | Prop | Type | Required | Default | Description |
1491
+ | ------------ | --------------------------------------- | -------- | ------- | --------------------------------- |
1492
+ | `items` | `T[]` | Yes | | 렌더링할 항목 배열 |
1493
+ | `renderItem` | `(item: T, index: number) => ReactNode` | Yes | | 항목 렌더 함수 |
1494
+ | `itemHeight` | `number` | Yes | | 각 항목의 고정 높이(px) |
1495
+ | `overscan` | `number` | | 3 | 뷰포트 밖에 미리 렌더링할 항목 수 |
1496
+
1497
+ ## Builder
1498
+
1499
+ | Component | Description | Key Exports |
1500
+ | ---------------- | ------------------------------------------------------------------------- | ------------------ |
1501
+ | StepBuilder | 순서가 있는 단계를 추가, 제거, 재정렬할 수 있는 빌더 컴포넌트. | `StepBuilder` |
1502
+ | ConditionBuilder | AND/OR 로직으로 조건 그룹을 구성하는 빌더. 속성, 연산자, 값을 조합합니다. | `ConditionBuilder` |
1503
+
1504
+ ### StepBuilder
1505
+
1506
+ > 순서가 있는 단계를 추가, 제거, 재정렬할 수 있는 빌더 컴포넌트.
1507
+
1508
+ **Import:** `import { StepBuilder } from "@reopt-ai/opt-ui"`
1509
+
1510
+ | Prop | Type | Required | Default | Description |
1511
+ | ---------- | ---------------------------- | -------- | ------- | --------------------------------------- |
1512
+ | `steps` | `StepDef[]` | Yes | | 단계 배열 ({ id, label, description? }) |
1513
+ | `onChange` | `(steps: StepDef[]) => void` | Yes | | 단계 변경 핸들러 |
1514
+ | `label` | `string` | | | 빌더 레이블 |
1515
+ | `maxSteps` | `number` | | | 최대 단계 수 |
1516
+
1517
+ ### ConditionBuilder
1518
+
1519
+ > AND/OR 로직으로 조건 그룹을 구성하는 빌더. 속성, 연산자, 값을 조합합니다.
1520
+
1521
+ **Import:** `import { ConditionBuilder } from "@reopt-ai/opt-ui"`
1522
+
1523
+ | Prop | Type | Required | Default | Description |
1524
+ | --------------------- | --------------------------------------- | -------- | ------------------ | -------------------------------------- |
1525
+ | `groups` | `ConditionGroup[]` | Yes | | 조건 그룹 배열 |
1526
+ | `onChange` | `(groups: ConditionGroup[]) => void` | Yes | | 그룹 변경 핸들러 |
1527
+ | `properties` | `string[]` | | | 선택 가능한 속성 목록 (기본 10개 제공) |
1528
+ | `label` | `string` | | | 빌더 레이블 |
1529
+ | `nested` | `boolean` | | false | 중첩 AND/OR 그룹 활성화 |
1530
+ | `maxDepth` | `number` | | 3 | 최대 중첩 깊이 |
1531
+ | `conditionTypes` | `Record<string, ConditionTypeRenderer>` | | | 이질적 조건 타입 렌더러 레지스트리 |
1532
+ | `onPreview` | `(groups: ConditionGroup[]) => void` | | | 조건 변경 시 미리보기 콜백 |
1533
+ | `previewCount` | `number` | | | 매치 카운트 표시 (toLocaleString 포맷) |
1534
+ | `addNestedGroupLabel` | `string` | | "Add nested group" | 중첩 그룹 추가 버튼 텍스트 |
1535
+
1536
+ ## Other
1537
+
1538
+ | Component | Description | Key Exports |
1539
+ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
1540
+ | Text | 본문, 레이블, 보조 설명, 코드에 일관된 의미 기반 타이포그래피를 적용합니다. | `Text` |
1541
+ | Heading | 문서의 의미 계층과 시각적 타이포그래피 계층을 독립적으로 제어합니다. | `Heading` |
1542
+ | Separator | 콘텐츠를 구분하는 가로/세로 디바이더. 라벨을 넣으면 가운데 정렬 구분선이 된다. | `Separator` |
1543
+ | OtpField | 일회용 코드(OTP)/PIN 입력. 셀 단위 입력, 붙여넣기 분배, 자동 포커스 이동, 백스페이스 네비게이션과 폼 이름/오류 연결을 지원합니다. | `OtpField` |
1544
+ | Toggle | 눌림(pressed) 두 상태 버튼. 단독 사용하거나 ToggleGroup으로 묶어 단일/다중 선택을 만든다. | `Toggle`, `ToggleGroup` |
1545
+ | Accordion | 수직으로 쌓인 펼침/접힘 패널. single(한 개)/multiple(여러 개) 모드와 collapsible 지원. | `AccordionRoot`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` |
1546
+ | Meter | 범위 내 스칼라 측정값 게이지 (디스크 사용량·점수·용량 등). role=meter로 작업 진행률(Progress)과 구분된다. | `Meter` |
1547
+ | BlockLayout | Block 루트 레이아웃. flex + 기본 section spacing 적용, loading prop으로 LoadingOverlay 자동 래핑. | `BlockLayout` |
1548
+ | createBlock | Block 팩토리 함수. BlockLayout 래핑을 구조적으로 강제하여 loading/className 처리를 자동화합니다. | `createBlock` |
1549
+ | StarRating | 1~N점 별점을 선택하는 인터랙티브 레이팅 입력. 읽기 전용 표시도 지원. | `StarRating` |
1550
+ | ErrorBoundary | React 에러 바운더리. 자식 컴포넌트 렌더 에러를 잡아 fallback UI를 표시합니다. | `ErrorBoundary` |
1551
+ | Command | cmdk 스타일 명령 anatomy. 자동 필터링 + 키보드 내비게이션. 인라인 또는 CommandDialog로 사용. | `Command`, `CommandDialog`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandSeparator`, `CommandShortcut` |
1552
+ | ShaderSurface | ogl 기반 애니메이션 WebGL 셰이더 배경. 럭셔리 프리셋(메탈릭/실크/유리 등) + 색·파라미터로 배경·카드 배경을 만든다. 정지 프레임·PNG 캡처·WebGL 폴백 지원. | `ShaderSurface` |
1553
+
1554
+ ### Text
1555
+
1556
+ > 본문, 레이블, 보조 설명, 코드에 일관된 의미 기반 타이포그래피를 적용합니다.
1557
+
1558
+ **Import:** `import { Text } from "@reopt-ai/opt-ui"`
1559
+
1560
+ | Prop | Type | Required | Default | Description |
1561
+ | ---------- | ----------------------------------------------------- | -------- | ------- | --------------------- |
1562
+ | `as` | `"span" \| "p" \| "div" \| "label" \| "code"` | | | 렌더링할 HTML 요소 |
1563
+ | `variant` | `"body" \| "label" \| "supporting" \| "code"` | | | 시각적 텍스트 역할 |
1564
+ | `tone` | `"primary" \| "secondary" \| "tertiary" \| "inherit"` | | | 의미 기반 텍스트 색상 |
1565
+ | `truncate` | `boolean` | | | 한 줄 말줄임 적용 |
1566
+ | `children` | `ReactNode` | | | 텍스트 콘텐츠 |
1567
+
1568
+ ### Heading
1569
+
1570
+ > 문서의 의미 계층과 시각적 타이포그래피 계층을 독립적으로 제어합니다.
1571
+
1572
+ **Import:** `import { Heading } from "@reopt-ai/opt-ui"`
1573
+
1574
+ | Prop | Type | Required | Default | Description |
1575
+ | ---------- | ------------------------------------------------------------------------ | -------- | ------- | ------------------------ |
1576
+ | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | | | 의미 기반 HTML 제목 레벨 |
1577
+ | `variant` | `"heading-1" … "heading-6" \| "display-1" \| "display-2" \| "display-3"` | | | 시각적 제목 역할 |
1578
+ | `children` | `ReactNode` | | | 제목 콘텐츠 |
1579
+
1580
+ ### Separator
1581
+
1582
+ > 콘텐츠를 구분하는 가로/세로 디바이더. 라벨을 넣으면 가운데 정렬 구분선이 된다.
1583
+
1584
+ **Import:** `import { Separator } from "@reopt-ai/opt-ui"`
1585
+
1586
+ | Prop | Type | Required | Default | Description |
1587
+ | ------------- | ---------------------------- | -------- | ------- | ------------------------------------- |
1588
+ | `orientation` | `"horizontal" \| "vertical"` | | | 구분선 방향 (기본 horizontal) |
1589
+ | `decorative` | `boolean` | | | 접근성 트리에서 숨김 (순수 시각 구분) |
1590
+ | `children` | `ReactNode` | | | 가운데 라벨 (horizontal 전용) |
1591
+ | `className` | `string` | | | 추가 CSS 클래스 |
1592
+
1593
+ ### OtpField
1594
+
1595
+ > 일회용 코드(OTP)/PIN 입력. 셀 단위 입력, 붙여넣기 분배, 자동 포커스 이동, 백스페이스 네비게이션과 폼 이름/오류 연결을 지원합니다.
1596
+
1597
+ **Import:** `import { OtpField } from "@reopt-ai/opt-ui"`
1598
+
1599
+ | Prop | Type | Required | Default | Description |
1600
+ | -------------- | ----------------------------- | -------- | ------- | ------------------------------------------- |
1601
+ | `length` | `number` | | | 셀 개수 (기본 6) |
1602
+ | `value` | `string` | | | 제어 모드 값 |
1603
+ | `defaultValue` | `string` | | | 비제어 초기값 |
1604
+ | `onChange` | `(value: string) => void` | | | 값 변경 콜백 |
1605
+ | `onComplete` | `(value: string) => void` | | | 모든 셀이 채워지면 1회 호출 |
1606
+ | `type` | `"numeric" \| "alphanumeric"` | | | 허용 문자 (기본 numeric) |
1607
+ | `mask` | `boolean` | | | 채워진 셀을 점(•)으로 마스킹 (PIN 용) |
1608
+ | `disabled` | `boolean` | | | 비활성화 |
1609
+ | `name` | `string` | | | 폼 필드명. 전체 값을 담은 hidden input 렌더 |
1610
+ | `label` | `string` | | | 필드 레이블 |
1611
+ | `error` | `string` | | | 에러 메시지 |
1612
+ | `hint` | `string` | | | 도움말 텍스트 |
1613
+
1614
+ ### Toggle
1615
+
1616
+ > 눌림(pressed) 두 상태 버튼. 단독 사용하거나 ToggleGroup으로 묶어 단일/다중 선택을 만든다.
1617
+
1618
+ **Import:** `import { Toggle } from "@reopt-ai/opt-ui"`
1619
+
1620
+ **Toggle:**
1621
+
1622
+ | Prop | Type | Required | Default | Description |
1623
+ | ----------------- | ---------------------------- | -------- | ------- | ---------------------------------------- |
1624
+ | `pressed` | `boolean` | | | 제어 모드: 눌림 상태 (단독 사용) |
1625
+ | `defaultPressed` | `boolean` | | | 비제어 초기 눌림 상태 (단독 사용) |
1626
+ | `onPressedChange` | `(pressed: boolean) => void` | | | 눌림 상태 변경 콜백 (단독 사용) |
1627
+ | `value` | `string` | | | ToggleGroup 내에서 이 토글을 식별하는 값 |
1628
+ | `size` | `"sm" \| "md"` | | | 크기 (기본 md) |
1629
+ | `disabled` | `boolean` | | | 비활성화 |
1630
+ | `children` | `ReactNode` | | | 토글 콘텐츠 |
1631
+
1632
+ **ToggleGroup:**
1633
+
1634
+ | Prop | Type | Required | Default | Description |
1635
+ | ---------------- | ------------------------------------- | -------- | ------- | ------------------------------------------------- |
1636
+ | `value` | `string \| string[]` | | | 선택 값. 단일은 string, toggleMultiple은 string[] |
1637
+ | `defaultValue` | `string \| string[]` | | | 비제어 초기 선택 값 |
1638
+ | `onValueChange` | `(value: string \| string[]) => void` | | | 선택 변경 콜백 |
1639
+ | `toggleMultiple` | `boolean` | | | 동시에 여러 개 눌림 허용 |
1640
+ | `orientation` | `"horizontal" \| "vertical"` | | | 배치 방향 (기본 horizontal) |
1641
+ | `disabled` | `boolean` | | | 그룹 전체 비활성화 |
1642
+ | `children` | `ReactNode` | | | Toggle 항목들 |
1643
+
1644
+ ### Accordion
1645
+
1646
+ > 수직으로 쌓인 펼침/접힘 패널. single(한 개)/multiple(여러 개) 모드와 collapsible 지원.
1647
+
1648
+ **Import:** `import { AccordionRoot } from "@reopt-ai/opt-ui"`
1649
+
1650
+ **AccordionRoot:**
1651
+
1652
+ | Prop | Type | Required | Default | Description |
1653
+ | --------------- | --------------------------------------------- | -------- | ------- | ------------------------------------------------------------- |
1654
+ | `type` | `"single" \| "multiple"` | | | 한 개만 열림(single) 또는 여러 개 열림(multiple). 기본 single |
1655
+ | `value` | `string \| string[] \| null` | | | 열린 항목 값. single은 string\|null, multiple은 string[] |
1656
+ | `defaultValue` | `string \| string[] \| null` | | | 비제어 초기 열림 값 |
1657
+ | `onValueChange` | `(value: string \| string[] \| null) => void` | | | 열림 변경 콜백 |
1658
+ | `collapsible` | `boolean` | | | single 모드에서 열린 패널을 다시 닫을 수 있게 허용 |
1659
+ | `disabled` | `boolean` | | | 전체 비활성화 |
1660
+ | `animated` | `boolean` | | | 콘텐츠 enter/leave 애니메이션 (기본 true) |
1661
+ | `children` | `ReactNode` | | | AccordionItem 목록 |
1662
+
1663
+ **AccordionItem:**
1664
+
1665
+ | Prop | Type | Required | Default | Description |
1666
+ | ---------- | ----------- | -------- | ------- | ------------------ |
1667
+ | `value` | `string` | Yes | | 항목 식별 값 |
1668
+ | `disabled` | `boolean` | | | 이 항목만 비활성화 |
1669
+ | `children` | `ReactNode` | | | Trigger + Content |
1670
+
1671
+ **AccordionTrigger:**
1672
+
1673
+ | Prop | Type | Required | Default | Description |
1674
+ | ---------- | ----------- | -------- | ------- | ----------------------------------- |
1675
+ | `children` | `ReactNode` | Yes | | 헤더 라벨 (쉐브론 아이콘 자동 추가) |
1676
+
1677
+ **AccordionContent:**
1678
+
1679
+ | Prop | Type | Required | Default | Description |
1680
+ | ---------- | ----------- | -------- | ------- | ------------------ |
1681
+ | `children` | `ReactNode` | Yes | | 펼쳐지는 패널 본문 |
1682
+
1683
+ ### Meter
1684
+
1685
+ > 범위 내 스칼라 측정값 게이지 (디스크 사용량·점수·용량 등). role=meter로 작업 진행률(Progress)과 구분된다.
1686
+
1687
+ **Import:** `import { Meter } from "@reopt-ai/opt-ui"`
1688
+
1689
+ | Prop | Type | Required | Default | Description |
1690
+ | ----------- | ------------------------------------------------ | -------- | --------- | ------------- |
1691
+ | `value` | `number` | Yes | | 현재 값 |
1692
+ | `min` | `number` | | 0 | 최소 값 |
1693
+ | `max` | `number` | | 100 | 최대 값 |
1694
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 게이지 높이 |
1695
+ | `variant` | `"default" \| "success" \| "warning" \| "error"` | | "default" | 색상 변형 |
1696
+ | `showValue` | `boolean` | | false | 퍼센트 표시 |
1697
+ | `label` | `string` | | | 레이블 텍스트 |
1698
+
1699
+ ### BlockLayout
1700
+
1701
+ > Block 루트 레이아웃. flex + 기본 section spacing 적용, loading prop으로 LoadingOverlay 자동 래핑.
1702
+
1703
+ **Import:** `import { BlockLayout } from "@reopt-ai/opt-ui"`
1704
+
1705
+ | Prop | Type | Required | Default | Description |
1706
+ | -------------- | --------------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------ |
1707
+ | `children` | `ReactNode` | Yes | | Block 콘텐츠 |
1708
+ | `loading` | `boolean` | | | 로딩 상태. 설정 시 LoadingOverlay로 자동 래핑 |
1709
+ | `loadingLabel` | `string` | | | LoadingOverlay 접근성 레이블 |
1710
+ | `spacing` | `"section" \| "group" \| "element" \| "none"` | | "section" | Block 최상위 자식 사이 간격. 데이터 밀도가 높은 Block는 group/element로 낮출 수 있음 |
1711
+ | `className` | `string` | | | 기본 스타일 오버라이드 |
1712
+
1713
+ **Examples:**
1714
+
1715
+ _Block 루트:_
1716
+
1717
+ ```tsx
1718
+ import { BlockLayout, PageHeader } from "@reopt-ai/opt-ui";
1719
+
1720
+ <BlockLayout loading={isLoading} spacing="section">
1721
+ <PageHeader title="대시보드" description="프로젝트 현황" />
1722
+ <section>콘텐츠 영역</section>
1723
+ </BlockLayout>;
1724
+ ```
1725
+
1726
+ ### createBlock
1727
+
1728
+ > Block 팩토리 함수. BlockLayout 래핑을 구조적으로 강제하여 loading/className 처리를 자동화합니다.
1729
+
1730
+ **Import:** `import { createBlock } from "@reopt-ai/opt-ui"`
1731
+
1732
+ | Prop | Type | Required | Default | Description |
1733
+ | ------------------------- | --------------------------------------------------------- | -------- | ------- | ----------------------------------- |
1734
+ | `name` | `string` | Yes | | 컴포넌트 displayName |
1735
+ | `render` | `(props: Omit<P, 'loading' \| 'className'>) => ReactNode` | Yes | | 콘텐츠를 반환하는 렌더 함수 |
1736
+ | `options.layoutClassName` | `string` | | | BlockLayout에 전달할 기본 className |
1737
+
1738
+ **Examples:**
1739
+
1740
+ _Block 팩토리:_
1741
+
1742
+ ```tsx
1743
+ import { createBlock, PageHeader } from "@reopt-ai/opt-ui";
1744
+
1745
+ interface MyDashboardProps {
1746
+ title: string;
1747
+ count: number;
1748
+ }
1749
+
1750
+ const MyDashboard = createBlock<MyDashboardProps>(
1751
+ "MyDashboard",
1752
+ ({ title, count }) => (
1753
+ <>
1754
+ <PageHeader title={title} />
1755
+ <p>항목 수: {count}</p>
1756
+ </>
1757
+ ),
1758
+ );
1759
+
1760
+ // loading, className은 자동 처리
1761
+ <MyDashboard title="개요" count={42} loading={false} />;
1762
+ ```
1763
+
1764
+ ### StarRating
1765
+
1766
+ > 1~N점 별점을 선택하는 인터랙티브 레이팅 입력. 읽기 전용 표시도 지원.
1767
+
1768
+ **Import:** `import { StarRating } from "@reopt-ai/opt-ui"`
1769
+
1770
+ | Prop | Type | Required | Default | Description |
1771
+ | ---------- | ------------------------- | -------- | ------- | ---------------- |
1772
+ | `value` | `number` | | | 현재 선택된 별점 |
1773
+ | `onChange` | `(value: number) => void` | | | 별점 변경 콜백 |
1774
+ | `max` | `number` | | 5 | 최대 별 개수 |
1775
+ | `readOnly` | `boolean` | | false | 읽기 전용 모드 |
1776
+ | `size` | `"sm" \| "md" \| "lg"` | | "md" | 별 아이콘 크기 |
1777
+
1778
+ ### ErrorBoundary
1779
+
1780
+ > React 에러 바운더리. 자식 컴포넌트 렌더 에러를 잡아 fallback UI를 표시합니다.
1781
+
1782
+ **Import:** `import { ErrorBoundary } from "@reopt-ai/opt-ui"`
1783
+
1784
+ | Prop | Type | Required | Default | Description |
1785
+ | ---------- | -------------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------- |
1786
+ | `children` | `ReactNode` | Yes | | 에러를 감시할 자식 컴포넌트 |
1787
+ | `fallback` | `ReactNode \| ((error: Error) => ReactNode)` | | | 에러 발생 시 표시할 UI. 함수 전달 시 에러 객체를 인자로 받음. 미지정 시 Alert error 표시 |
1788
+ | `onError` | `(error: Error, info: ErrorInfo) => void` | | | 에러 발생 시 호출되는 콜백 (에러 리포팅용) |
1789
+
1790
+ ### Command
1791
+
1792
+ > cmdk 스타일 명령 anatomy. 자동 필터링 + 키보드 내비게이션. 인라인 또는 CommandDialog로 사용.
1793
+
1794
+ **Import:** `import { Command } from "@reopt-ai/opt-ui"`
1795
+
1796
+ **Command:**
1797
+
1798
+ | Prop | Type | Required | Default | Description |
1799
+ | -------------- | ---------------------------------------------------------------- | -------- | ------- | --------------------------------------------------- |
1800
+ | `shouldFilter` | `boolean` | | | 자동 필터링 여부 (기본 true) |
1801
+ | `filter` | `(value: string, search: string, keywords: string[]) => boolean` | | | 커스텀 필터 함수 (기본: case-insensitive substring) |
1802
+
1803
+ **CommandInput:**
1804
+
1805
+ | Prop | Type | Required | Default | Description |
1806
+ | ------------------ | ----------- | -------- | ------- | ----------------------------------- |
1807
+ | `trailing` | `ReactNode` | | | 검색 입력 우측에 표시할 보조 콘텐츠 |
1808
+ | `wrapperClassName` | `string` | | | 입력 wrapper className |
1809
+
1810
+ **CommandList:**
1811
+
1812
+ | Prop | Type | Required | Default | Description |
1813
+ | ------------ | -------- | -------- | ---------- | ------------------- |
1814
+ | `aria-label` | `string` | | "Commands" | listbox 접근성 라벨 |
1815
+
1816
+ **CommandItem:**
1817
+
1818
+ | Prop | Type | Required | Default | Description |
1819
+ | ---------- | ------------------------- | -------- | ------- | ---------------------------------------- |
1820
+ | `value` | `string` | Yes | | 이 항목의 검색 가능 값 (고유 식별자) |
1821
+ | `keywords` | `string[]` | | | value에 더해 검색어와 비교할 추가 키워드 |
1822
+ | `onSelect` | `(value: string) => void` | | | Enter/클릭 시 호출 |
1823
+
1824
+ ### ShaderSurface
1825
+
1826
+ > ogl 기반 애니메이션 WebGL 셰이더 배경. 럭셔리 프리셋(메탈릭/실크/유리 등) + 색·파라미터로 배경·카드 배경을 만든다. 정지 프레임·PNG 캡처·WebGL 폴백 지원.
1827
+
1828
+ **Import:** `import { ShaderSurface } from "@reopt-ai/opt-ui"`
1829
+
1830
+ | Prop | Type | Required | Default | Description |
1831
+ | ------------ | ---------------- | -------- | ------- | ------------------------------------------ |
1832
+ | `preset` | `ShaderPresetId` | Yes | | 렌더할 셰이더 프리셋 id |
1833
+ | `colors` | `string[]` | | | hex 색상 배열 (미지정 시 프리셋 기본 색) |
1834
+ | `scale` | `number` | | | 패턴 스케일 |
1835
+ | `speed` | `number` | | | 애니메이션 속도 |
1836
+ | `distortion` | `number` | | | 왜곡 강도 |
1837
+ | `grain` | `number` | | | 필름 그레인 양 |
1838
+ | `contrast` | `number` | | | 하이라이트 대비 |
1839
+ | `angle` | `number` | | | 회전 각도(도) |
1840
+ | `paused` | `boolean` | | | true면 단일 프레임만 렌더 (카드 배경 등) |
1841
+ | `dpr` | `number` | | | device pixel ratio 상한 (기본 min(dpr, 2)) |