@ceed/cds 1.24.1-next.3 → 1.25.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 (63) hide show
  1. package/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
  2. package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  3. package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
  4. package/dist/components/DataTable/components.d.ts +2 -1
  5. package/dist/components/DataTable/styled.d.ts +3 -1
  6. package/dist/components/DataTable/types.d.ts +1 -0
  7. package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
  8. package/dist/components/RadioTileGroup/index.d.ts +3 -0
  9. package/dist/components/data-display/DataTable.md +77 -1
  10. package/dist/components/data-display/InfoSign.md +74 -91
  11. package/dist/components/data-display/Typography.md +411 -94
  12. package/dist/components/feedback/CircularProgress.md +257 -0
  13. package/dist/components/feedback/Dialog.md +76 -62
  14. package/dist/components/feedback/Modal.md +430 -138
  15. package/dist/components/feedback/Skeleton.md +280 -0
  16. package/dist/components/feedback/llms.txt +2 -0
  17. package/dist/components/index.d.ts +1 -0
  18. package/dist/components/inputs/Autocomplete.md +356 -107
  19. package/dist/components/inputs/ButtonGroup.md +115 -104
  20. package/dist/components/inputs/CurrencyInput.md +183 -5
  21. package/dist/components/inputs/DatePicker.md +108 -431
  22. package/dist/components/inputs/DateRangePicker.md +131 -492
  23. package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
  24. package/dist/components/inputs/FormControl.md +361 -0
  25. package/dist/components/inputs/IconButton.md +137 -88
  26. package/dist/components/inputs/Input.md +204 -73
  27. package/dist/components/inputs/MonthPicker.md +95 -422
  28. package/dist/components/inputs/MonthRangePicker.md +89 -466
  29. package/dist/components/inputs/PercentageInput.md +185 -16
  30. package/dist/components/inputs/RadioButton.md +163 -35
  31. package/dist/components/inputs/RadioList.md +241 -0
  32. package/dist/components/inputs/RadioTileGroup.md +507 -0
  33. package/dist/components/inputs/Select.md +222 -326
  34. package/dist/components/inputs/Slider.md +334 -0
  35. package/dist/components/inputs/Switch.md +143 -376
  36. package/dist/components/inputs/Textarea.md +213 -10
  37. package/dist/components/inputs/Uploader/Uploader.md +145 -66
  38. package/dist/components/inputs/llms.txt +4 -0
  39. package/dist/components/navigation/Breadcrumbs.md +57 -308
  40. package/dist/components/navigation/Drawer.md +180 -0
  41. package/dist/components/navigation/Dropdown.md +98 -215
  42. package/dist/components/navigation/IconMenuButton.md +40 -502
  43. package/dist/components/navigation/InsetDrawer.md +281 -650
  44. package/dist/components/navigation/Link.md +31 -348
  45. package/dist/components/navigation/Menu.md +92 -285
  46. package/dist/components/navigation/MenuButton.md +55 -448
  47. package/dist/components/navigation/Pagination.md +47 -338
  48. package/dist/components/navigation/Stepper.md +160 -28
  49. package/dist/components/navigation/Tabs.md +57 -316
  50. package/dist/components/surfaces/Accordions.md +49 -804
  51. package/dist/components/surfaces/Card.md +97 -157
  52. package/dist/components/surfaces/Divider.md +83 -234
  53. package/dist/components/surfaces/Sheet.md +153 -328
  54. package/dist/guides/ThemeProvider.md +89 -0
  55. package/dist/guides/llms.txt +9 -0
  56. package/dist/index.browser.js +224 -0
  57. package/dist/index.browser.js.map +7 -0
  58. package/dist/index.cjs +648 -390
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.js +563 -361
  61. package/dist/llms.txt +9 -0
  62. package/framer/index.js +1 -163
  63. package/package.json +22 -17
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
+ PercentageInput is a specialized numeric input for entering percentage values. It automatically appends a `%` suffix, formats numbers with thousand separators, and supports min/max validation with built-in error states. Use it for any field where users need to enter a percentage, such as tax rates, discounts, or commission rates.
6
+
7
+ > **Tip: Use built-in form props**
8
+ >
9
+ > This component supports `label` and `helperText` props directly. Use these instead of wrapping with FormControl + FormLabel + FormHelperText for simpler forms.
10
+
5
11
  ```tsx
6
12
  <PercentageInput placeholder="Enter a percentage" />
7
13
  ```
@@ -22,7 +28,29 @@
22
28
  | min | — | — |
23
29
  | max | — | — |
24
30
 
25
- ### Sizes
31
+ ## Usage
32
+
33
+ ```tsx
34
+ import { PercentageInput } from '@ceed/cds';
35
+
36
+ function MyComponent() {
37
+ const [rate, setRate] = React.useState(0);
38
+
39
+ return (
40
+ <PercentageInput
41
+ label="Tax Rate"
42
+ value={rate}
43
+ onChange={(e) => setRate(e.target.value)}
44
+ min={0}
45
+ max={100}
46
+ />
47
+ );
48
+ }
49
+ ```
50
+
51
+ ## Sizes
52
+
53
+ PercentageInput supports three sizes: `sm`, `md` (default), and `lg`.
26
54
 
27
55
  ```tsx
28
56
  <Stack gap={2}>
@@ -32,44 +60,45 @@
32
60
  </Stack>
33
61
  ```
34
62
 
35
- ### Disabled
63
+ ## Form Features
64
+
65
+ ### With Label
36
66
 
37
67
  ```tsx
38
68
  <PercentageInput
39
69
  placeholder="Enter a percentage"
40
- disabled
70
+ label="Percentage Input"
41
71
  />
42
72
  ```
43
73
 
44
- ### Error
45
-
46
74
  ```tsx
47
- <PercentageInput
48
- placeholder="Enter a percentage"
49
- error
50
- />
75
+ <PercentageInput label="Discount Rate" />
51
76
  ```
52
77
 
53
- ### WithLabel
78
+ ### With Helper Text
54
79
 
55
80
  ```tsx
56
81
  <PercentageInput
57
82
  placeholder="Enter a percentage"
58
83
  label="Percentage Input"
84
+ helperText="Please enter a percentage"
59
85
  />
60
86
  ```
61
87
 
62
- ### WithHelperText
88
+ ```tsx
89
+ <PercentageInput label="Commission" helperText="Enter the commission percentage." />
90
+ ```
91
+
92
+ ### Error State
63
93
 
64
94
  ```tsx
65
95
  <PercentageInput
66
96
  placeholder="Enter a percentage"
67
- label="Percentage Input"
68
- helperText="Please enter a percentage"
97
+ error
69
98
  />
70
99
  ```
71
100
 
72
- ### WithErrorText
101
+ ### With Error Text
73
102
 
74
103
  ```tsx
75
104
  <PercentageInput
@@ -91,7 +120,20 @@
91
120
  />
92
121
  ```
93
122
 
94
- ### Min
123
+ ### Disabled
124
+
125
+ ```tsx
126
+ <PercentageInput
127
+ placeholder="Enter a percentage"
128
+ disabled
129
+ />
130
+ ```
131
+
132
+ ## Min / Max Validation
133
+
134
+ Set `min` and `max` to enforce value boundaries. When the entered value is outside the range, the input automatically shows an error state.
135
+
136
+ ### Minimum Value
95
137
 
96
138
  ```tsx
97
139
  <PercentageInput
@@ -103,7 +145,12 @@
103
145
  />
104
146
  ```
105
147
 
106
- ### Max
148
+ ```tsx
149
+ <PercentageInput min={10} defaultValue={5} />
150
+ {/* Shows error because 5 < 10 */}
151
+ ```
152
+
153
+ ### Maximum Value
107
154
 
108
155
  ```tsx
109
156
  <PercentageInput
@@ -114,3 +161,125 @@
114
161
  defaultValue={5000}
115
162
  />
116
163
  ```
164
+
165
+ ```tsx
166
+ <PercentageInput max={500} defaultValue={5000} />
167
+ {/* Shows error because 5000 > 500 */}
168
+ ```
169
+
170
+ ## Decimal Scale
171
+
172
+ Use `maxDecimalScale` to control the number of decimal places allowed. The default is `0` (integers only).
173
+
174
+ ```tsx
175
+ // Integer only (default)
176
+ <PercentageInput maxDecimalScale={0} />
177
+
178
+ // Up to 2 decimal places: 12.34%
179
+ <PercentageInput maxDecimalScale={2} />
180
+
181
+ // Up to 3 decimal places: 12.345%
182
+ <PercentageInput maxDecimalScale={3} />
183
+ ```
184
+
185
+ ## Minor Unit Mode
186
+
187
+ When `useMinorUnit` is `true`, the component converts between display values and minor units based on `maxDecimalScale`. This is useful when your API stores percentages in integer form.
188
+
189
+ ```tsx
190
+ // Display shows "20.12%", onChange returns 20120
191
+ <PercentageInput useMinorUnit maxDecimalScale={3} value={20120} />
192
+
193
+ // Display shows "20.12%", onChange returns 20.12
194
+ <PercentageInput useMinorUnit={false} maxDecimalScale={2} value={20.12} />
195
+ ```
196
+
197
+ ## Common Use Cases
198
+
199
+ ### Discount Configuration
200
+
201
+ ```tsx
202
+ function DiscountSetting() {
203
+ const [discount, setDiscount] = React.useState(0);
204
+
205
+ return (
206
+ <PercentageInput
207
+ label="Discount"
208
+ helperText="Enter 0-100%"
209
+ value={discount}
210
+ onChange={(e) => setDiscount(e.target.value)}
211
+ min={0}
212
+ max={100}
213
+ />
214
+ );
215
+ }
216
+ ```
217
+
218
+ ### Tax Rate Settings
219
+
220
+ ```tsx
221
+ function TaxRateForm() {
222
+ return (
223
+ <Stack gap={2}>
224
+ <PercentageInput
225
+ label="VAT Rate"
226
+ defaultValue={10}
227
+ maxDecimalScale={2}
228
+ min={0}
229
+ max={100}
230
+ />
231
+ <PercentageInput
232
+ label="Local Tax Rate"
233
+ defaultValue={2.5}
234
+ maxDecimalScale={2}
235
+ min={0}
236
+ max={50}
237
+ />
238
+ </Stack>
239
+ );
240
+ }
241
+ ```
242
+
243
+ ### Progress / Completion Tracker
244
+
245
+ ```tsx
246
+ function CompletionTracker({ value, onChange }) {
247
+ return (
248
+ <PercentageInput
249
+ label="Completion"
250
+ value={value}
251
+ onChange={(e) => onChange(e.target.value)}
252
+ min={0}
253
+ max={100}
254
+ helperText={value >= 100 ? 'Task is complete!' : `${100 - value}% remaining`}
255
+ />
256
+ );
257
+ }
258
+ ```
259
+
260
+ ## Best Practices
261
+
262
+ 1. **Always set min and max**: Bound the valid range to prevent unreasonable values.
263
+
264
+ ```tsx
265
+ // ✅ Clear bounds
266
+ <PercentageInput min={0} max={100} />
267
+
268
+ // ❌ Unbounded — user could enter 99999%
269
+ <PercentageInput />
270
+ ```
271
+
272
+ 2. **Use `maxDecimalScale` appropriately**: Set it to `0` for whole percentages (discounts, progress), or `2`–`3` for precise rates (tax, interest).
273
+
274
+ 3. **Use `useMinorUnit` for API compatibility**: When your backend stores percentages as integers (e.g., 1050 for 10.50%), enable `useMinorUnit` with the appropriate `maxDecimalScale`.
275
+
276
+ 4. **Provide context via labels and helper text**: Clarify what the percentage represents and what range is valid.
277
+
278
+ 5. **Handle edge cases**: Consider what happens at boundary values (0%, 100%) and negative percentages if allowed.
279
+
280
+ ## Accessibility
281
+
282
+ - The input has proper labeling via the `label` prop, linked with `aria-labelledby`.
283
+ - Error states trigger `aria-invalid` with associated error messages via `aria-describedby`.
284
+ - The `%` suffix is a visual decoration — ensure labels clarify that the field expects a percentage for screen readers.
285
+ - Min/max validation errors are communicated visually; add descriptive `helperText` to make the valid range clear.
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Radio 컴포넌트는 사용자가 여러 옵션 하나만 선택할 있는 단일 선택 인터페이스를 제공합니다. 라디오 버튼은 그룹으로 사용되어야 하며, 하나가 선택되면 다른 옵션들은 자동으로 해제됩니다.
5
+ The Radio component provides a single-selection interface where users can choose exactly one option from a set of mutually exclusive choices. When one radio button is selected, any previously selected option in the same group is automatically deselected.
6
+
7
+ Radio buttons are built on Joy UI's Radio and RadioGroup components, supporting multiple visual variants, sizes, colors, and advanced customization such as custom icons, overlay interactions, and icon-free designs. They are ideal for settings forms, preference panels, and any UI where a user must commit to a single option from a predefined list.
6
8
 
7
9
  ```tsx
8
10
  <Radio />
@@ -28,35 +30,28 @@ Radio 컴포넌트는 사용자가 여러 옵션 중 하나만 선택할 수 있
28
30
  ## Usage
29
31
 
30
32
  ```tsx
31
- import { Radio } from '@ceed/cds';
33
+ import { Radio, RadioGroup } from '@ceed/cds';
32
34
 
33
35
  function MyComponent() {
34
36
  const [selectedValue, setSelectedValue] = useState('option1');
35
37
 
36
38
  return (
37
- <div>
38
- <Radio
39
- name="example"
40
- value="option1"
41
- checked={selectedValue === 'option1'}
42
- onChange={(e) => setSelectedValue(e.target.value)}
43
- label="옵션 1"
44
- />
45
- <Radio
46
- name="example"
47
- value="option2"
48
- checked={selectedValue === 'option2'}
49
- onChange={(e) => setSelectedValue(e.target.value)}
50
- label="옵션 2"
51
- />
52
- </div>
39
+ <RadioGroup
40
+ name="example"
41
+ value={selectedValue}
42
+ onChange={(e) => setSelectedValue(e.target.value)}
43
+ >
44
+ <Radio value="option1" label="Option 1" />
45
+ <Radio value="option2" label="Option 2" />
46
+ <Radio value="option3" label="Option 3" />
47
+ </RadioGroup>
53
48
  );
54
49
  }
55
50
  ```
56
51
 
57
- ## Examples
52
+ ## Variants
58
53
 
59
- ### Variants
54
+ Radio supports four visual variants: `outlined` (default), `soft`, `solid`, and `plain`. Choose a variant that fits the visual context of your form.
60
55
 
61
56
  ```tsx
62
57
  <FormControl>
@@ -70,7 +65,9 @@ function MyComponent() {
70
65
  </FormControl>
71
66
  ```
72
67
 
73
- ### Sizes
68
+ ## Sizes
69
+
70
+ Three size presets are available: `sm`, `md` (default), and `lg`. Use smaller sizes for compact layouts and larger sizes for touch-friendly interfaces.
74
71
 
75
72
  ```tsx
76
73
  <FormControl>
@@ -83,7 +80,9 @@ function MyComponent() {
83
80
  </FormControl>
84
81
  ```
85
82
 
86
- ### Colors
83
+ ## Colors
84
+
85
+ Radio supports five semantic colors: `primary`, `neutral`, `danger`, `success`, and `warning`. Use semantic colors to convey meaning -- for example, `danger` for destructive options or `success` for confirmed selections.
87
86
 
88
87
  ```tsx
89
88
  <FormControl>
@@ -98,9 +97,9 @@ function MyComponent() {
98
97
  </FormControl>
99
98
  ```
100
99
 
101
- ### Custom Icons
100
+ ## Custom Icons
102
101
 
103
- 아이콘을 커스터마이징하여 특별한 용도로 사용할 있습니다.
102
+ Replace the default radio indicator with custom icons using the `checkedIcon` prop. This is useful for card-style selectors where the selection state needs a distinctive visual treatment.
104
103
 
105
104
  ```tsx
106
105
  <RadioGroup aria-label="platform" defaultValue="Website" overlay name="platform" sx={{
@@ -142,9 +141,9 @@ function MyComponent() {
142
141
  </RadioGroup>
143
142
  ```
144
143
 
145
- ### No Icon
144
+ ## No Icon
146
145
 
147
- 아이콘 없이 텍스트만으로 구성할 있습니다.
146
+ Use the `disableIcon` prop to hide the radio indicator entirely. This pattern is commonly used for card-style selectors where the entire card acts as the selectable target, and the selection state is conveyed through border or background styling.
148
147
 
149
148
  ```tsx
150
149
  <Box sx={{
@@ -195,9 +194,9 @@ Storage
195
194
  </Box>
196
195
  ```
197
196
 
198
- ### Segmented Controls
197
+ ## Segmented Controls
199
198
 
200
- 세그먼트 컨트롤 스타일로 사용할 있습니다.
199
+ Radio buttons can be styled as segmented controls for compact, inline option switching. This pattern works well for toolbar-style controls where horizontal space is limited.
201
200
 
202
201
  ```tsx
203
202
  <Box sx={{
@@ -238,9 +237,9 @@ Storage
238
237
  </Box>
239
238
  ```
240
239
 
241
- ### Product Attributes
240
+ ## Product Attributes
242
241
 
243
- 제품 속성 선택 등의 용도로 활용할 있습니다.
242
+ Radio groups can be customized to represent product attributes such as colors and sizes. Using `overlay`, `disableIcon`, and custom `Sheet` containers, you can create rich visual selectors.
244
243
 
245
244
  ```tsx
246
245
  <Box sx={{
@@ -337,14 +336,143 @@ Size
337
336
  </Box>
338
337
  ```
339
338
 
339
+ ## Common Use Cases
340
+
341
+ ### Survey / Questionnaire Form
342
+
343
+ ```tsx
344
+ function SurveyQuestion() {
345
+ const [answer, setAnswer] = useState('');
346
+
347
+ return (
348
+ <FormControl>
349
+ <FormLabel>How satisfied are you with our service?</FormLabel>
350
+ <RadioGroup
351
+ name="satisfaction"
352
+ value={answer}
353
+ onChange={(e) => setAnswer(e.target.value)}
354
+ >
355
+ <Radio value="very-satisfied" label="Very Satisfied" color="success" />
356
+ <Radio value="satisfied" label="Satisfied" />
357
+ <Radio value="neutral" label="Neutral" />
358
+ <Radio value="dissatisfied" label="Dissatisfied" color="warning" />
359
+ <Radio value="very-dissatisfied" label="Very Dissatisfied" color="danger" />
360
+ </RadioGroup>
361
+ </FormControl>
362
+ );
363
+ }
364
+ ```
365
+
366
+ ### Payment Method Selection
367
+
368
+ ```tsx
369
+ function PaymentMethodSelector() {
370
+ const [method, setMethod] = useState('credit-card');
371
+
372
+ return (
373
+ <FormControl>
374
+ <FormLabel>Payment Method</FormLabel>
375
+ <RadioGroup
376
+ name="payment"
377
+ value={method}
378
+ onChange={(e) => setMethod(e.target.value)}
379
+ sx={{ gap: 1.5 }}
380
+ >
381
+ {[
382
+ { value: 'credit-card', label: 'Credit Card' },
383
+ { value: 'bank-transfer', label: 'Bank Transfer' },
384
+ { value: 'paypal', label: 'PayPal' },
385
+ ].map((option) => (
386
+ <Sheet key={option.value} variant="outlined" sx={{ p: 2, borderRadius: 'md' }}>
387
+ <Radio
388
+ overlay
389
+ value={option.value}
390
+ label={option.label}
391
+ slotProps={{
392
+ action: ({ checked }) => ({
393
+ sx: {
394
+ ...(checked && {
395
+ '--variant-borderWidth': '2px',
396
+ borderColor: 'primary.500',
397
+ }),
398
+ },
399
+ }),
400
+ }}
401
+ />
402
+ </Sheet>
403
+ ))}
404
+ </RadioGroup>
405
+ </FormControl>
406
+ );
407
+ }
408
+ ```
409
+
410
+ ### Shipping Speed Selection
411
+
412
+ ```tsx
413
+ function ShippingOptions() {
414
+ const [shipping, setShipping] = useState('standard');
415
+
416
+ return (
417
+ <FormControl>
418
+ <FormLabel>Shipping Speed</FormLabel>
419
+ <RadioGroup
420
+ name="shipping"
421
+ value={shipping}
422
+ onChange={(e) => setShipping(e.target.value)}
423
+ >
424
+ <Radio value="standard" label="Standard (5-7 business days) - Free" />
425
+ <Radio value="express" label="Express (2-3 business days) - $9.99" />
426
+ <Radio value="overnight" label="Overnight (next day) - $24.99" />
427
+ </RadioGroup>
428
+ </FormControl>
429
+ );
430
+ }
431
+ ```
432
+
340
433
  ## Best Practices
341
434
 
342
- 1. **그룹 사용**: Radio 버튼들은 반드시 동일한 `name` 속성을 가진 그룹으로 사용해야 합니다.
435
+ 1. **Always use RadioGroup**: Wrap Radio buttons in a `RadioGroup` to ensure proper grouping, keyboard navigation, and mutual exclusivity.
436
+
437
+ ```tsx
438
+ // ✅ Wrapped in RadioGroup
439
+ <RadioGroup name="options" value={value} onChange={handleChange}>
440
+ <Radio value="a" label="Option A" />
441
+ <Radio value="b" label="Option B" />
442
+ </RadioGroup>
443
+
444
+ // ❌ Standalone radios without a group
445
+ <Radio name="options" value="a" label="Option A" />
446
+ <Radio name="options" value="b" label="Option B" />
447
+ ```
448
+
449
+ 2. **Provide clear labels**: Every radio button must have a visible label or an `aria-label` to describe its purpose.
450
+
451
+ ```tsx
452
+ // ✅ Descriptive label
453
+ <Radio value="monthly" label="Monthly billing ($10/month)" />
454
+
455
+ // ❌ Vague label
456
+ <Radio value="monthly" label="Option 1" />
457
+ ```
458
+
459
+ 3. **Set a default selection**: Pre-select the most common or recommended option using `defaultValue` or a controlled `value` to reduce user effort.
460
+
461
+ ```tsx
462
+ // ✅ Sensible default
463
+ <RadioGroup defaultValue="standard" name="shipping">
464
+ <Radio value="standard" label="Standard Shipping" />
465
+ <Radio value="express" label="Express Shipping" />
466
+ </RadioGroup>
467
+ ```
343
468
 
344
- 2. **레이블 제공**: 접근성을 위해 항상 의미 있는 `label`을 제공하세요.
469
+ 4. **Limit options to 2-7 items**: If more than 7 options exist, consider using a Select or Autocomplete component instead.
345
470
 
346
- 3. **초기값 설정**: 사용자 경험을 위해 적절한 초기값을 설정하는 것이 좋습니다.
471
+ 5. **Use vertical layout by default**: Vertical lists are easier to scan. Only use horizontal (`orientation="horizontal"`) when options are very short (e.g., segmented controls).
347
472
 
348
- 4. **적절한 간격**: 여러 라디오 버튼 간에는 충분한 간격을 두어 선택하기 쉽게 만드세요.
473
+ ## Accessibility
349
474
 
350
- 5. **상태 관리**: 제어 컴포넌트로 사용하여 상태를 명확히 관리하세요.
475
+ - **Keyboard navigation**: Users can move between options using `Arrow Up` / `Arrow Down` (vertical) or `Arrow Left` / `Arrow Right` (horizontal). `Space` selects the focused option.
476
+ - **ARIA roles**: `RadioGroup` renders with `role="radiogroup"`, and each `Radio` renders with `role="radio"` and `aria-checked` reflecting its state.
477
+ - **Labeling**: Always connect a `FormLabel` to the `RadioGroup` via `aria-labelledby`, or provide an `aria-label` directly on the group so screen readers can announce the group purpose.
478
+ - **Focus management**: When using `overlay` or `disableIcon` patterns, ensure the interactive area is clearly communicated -- the `overlay` prop extends the clickable area to the parent container, which is also reflected in focus styling.