@ceed/cds 1.29.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/Overview.md +5 -5
  2. package/dist/components/Calendar/utils/index.d.ts +1 -0
  3. package/dist/components/DatePicker/DatePicker.d.ts +4 -0
  4. package/dist/components/DateRangePicker/DateRangePicker.d.ts +4 -0
  5. package/dist/components/MonthRangePicker/MonthRangePicker.d.ts +8 -0
  6. package/dist/components/data-display/Avatar.md +110 -69
  7. package/dist/components/data-display/Badge.md +91 -39
  8. package/dist/components/data-display/Chip.md +49 -20
  9. package/dist/components/data-display/DataTable.md +93 -0
  10. package/dist/components/data-display/InfoSign.md +12 -0
  11. package/dist/components/data-display/Table.md +72 -55
  12. package/dist/components/data-display/Tooltip.md +40 -40
  13. package/dist/components/data-display/Typography.md +53 -70
  14. package/dist/components/feedback/Alert.md +88 -72
  15. package/dist/components/feedback/CircularProgress.md +17 -0
  16. package/dist/components/feedback/Skeleton.md +17 -0
  17. package/dist/components/inputs/Button.md +94 -68
  18. package/dist/components/inputs/ButtonGroup.md +17 -0
  19. package/dist/components/inputs/Calendar.md +118 -457
  20. package/dist/components/inputs/Checkbox.md +185 -430
  21. package/dist/components/inputs/CurrencyInput.md +22 -0
  22. package/dist/components/inputs/DatePicker.md +59 -5
  23. package/dist/components/inputs/DateRangePicker.md +46 -5
  24. package/dist/components/inputs/FilterableCheckboxGroup.md +20 -3
  25. package/dist/components/inputs/FormControl.md +32 -2
  26. package/dist/components/inputs/IconButton.md +18 -0
  27. package/dist/components/inputs/Input.md +198 -136
  28. package/dist/components/inputs/MonthPicker.md +59 -5
  29. package/dist/components/inputs/MonthRangePicker.md +44 -5
  30. package/dist/components/inputs/PercentageInput.md +25 -0
  31. package/dist/components/inputs/RadioButton.md +23 -0
  32. package/dist/components/inputs/RadioList.md +20 -1
  33. package/dist/components/inputs/RadioTileGroup.md +37 -3
  34. package/dist/components/inputs/Select.md +56 -0
  35. package/dist/components/inputs/Slider.md +23 -0
  36. package/dist/components/inputs/Switch.md +20 -0
  37. package/dist/components/inputs/Textarea.md +32 -8
  38. package/dist/components/inputs/Uploader/Uploader.md +21 -0
  39. package/dist/components/layout/Box.md +52 -41
  40. package/dist/components/layout/Grid.md +87 -81
  41. package/dist/components/layout/Stack.md +88 -68
  42. package/dist/components/navigation/Breadcrumbs.md +57 -46
  43. package/dist/components/navigation/Drawer.md +17 -0
  44. package/dist/components/navigation/Dropdown.md +13 -0
  45. package/dist/components/navigation/IconMenuButton.md +17 -0
  46. package/dist/components/navigation/InsetDrawer.md +130 -292
  47. package/dist/components/navigation/Link.md +78 -0
  48. package/dist/components/navigation/Menu.md +17 -0
  49. package/dist/components/navigation/MenuButton.md +18 -0
  50. package/dist/components/navigation/Pagination.md +13 -0
  51. package/dist/components/navigation/Stepper.md +15 -0
  52. package/dist/components/navigation/Tabs.md +27 -0
  53. package/dist/components/surfaces/Accordions.md +804 -49
  54. package/dist/components/surfaces/Card.md +173 -97
  55. package/dist/components/surfaces/Divider.md +246 -82
  56. package/dist/components/surfaces/Sheet.md +15 -0
  57. package/dist/index.browser.js +4 -4
  58. package/dist/index.browser.js.map +3 -3
  59. package/dist/index.cjs +99 -39
  60. package/dist/index.js +99 -39
  61. package/framer/index.js +1 -1
  62. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Checkbox 컴포넌트는 사용자가 여러 옵션 중에서 하나 또는 여러 개를 선택할 있는 입력 요소입니다. Joy UI Checkbox 기반으로 하며, Framer Motion 애니메이션 지원이 추가되었습니다. 설정 화면, 폼, 필터링, 다중 선택 목록 등에서 널리 사용됩니다.
5
+ Checkbox is an input element that allows users to select one or more options from a set. Built on Joy UI's Checkbox with Framer Motion animation support, it is used in settings screens, forms, filters, multi-select lists, and agreement flows. It supports indeterminate state for "select all" patterns, decorators for custom content, and overlay mode for clickable card patterns.
6
6
 
7
7
  ```tsx
8
8
  <Checkbox />
@@ -25,7 +25,13 @@ import { Checkbox } from '@ceed/cds';
25
25
  function MyComponent() {
26
26
  const [checked, setChecked] = useState(false);
27
27
 
28
- return <Checkbox label="동의합니다" checked={checked} onChange={(e) => setChecked(e.target.checked)} />;
28
+ return (
29
+ <Checkbox
30
+ label="I agree"
31
+ checked={checked}
32
+ onChange={(e) => setChecked(e.target.checked)}
33
+ />
34
+ );
29
35
  }
30
36
  ```
31
37
 
@@ -33,26 +39,95 @@ function MyComponent() {
33
39
 
34
40
  ### Basic Usage
35
41
 
36
- 가장 기본적인 Checkbox 사용법입니다.
42
+ A simple checkbox with a label.
37
43
 
38
44
  ```tsx
39
45
  <Checkbox label="Label" />
40
46
  ```
41
47
 
42
- ### With Labels
48
+ ### Variants
43
49
 
44
- 체크박스에 라벨을 추가할 있습니다.
50
+ Four visual styles are available: `solid` (default when checked), `soft`, `outlined`, and `plain`.
45
51
 
46
52
  ```tsx
47
- <Checkbox />
53
+ <Stack spacing={2}>
54
+ <Checkbox label="Solid" variant="solid" defaultChecked />
55
+ <Checkbox label="Soft" variant="soft" defaultChecked />
56
+ <Checkbox label="Outlined" variant="outlined" defaultChecked />
57
+ <Checkbox label="Plain" variant="plain" defaultChecked />
58
+ </Stack>
59
+ ```
60
+
61
+ ### Sizes
62
+
63
+ Three sizes are available: `sm`, `md` (default), and `lg`.
64
+
65
+ ```tsx
66
+ <Stack direction="row" spacing={2} alignItems="center">
67
+ <Checkbox label="Small" size="sm" defaultChecked />
68
+ <Checkbox label="Medium" size="md" defaultChecked />
69
+ <Checkbox label="Large" size="lg" defaultChecked />
70
+ </Stack>
71
+ ```
72
+
73
+ ### Colors
74
+
75
+ Apply semantic colors to communicate meaning.
76
+
77
+ ```tsx
78
+ <Stack spacing={2}>
79
+ <Checkbox label="Primary" color="primary" defaultChecked />
80
+ <Checkbox label="Success" color="success" defaultChecked />
81
+ <Checkbox label="Warning" color="warning" defaultChecked />
82
+ <Checkbox label="Danger" color="danger" defaultChecked />
83
+ <Checkbox label="Neutral" color="neutral" defaultChecked />
84
+ </Stack>
85
+ ```
86
+
87
+ ## States
88
+
89
+ ### Indeterminate
90
+
91
+ Represents a partially selected state, commonly used in "select all" patterns where some but not all children are selected.
92
+
93
+ ```tsx
94
+ <Checkbox
95
+ label="Partially selected"
96
+ indeterminate
97
+ onChange={handleChange}
98
+ />
99
+ ```
100
+
101
+ ### Disabled
102
+
103
+ A disabled checkbox cannot be interacted with.
104
+
105
+ ```tsx
106
+ <Stack spacing={1}>
107
+ <Checkbox label="Disabled (unchecked)" disabled />
108
+ <Checkbox label="Disabled (checked)" disabled defaultChecked />
109
+ <Checkbox label="Disabled (indeterminate)" disabled indeterminate />
110
+ </Stack>
111
+ ```
112
+
113
+ ### Error
114
+
115
+ Use with `FormControl` to show validation errors.
116
+
117
+ ```tsx
118
+ <FormControl error>
119
+ <Checkbox label="Required agreement" required />
120
+ <FormHelperText>
121
+ <InfoOutlinedIcon />
122
+ This field is required.
123
+ </FormHelperText>
124
+ </FormControl>
48
125
  ```
49
126
 
50
127
  ## Common Use Cases
51
128
 
52
129
  ### Terms and Conditions
53
130
 
54
- 이용약관 동의에 체크박스를 사용하는 예제입니다.
55
-
56
131
  ```tsx
57
132
  function TermsForm() {
58
133
  const [agreements, setAgreements] = useState({
@@ -61,23 +136,22 @@ function TermsForm() {
61
136
  marketing: false,
62
137
  });
63
138
 
64
- const handleChange = (key: keyof typeof agreements) => (event: React.ChangeEvent<HTMLInputElement>) => {
65
- setAgreements((prev) => ({
66
- ...prev,
67
- [key]: event.target.checked,
68
- }));
139
+ const handleChange = (key: keyof typeof agreements) => (
140
+ event: React.ChangeEvent<HTMLInputElement>
141
+ ) => {
142
+ setAgreements(prev => ({ ...prev, [key]: event.target.checked }));
69
143
  };
70
144
 
71
145
  return (
72
146
  <Stack spacing={2}>
73
- <Typography level="title-md">약관 동의</Typography>
147
+ <Typography level="title-md">Agreements</Typography>
74
148
 
75
149
  <Checkbox
76
150
  label={
77
151
  <Typography>
78
- <strong>(필수)</strong> 이용약관에 동의합니다.
152
+ <strong>(Required)</strong> I agree to the Terms of Service.
79
153
  <Link href="/terms" target="_blank" sx={{ ml: 1 }}>
80
- 약관 보기
154
+ View Terms
81
155
  </Link>
82
156
  </Typography>
83
157
  }
@@ -89,9 +163,9 @@ function TermsForm() {
89
163
  <Checkbox
90
164
  label={
91
165
  <Typography>
92
- <strong>(필수)</strong> 개인정보 처리방침에 동의합니다.
166
+ <strong>(Required)</strong> I agree to the Privacy Policy.
93
167
  <Link href="/privacy" target="_blank" sx={{ ml: 1 }}>
94
- 방침 보기
168
+ View Policy
95
169
  </Link>
96
170
  </Typography>
97
171
  }
@@ -101,154 +175,49 @@ function TermsForm() {
101
175
  />
102
176
 
103
177
  <Checkbox
104
- label="(선택) 마케팅 정보 수신에 동의합니다."
178
+ label="(Optional) I agree to receive marketing communications."
105
179
  checked={agreements.marketing}
106
180
  onChange={handleChange('marketing')}
107
181
  color="neutral"
108
182
  />
109
183
 
110
184
  <Button disabled={!agreements.terms || !agreements.privacy} sx={{ mt: 2 }}>
111
- 가입하기
185
+ Sign Up
112
186
  </Button>
113
187
  </Stack>
114
188
  );
115
189
  }
116
190
  ```
117
191
 
118
- ### Multi-Select List
119
-
120
- 목록에서 여러 항목을 선택할 때 사용합니다.
121
-
122
- ```tsx
123
- function TodoList() {
124
- const [todos, setTodos] = useState([
125
- { id: 1, text: '회의 준비하기', completed: false },
126
- { id: 2, text: '보고서 작성하기', completed: true },
127
- { id: 3, text: '이메일 답장하기', completed: false },
128
- ]);
129
-
130
- const handleToggle = (id: number) => {
131
- setTodos((prev) => prev.map((todo) => (todo.id === id ? { ...todo, completed: !todo.completed } : todo)));
132
- };
133
-
134
- return (
135
- <Stack spacing={1}>
136
- <Typography level="title-md">할 일 목록</Typography>
137
- {todos.map((todo) => (
138
- <Checkbox
139
- key={todo.id}
140
- label={
141
- <Typography
142
- sx={{
143
- textDecoration: todo.completed ? 'line-through' : 'none',
144
- color: todo.completed ? 'neutral.500' : 'inherit',
145
- }}
146
- >
147
- {todo.text}
148
- </Typography>
149
- }
150
- checked={todo.completed}
151
- onChange={() => handleToggle(todo.id)}
152
- color={todo.completed ? 'success' : 'primary'}
153
- />
154
- ))}
155
- </Stack>
156
- );
157
- }
158
- ```
159
-
160
- ### Filter Options
161
-
162
- 필터링 옵션에서 체크박스를 사용하는 예제입니다.
163
-
164
- ```tsx
165
- function ProductFilter() {
166
- const [filters, setFilters] = useState({
167
- categories: [] as string[],
168
- priceRanges: [] as string[],
169
- brands: [] as string[],
170
- });
171
-
172
- const handleCategoryChange = (category: string) => (event: React.ChangeEvent<HTMLInputElement>) => {
173
- setFilters((prev) => ({
174
- ...prev,
175
- categories: event.target.checked ? [...prev.categories, category] : prev.categories.filter((c) => c !== category),
176
- }));
177
- };
178
-
179
- return (
180
- <Stack spacing={3}>
181
- <Stack spacing={2}>
182
- <Typography level="title-sm">카테고리</Typography>
183
- <Stack spacing={1} sx={{ pl: 1 }}>
184
- <Checkbox
185
- label="전자제품"
186
- checked={filters.categories.includes('electronics')}
187
- onChange={handleCategoryChange('electronics')}
188
- size="sm"
189
- />
190
- <Checkbox
191
- label="의류"
192
- checked={filters.categories.includes('clothing')}
193
- onChange={handleCategoryChange('clothing')}
194
- size="sm"
195
- />
196
- <Checkbox
197
- label="도서"
198
- checked={filters.categories.includes('books')}
199
- onChange={handleCategoryChange('books')}
200
- size="sm"
201
- />
202
- </Stack>
203
- </Stack>
204
-
205
- <Stack spacing={2}>
206
- <Typography level="title-sm">가격대</Typography>
207
- <Stack spacing={1} sx={{ pl: 1 }}>
208
- <Checkbox label="10만원 이하" size="sm" />
209
- <Checkbox label="10-50만원" size="sm" />
210
- <Checkbox label="50만원 이상" size="sm" />
211
- </Stack>
212
- </Stack>
213
- </Stack>
214
- );
215
- }
216
- ```
217
-
218
192
  ### Select All Pattern
219
193
 
220
- 전체 선택/해제 패턴을 구현하는 예제입니다.
221
-
222
194
  ```tsx
223
195
  function SelectAllExample() {
224
196
  const [items, setItems] = useState([
225
- { id: 1, name: '항목 1', selected: false },
226
- { id: 2, name: '항목 2', selected: true },
227
- { id: 3, name: '항목 3', selected: false },
228
- { id: 4, name: '항목 4', selected: false },
197
+ { id: 1, name: 'Item 1', selected: false },
198
+ { id: 2, name: 'Item 2', selected: true },
199
+ { id: 3, name: 'Item 3', selected: false },
200
+ { id: 4, name: 'Item 4', selected: false },
229
201
  ]);
230
202
 
231
- const selectedCount = items.filter((item) => item.selected).length;
203
+ const selectedCount = items.filter(item => item.selected).length;
232
204
  const isAllSelected = selectedCount === items.length;
233
205
  const isIndeterminate = selectedCount > 0 && selectedCount < items.length;
234
206
 
235
207
  const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
236
- setItems((prev) =>
237
- prev.map((item) => ({
238
- ...item,
239
- selected: event.target.checked,
240
- })),
241
- );
208
+ setItems(prev => prev.map(item => ({ ...item, selected: event.target.checked })));
242
209
  };
243
210
 
244
211
  const handleItemToggle = (id: number) => {
245
- setItems((prev) => prev.map((item) => (item.id === id ? { ...item, selected: !item.selected } : item)));
212
+ setItems(prev => prev.map(item =>
213
+ item.id === id ? { ...item, selected: !item.selected } : item
214
+ ));
246
215
  };
247
216
 
248
217
  return (
249
218
  <Stack spacing={2}>
250
219
  <Checkbox
251
- label={`전체 선택 (${selectedCount}/${items.length})`}
220
+ label={`Select All (${selectedCount}/${items.length})`}
252
221
  checked={isAllSelected}
253
222
  indeterminate={isIndeterminate}
254
223
  onChange={handleSelectAll}
@@ -259,7 +228,7 @@ function SelectAllExample() {
259
228
  <Divider />
260
229
 
261
230
  <Stack spacing={1} sx={{ pl: 2 }}>
262
- {items.map((item) => (
231
+ {items.map(item => (
263
232
  <Checkbox
264
233
  key={item.id}
265
234
  label={item.name}
@@ -274,175 +243,48 @@ function SelectAllExample() {
274
243
  }
275
244
  ```
276
245
 
277
- ### Form with Validation
278
-
279
- 폼 검증과 함께 사용하는 체크박스 예제입니다.
246
+ ### Filter Options
280
247
 
281
248
  ```tsx
282
- function RegistrationForm() {
283
- const [formData, setFormData] = useState({
284
- name: '',
285
- email: '',
286
- agreeToTerms: false,
287
- subscribeNewsletter: false,
249
+ function ProductFilter() {
250
+ const [filters, setFilters] = useState({
251
+ categories: [] as string[],
288
252
  });
289
253
 
290
- const [errors, setErrors] = useState<Record<string, string>>({});
291
-
292
- const handleSubmit = (event: React.FormEvent) => {
293
- event.preventDefault();
294
-
295
- const newErrors: Record<string, string> = {};
296
-
297
- if (!formData.name) {
298
- newErrors.name = '이름을 입력해주세요';
299
- }
300
-
301
- if (!formData.email) {
302
- newErrors.email = '이메일을 입력해주세요';
303
- }
304
-
305
- if (!formData.agreeToTerms) {
306
- newErrors.agreeToTerms = '이용약관에 동의해주세요';
307
- }
308
-
309
- setErrors(newErrors);
310
-
311
- if (Object.keys(newErrors).length === 0) {
312
- console.log('폼 제출:', formData);
313
- }
254
+ const handleCategoryChange = (category: string) => (
255
+ event: React.ChangeEvent<HTMLInputElement>
256
+ ) => {
257
+ setFilters(prev => ({
258
+ ...prev,
259
+ categories: event.target.checked
260
+ ? [...prev.categories, category]
261
+ : prev.categories.filter(c => c !== category)
262
+ }));
314
263
  };
315
264
 
316
265
  return (
317
- <form onSubmit={handleSubmit}>
318
- <Stack spacing={3}>
319
- <Input
320
- placeholder="이름"
321
- value={formData.name}
322
- onChange={(e) => setFormData((prev) => ({ ...prev, name: e.target.value }))}
323
- error={!!errors.name}
266
+ <Stack spacing={2}>
267
+ <Typography level="title-sm">Categories</Typography>
268
+ <Stack spacing={1} sx={{ pl: 1 }}>
269
+ <Checkbox
270
+ label="Electronics"
271
+ checked={filters.categories.includes('electronics')}
272
+ onChange={handleCategoryChange('electronics')}
273
+ size="sm"
324
274
  />
325
- {errors.name && (
326
- <Typography level="body-xs" color="danger">
327
- {errors.name}
328
- </Typography>
329
- )}
330
-
331
- <Input
332
- placeholder="이메일"
333
- type="email"
334
- value={formData.email}
335
- onChange={(e) => setFormData((prev) => ({ ...prev, email: e.target.value }))}
336
- error={!!errors.email}
275
+ <Checkbox
276
+ label="Clothing"
277
+ checked={filters.categories.includes('clothing')}
278
+ onChange={handleCategoryChange('clothing')}
279
+ size="sm"
337
280
  />
338
- {errors.email && (
339
- <Typography level="body-xs" color="danger">
340
- {errors.email}
341
- </Typography>
342
- )}
343
-
344
- <FormControl error={!!errors.agreeToTerms}>
345
- <Checkbox
346
- label="이용약관 및 개인정보처리방침에 동의합니다"
347
- checked={formData.agreeToTerms}
348
- onChange={(e) => setFormData((prev) => ({ ...prev, agreeToTerms: e.target.checked }))}
349
- required
350
- />
351
- {errors.agreeToTerms && (
352
- <FormHelperText>
353
- <InfoOutlinedIcon />
354
- {errors.agreeToTerms}
355
- </FormHelperText>
356
- )}
357
- </FormControl>
358
-
359
281
  <Checkbox
360
- label="뉴스레터 구독하기"
361
- checked={formData.subscribeNewsletter}
362
- onChange={(e) => setFormData((prev) => ({ ...prev, subscribeNewsletter: e.target.checked }))}
363
- color="success"
364
- variant="soft"
282
+ label="Books"
283
+ checked={filters.categories.includes('books')}
284
+ onChange={handleCategoryChange('books')}
285
+ size="sm"
365
286
  />
366
-
367
- <Button type="submit">가입하기</Button>
368
287
  </Stack>
369
- </form>
370
- );
371
- }
372
- ```
373
-
374
- ### Data Table Row Selection
375
-
376
- 데이터 테이블에서 행 선택에 사용하는 예제입니다.
377
-
378
- ```tsx
379
- function DataTableWithSelection() {
380
- const [selectedRows, setSelectedRows] = useState<Set<number>>(new Set());
381
-
382
- const users = [
383
- { id: 1, name: '김철수', email: 'kim@example.com', role: '관리자' },
384
- { id: 2, name: '이영희', email: 'lee@example.com', role: '사용자' },
385
- { id: 3, name: '박민수', email: 'park@example.com', role: '사용자' },
386
- ];
387
-
388
- const handleRowSelect = (id: number) => {
389
- const newSelected = new Set(selectedRows);
390
- if (newSelected.has(id)) {
391
- newSelected.delete(id);
392
- } else {
393
- newSelected.add(id);
394
- }
395
- setSelectedRows(newSelected);
396
- };
397
-
398
- const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
399
- if (event.target.checked) {
400
- setSelectedRows(new Set(users.map((user) => user.id)));
401
- } else {
402
- setSelectedRows(new Set());
403
- }
404
- };
405
-
406
- return (
407
- <Stack spacing={2}>
408
- {selectedRows.size > 0 && (
409
- <Alert color="primary">
410
- {selectedRows.size}개 항목이 선택되었습니다.
411
- <Button size="sm" variant="soft" sx={{ ml: 2 }}>
412
- 선택 항목 삭제
413
- </Button>
414
- </Alert>
415
- )}
416
-
417
- <Table>
418
- <thead>
419
- <tr>
420
- <th style={{ width: 48 }}>
421
- <Checkbox
422
- size="sm"
423
- checked={selectedRows.size === users.length}
424
- indeterminate={selectedRows.size > 0 && selectedRows.size < users.length}
425
- onChange={handleSelectAll}
426
- />
427
- </th>
428
- <th>이름</th>
429
- <th>이메일</th>
430
- <th>역할</th>
431
- </tr>
432
- </thead>
433
- <tbody>
434
- {users.map((user) => (
435
- <tr key={user.id}>
436
- <td>
437
- <Checkbox size="sm" checked={selectedRows.has(user.id)} onChange={() => handleRowSelect(user.id)} />
438
- </td>
439
- <td>{user.name}</td>
440
- <td>{user.email}</td>
441
- <td>{user.role}</td>
442
- </tr>
443
- ))}
444
- </tbody>
445
- </Table>
446
288
  </Stack>
447
289
  );
448
290
  }
@@ -450,172 +292,85 @@ function DataTableWithSelection() {
450
292
 
451
293
  ## Props and Customization
452
294
 
453
- ### Colors
454
-
455
- 다양한 색상을 적용할 있습니다.
456
-
457
- ```tsx
458
- <Stack spacing={2}>
459
- <Checkbox label="Primary" color="primary" checked />
460
- <Checkbox label="Success" color="success" checked />
461
- <Checkbox label="Warning" color="warning" checked />
462
- <Checkbox label="Danger" color="danger" checked />
463
- <Checkbox label="Neutral" color="neutral" checked />
464
- </Stack>
465
- ```
466
-
467
- ### Sizes
468
-
469
- 체크박스의 크기를 조절할 있습니다.
470
-
471
- ```tsx
472
- <Stack direction="row" spacing={2} alignItems="center">
473
- <Checkbox label="Small" size="sm" checked />
474
- <Checkbox label="Medium" size="md" checked />
475
- <Checkbox label="Large" size="lg" checked />
476
- </Stack>
477
- ```
478
-
479
- ### Variants
480
-
481
- 다양한 시각적 스타일을 적용할 수 있습니다.
482
-
483
- ```tsx
484
- <Stack spacing={2}>
485
- <Checkbox label="Solid" variant="solid" checked />
486
- <Checkbox label="Soft" variant="soft" checked />
487
- <Checkbox label="Outlined" variant="outlined" checked />
488
- <Checkbox label="Plain" variant="plain" checked />
489
- </Stack>
490
- ```
491
-
492
- ## States
493
-
494
- ### Indeterminate State
495
-
496
- 부분적으로 선택된 상태를 나타낼 때 사용합니다.
497
-
498
- ```tsx
499
- <Checkbox label="부분 선택됨" indeterminate={true} onChange={handleChange} />
500
- ```
501
-
502
- ### Disabled State
503
-
504
- 비활성 상태의 체크박스입니다.
505
-
506
- ```tsx
507
- <Stack spacing={1}>
508
- <Checkbox label="비활성 (선택 안됨)" disabled />
509
- <Checkbox label="비활성 (선택됨)" disabled checked />
510
- <Checkbox label="비활성 (부분 선택)" disabled indeterminate />
511
- </Stack>
512
- ```
513
-
514
- ### Error State
515
-
516
- 오류 상태를 표시할 때 사용합니다.
517
-
518
- ```tsx
519
- <FormControl error>
520
- <Checkbox label="필수 동의 항목" required onChange={handleChange} />
521
- <FormHelperText>
522
- <InfoOutlinedIcon />이 항목은 필수로 선택해야 합니다.
523
- </FormHelperText>
524
- </FormControl>
525
- ```
295
+ ### Key Props
296
+
297
+ | Prop | Type | Default | Description |
298
+ | ------------------- | -------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- |
299
+ | `checked` | `boolean` | - | Whether the checkbox is checked (controlled) |
300
+ | `defaultChecked` | `boolean` | `false` | Initial checked state (uncontrolled) |
301
+ | `indeterminate` | `boolean` | `false` | Shows a partial selection state (dash icon instead of checkmark) |
302
+ | `label` | `ReactNode` | - | Label content displayed next to the checkbox |
303
+ | `onChange` | `(event: ChangeEvent<HTMLInputElement>) => void` | - | Callback when the checked state changes |
304
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color scheme |
305
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Checkbox size |
306
+ | `variant` | `'solid' \| 'soft' \| 'outlined' \| 'plain'` | `'solid'` | Visual style when checked |
307
+ | `disabled` | `boolean` | `false` | Disables the checkbox |
308
+ | `readOnly` | `boolean` | `false` | Makes the checkbox read-only |
309
+ | `required` | `boolean` | `false` | Marks the field as required |
310
+ | `name` | `string` | - | HTML name attribute for form submission |
311
+ | `value` | `string \| number` | - | Value attribute for form submission |
312
+ | `overlay` | `boolean` | `false` | Extends the clickable area to cover the parent container (useful for card-click patterns) |
313
+ | `disableIcon` | `boolean` | `false` | Hides the checkbox icon |
314
+ | `checkedIcon` | `ReactNode` | - | Custom icon for the checked state |
315
+ | `uncheckedIcon` | `ReactNode` | - | Custom icon for the unchecked state |
316
+ | `indeterminateIcon` | `ReactNode` | - | Custom icon for the indeterminate state |
317
+ | `endDecorator` | `ReactNode` | - | Content rendered after the checkbox label |
318
+ | `sx` | `SxProps` | - | Custom styles using the MUI system |
319
+
320
+ > **Note**: Checkbox also accepts all Joy UI Checkbox props and Framer Motion props.
526
321
 
527
322
  ## Best Practices
528
323
 
529
- 1. **명확한 라벨**: 체크박스가 무엇을 의미하는지 명확하게 표시하세요.
324
+ 1. **Use clear, descriptive labels.** The label should make it obvious what the user is agreeing to or selecting.
530
325
 
531
326
  ```tsx
532
- // ✅ 명확한 라벨
533
- <Checkbox label="마케팅 정보 수신에 동의합니다" />
327
+ // ✅ Good
328
+ <Checkbox label="I agree to receive marketing emails" />
534
329
 
535
- // ❌ 모호한 라벨
536
- <Checkbox label="동의" />
330
+ // ❌ Bad
331
+ <Checkbox label="Agree" />
537
332
  ```
538
333
 
539
- 2. **적절한 그룹화**: 관련된 체크박스들은 논리적으로 그룹화하세요.
334
+ 2. **Group related checkboxes logically.** Use headings and indentation to create visual hierarchy.
540
335
 
541
336
  ```tsx
542
337
  <Stack spacing={3}>
543
- <Typography level="title-md">알림 설정</Typography>
338
+ <Typography level="title-md">Notification Settings</Typography>
544
339
  <Stack spacing={1} sx={{ pl: 1 }}>
545
- <Checkbox label="이메일 알림" />
546
- <Checkbox label="SMS 알림" />
547
- <Checkbox label="푸시 알림" />
340
+ <Checkbox label="Email notifications" />
341
+ <Checkbox label="SMS notifications" />
342
+ <Checkbox label="Push notifications" />
548
343
  </Stack>
549
344
  </Stack>
550
345
  ```
551
346
 
552
- 3. **필수/선택 구분**: 필수 선택 항목과 선택사항을 명확히 구분하세요.
347
+ 3. **Distinguish required from optional.** Make it clear which selections are mandatory.
553
348
 
554
349
  ```tsx
555
- <Checkbox
556
- label={<><strong>(필수)</strong> 이용약관에 동의</>}
557
- required
558
- />
559
- <Checkbox
560
- label="(선택) 뉴스레터 구독"
561
- color="neutral"
562
- />
350
+ <Checkbox label={<><strong>(Required)</strong> Accept Terms</>} required />
351
+ <Checkbox label="(Optional) Subscribe to newsletter" color="neutral" />
563
352
  ```
564
353
 
565
- 4. **상태 피드백**: 사용자가 선택한 내용을 명확히 보여주세요.
354
+ 4. **Use the indeterminate state correctly.** Only apply it to a parent checkbox that represents a group of child checkboxes with mixed selection.
566
355
 
567
- ```tsx
568
- {
569
- selectedCount > 0 && (
570
- <Typography level="body-sm" color="primary">
571
- {selectedCount}개 항목이 선택되었습니다.
572
- </Typography>
573
- );
574
- }
575
- ```
356
+ 5. **Prefer Checkbox over Switch** when the setting is a binary choice that is not applied immediately. Use Switch for instant toggle actions.
576
357
 
577
358
  ## Accessibility
578
359
 
579
- Checkbox 컴포넌트는 다음과 같은 접근성 기능을 제공합니다:
580
-
581
- ### 키보드 탐색
360
+ - **Keyboard navigation**: Tab to focus, Space to toggle checked state.
361
+ - **ARIA attributes**: `role="checkbox"`, `aria-checked` reflects the current state (including `mixed` for indeterminate).
362
+ - **Label association**: The `label` prop is automatically associated with the checkbox input.
363
+ - **Error state**: When used inside `FormControl` with `error`, `aria-invalid` is applied.
364
+ - **Screen reader**: Announces as "\[label], checkbox, checked/unchecked/mixed".
582
365
 
583
- - **Tab**: 체크박스로 포커스 이동
584
- - **Space**: 체크박스 선택/해제
585
- - **Enter**: 링크나 버튼 라벨이 있는 경우 활성화
366
+ ## Performance Tips
586
367
 
587
- ### ARIA 속성
588
-
589
- - `role="checkbox"`: 체크박스 역할 정의
590
- - `aria-checked`: 현재 선택 상태 표시
591
- - `aria-describedby`: 추가 설명과 연결
592
-
593
- ### 추가 접근성 고려사항
594
-
595
- ```tsx
596
- <Checkbox
597
- label="마케팅 정보 수신 동의"
598
- aria-describedby="marketing-description"
599
- />
600
- <FormHelperText id="marketing-description">
601
- 새로운 제품이나 할인 정보를 이메일로 받아보실 수 있습니다.
602
- </FormHelperText>
603
- ```
604
-
605
- ## Performance Considerations
606
-
607
- 1. **상태 최적화**: 큰 목록에서는 상태 관리를 효율적으로 하세요.
368
+ Use `Set` instead of arrays for managing selection state in large lists:
608
369
 
609
370
  ```tsx
610
- // ✅ Set을 사용한 효율적인 선택 관리
371
+ // ✅ Good: O(1) lookup with Set
611
372
  const [selected, setSelected] = useState(new Set<number>());
612
373
 
613
- // ❌ 배열로 관리하는 비효율적 방법
374
+ // ❌ Bad: O(n) lookup with Array
614
375
  const [selected, setSelected] = useState<number[]>([]);
615
376
  ```
616
-
617
- 2. **메모이제이션**: 복잡한 핸들러는 useCallback으로 메모이제이션하세요.
618
-
619
- 3. **가상화**: 매우 긴 목록에서는 가상화를 고려하세요.
620
-
621
- Checkbox는 사용자가 다중 선택을 할 수 있는 직관적인 인터페이스를 제공하는 핵심 입력 컴포넌트입니다. 적절한 라벨링과 그룹화를 통해 명확하고 사용하기 쉬운 선택 인터페이스를 만들 수 있습니다.