@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.
- package/dist/chunks/rehype-accent-FZRUD7VI.js +39 -0
- package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
- package/dist/components/CurrencyInput/hooks/use-currency-setting.d.ts +2 -2
- package/dist/components/DataTable/components.d.ts +2 -1
- package/dist/components/DataTable/styled.d.ts +3 -1
- package/dist/components/DataTable/types.d.ts +1 -0
- package/dist/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
- package/dist/components/RadioTileGroup/index.d.ts +3 -0
- package/dist/components/data-display/DataTable.md +77 -1
- package/dist/components/data-display/InfoSign.md +74 -91
- package/dist/components/data-display/Typography.md +411 -94
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Dialog.md +76 -62
- package/dist/components/feedback/Modal.md +430 -138
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/inputs/Autocomplete.md +356 -107
- package/dist/components/inputs/ButtonGroup.md +115 -104
- package/dist/components/inputs/CurrencyInput.md +183 -5
- package/dist/components/inputs/DatePicker.md +108 -431
- package/dist/components/inputs/DateRangePicker.md +131 -492
- package/dist/components/inputs/FilterableCheckboxGroup.md +145 -19
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/Input.md +204 -73
- package/dist/components/inputs/MonthPicker.md +95 -422
- package/dist/components/inputs/MonthRangePicker.md +89 -466
- package/dist/components/inputs/PercentageInput.md +185 -16
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +507 -0
- package/dist/components/inputs/Select.md +222 -326
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +143 -376
- package/dist/components/inputs/Textarea.md +213 -10
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +4 -0
- package/dist/components/navigation/Breadcrumbs.md +57 -308
- package/dist/components/navigation/Drawer.md +180 -0
- package/dist/components/navigation/Dropdown.md +98 -215
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +281 -650
- package/dist/components/navigation/Link.md +31 -348
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Accordions.md +49 -804
- package/dist/components/surfaces/Card.md +97 -157
- package/dist/components/surfaces/Divider.md +83 -234
- package/dist/components/surfaces/Sheet.md +153 -328
- package/dist/guides/ThemeProvider.md +89 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/index.browser.js +224 -0
- package/dist/index.browser.js.map +7 -0
- package/dist/index.cjs +648 -390
- package/dist/index.d.ts +1 -1
- package/dist/index.js +563 -361
- package/dist/llms.txt +9 -0
- package/framer/index.js +1 -163
- package/package.json +22 -17
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
# RadioTileGroup
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
RadioTileGroup is a selection component that displays radio options as visually distinct tiles. Unlike a standard radio button list, each option occupies a tile card, making selections more prominent and scannable. This is especially useful for choices that benefit from visual emphasis, such as plan selection, category picking, or preference surveys.
|
|
6
|
+
|
|
7
|
+
The component supports flexible layouts (horizontal, grid, vertical), icons via `startDecorator`, three sizes, and built-in form integration with `label`, `helperText`, `error`, and `required` props. It works in both controlled and uncontrolled modes.
|
|
8
|
+
|
|
9
|
+
> **Form 구성 시 내장 prop 사용을 권장합니다**
|
|
10
|
+
>
|
|
11
|
+
> 이 컴포넌트는 `label`, `helperText` 등의 Form 요소를 자체적으로 지원합니다.
|
|
12
|
+
> Form을 구성할 때 Typography로 별도의 label이나 helperText를 만드는 대신, 컴포넌트의 내장 prop을 사용하세요.
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
<RadioTileGroup
|
|
16
|
+
options={simpleOptions}
|
|
17
|
+
useIndicator
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Field | Description | Default |
|
|
22
|
+
| ------------ | ----------- | ------------- |
|
|
23
|
+
| options | — | simpleOptions |
|
|
24
|
+
| value | — | — |
|
|
25
|
+
| defaultValue | — | — |
|
|
26
|
+
| onChange | — | — |
|
|
27
|
+
| name | — | — |
|
|
28
|
+
| disabled | — | — |
|
|
29
|
+
| className | — | — |
|
|
30
|
+
| useIndicator | — | — |
|
|
31
|
+
| textAlign | — | — |
|
|
32
|
+
| size | — | — |
|
|
33
|
+
| flex | — | — |
|
|
34
|
+
| columns | — | — |
|
|
35
|
+
| label | — | — |
|
|
36
|
+
| helperText | — | — |
|
|
37
|
+
| error | — | — |
|
|
38
|
+
| required | — | — |
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import { RadioTileGroup } from '@ceed/cds';
|
|
44
|
+
|
|
45
|
+
function MyComponent() {
|
|
46
|
+
const [value, setValue] = useState('');
|
|
47
|
+
|
|
48
|
+
const options = [
|
|
49
|
+
{ value: 'option1', label: 'Option 1' },
|
|
50
|
+
{ value: 'option2', label: 'Option 2' },
|
|
51
|
+
{ value: 'option3', label: 'Option 3' },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<RadioTileGroup
|
|
56
|
+
label="Choose an option"
|
|
57
|
+
helperText="Select one of the options below"
|
|
58
|
+
options={options}
|
|
59
|
+
value={value}
|
|
60
|
+
onChange={(e) => setValue(e.target.value)}
|
|
61
|
+
useIndicator
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Label and Helper Text
|
|
68
|
+
|
|
69
|
+
Use the `label` and `helperText` props to add descriptive text above and below the tile group. The `required` prop appends a required indicator to the label.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
<RadioTileGroup
|
|
73
|
+
options={simpleOptions}
|
|
74
|
+
useIndicator
|
|
75
|
+
label="Select an option"
|
|
76
|
+
helperText="This is helper text for the RadioTileGroup"
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<RadioTileGroup
|
|
82
|
+
options={simpleOptions}
|
|
83
|
+
useIndicator
|
|
84
|
+
label="Select an option"
|
|
85
|
+
helperText="This field is required"
|
|
86
|
+
required
|
|
87
|
+
/>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Layout Options
|
|
91
|
+
|
|
92
|
+
### Flex
|
|
93
|
+
|
|
94
|
+
Enable the `flex` prop to make tiles stretch and fill all available horizontal space equally.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<RadioTileGroup
|
|
98
|
+
options={simpleOptions}
|
|
99
|
+
useIndicator
|
|
100
|
+
flex
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Columns
|
|
105
|
+
|
|
106
|
+
Use the `columns` prop to arrange tiles in a specific number of columns using a grid layout.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<RadioTileGroup
|
|
110
|
+
options={simpleOptions}
|
|
111
|
+
useIndicator
|
|
112
|
+
columns={2}
|
|
113
|
+
/>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Flex with Columns
|
|
117
|
+
|
|
118
|
+
Combine `flex` and `columns` to create a grid where each tile stretches to fill its column width evenly.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<Box sx={{
|
|
122
|
+
width: '100%',
|
|
123
|
+
maxWidth: 700
|
|
124
|
+
}}>
|
|
125
|
+
<Typography level="body-md" sx={{
|
|
126
|
+
mb: 2
|
|
127
|
+
}}>
|
|
128
|
+
Flex with Columns
|
|
129
|
+
</Typography>
|
|
130
|
+
<RadioTileGroup options={optionsWithIcons} onChange={handleChange} value={selectedValue} flex={true} columns={3} size="md" useIndicator={true} label="Flex with Columns Example" helperText="Items stretch to fill available space and are arranged in 3 columns" />
|
|
131
|
+
</Box>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Vertical Layout
|
|
135
|
+
|
|
136
|
+
Set `columns={1}` to stack tiles vertically. This layout is well-suited for list-style selections like pricing plans or step-by-step options.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Box sx={{
|
|
140
|
+
width: '100%',
|
|
141
|
+
maxWidth: 400
|
|
142
|
+
}}>
|
|
143
|
+
<Typography level="body-md" sx={{
|
|
144
|
+
mb: 2
|
|
145
|
+
}}>
|
|
146
|
+
Vertical Layout (columns: 1)
|
|
147
|
+
</Typography>
|
|
148
|
+
<RadioTileGroup options={pricingOptions} onChange={handleChange} value={selectedValue} columns={1} size="md" useIndicator={true} label="Subscription Plans" helperText="Select your preferred subscription plan" />
|
|
149
|
+
</Box>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Text Alignment
|
|
153
|
+
|
|
154
|
+
The `textAlign` prop controls the alignment of content inside each tile. The default is `center`. Set it to `start` for left-aligned content.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<Box sx={{
|
|
158
|
+
display: 'flex',
|
|
159
|
+
flexDirection: 'column',
|
|
160
|
+
gap: 4,
|
|
161
|
+
width: '100%',
|
|
162
|
+
maxWidth: 700
|
|
163
|
+
}}>
|
|
164
|
+
<Box>
|
|
165
|
+
<Typography level="body-md" sx={{
|
|
166
|
+
mb: 2
|
|
167
|
+
}}>
|
|
168
|
+
Center Alignment (default)
|
|
169
|
+
</Typography>
|
|
170
|
+
<RadioTileGroup options={simpleOptions} textAlign="center" useIndicator={true} />
|
|
171
|
+
</Box>
|
|
172
|
+
<Box>
|
|
173
|
+
<Typography level="body-md" sx={{
|
|
174
|
+
mb: 2
|
|
175
|
+
}}>
|
|
176
|
+
Start Alignment
|
|
177
|
+
</Typography>
|
|
178
|
+
<RadioTileGroup options={simpleOptions} textAlign="start" useIndicator={true} />
|
|
179
|
+
</Box>
|
|
180
|
+
</Box>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Sizes
|
|
184
|
+
|
|
185
|
+
RadioTileGroup supports three sizes: `sm` (default), `md`, and `lg`. Choose the size that best fits the density of your layout.
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<Box sx={{
|
|
189
|
+
display: 'flex',
|
|
190
|
+
flexDirection: 'column',
|
|
191
|
+
gap: 4,
|
|
192
|
+
width: '100%',
|
|
193
|
+
maxWidth: 700
|
|
194
|
+
}}>
|
|
195
|
+
<Box>
|
|
196
|
+
<Typography level="body-md" sx={{
|
|
197
|
+
mb: 2
|
|
198
|
+
}}>
|
|
199
|
+
Size: sm (default)
|
|
200
|
+
</Typography>
|
|
201
|
+
<RadioTileGroup options={simpleOptions} size="sm" useIndicator={true} />
|
|
202
|
+
</Box>
|
|
203
|
+
<Box>
|
|
204
|
+
<Typography level="body-md" sx={{
|
|
205
|
+
mb: 2
|
|
206
|
+
}}>
|
|
207
|
+
Size: md
|
|
208
|
+
</Typography>
|
|
209
|
+
<RadioTileGroup options={simpleOptions} size="md" useIndicator={true} />
|
|
210
|
+
</Box>
|
|
211
|
+
<Box>
|
|
212
|
+
<Typography level="body-md" sx={{
|
|
213
|
+
mb: 2
|
|
214
|
+
}}>
|
|
215
|
+
Size: lg
|
|
216
|
+
</Typography>
|
|
217
|
+
<RadioTileGroup options={simpleOptions} size="lg" useIndicator={true} />
|
|
218
|
+
</Box>
|
|
219
|
+
</Box>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Icons
|
|
223
|
+
|
|
224
|
+
Add icons to tiles using the `startDecorator` property on each option. Icons help users visually distinguish between choices at a glance.
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
<RadioTileGroup
|
|
228
|
+
options={[{
|
|
229
|
+
value: 'home',
|
|
230
|
+
label: 'Home',
|
|
231
|
+
startDecorator: <HomeIcon fontSize="large" />
|
|
232
|
+
}, {
|
|
233
|
+
value: 'office',
|
|
234
|
+
label: 'Office',
|
|
235
|
+
startDecorator: <BusinessIcon fontSize="large" />
|
|
236
|
+
}, {
|
|
237
|
+
value: 'shipping',
|
|
238
|
+
label: 'Shipping',
|
|
239
|
+
startDecorator: <LocalShippingIcon fontSize="large" />
|
|
240
|
+
}]}
|
|
241
|
+
useIndicator
|
|
242
|
+
/>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## State Management
|
|
246
|
+
|
|
247
|
+
### Controlled
|
|
248
|
+
|
|
249
|
+
Pass `value` and `onChange` to fully control the selected state from the parent component. This is recommended when the selection participates in form state or triggers side effects.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
<Box sx={{
|
|
253
|
+
width: '100%',
|
|
254
|
+
maxWidth: 500
|
|
255
|
+
}}>
|
|
256
|
+
<Typography level="body-md" sx={{
|
|
257
|
+
mb: 1
|
|
258
|
+
}}>
|
|
259
|
+
Selected value: {selectedValue}
|
|
260
|
+
</Typography>
|
|
261
|
+
<RadioTileGroup options={simpleOptions} value={selectedValue} onChange={handleChange} />
|
|
262
|
+
</Box>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Uncontrolled
|
|
266
|
+
|
|
267
|
+
Use `defaultValue` to set an initial selection without managing state externally. The component tracks its own selection internally.
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
<Box sx={{
|
|
271
|
+
display: 'flex',
|
|
272
|
+
flexDirection: 'column',
|
|
273
|
+
gap: 4
|
|
274
|
+
}}>
|
|
275
|
+
<Box sx={{
|
|
276
|
+
width: '100%',
|
|
277
|
+
maxWidth: 500
|
|
278
|
+
}}>
|
|
279
|
+
<Typography level="body-md" sx={{
|
|
280
|
+
mb: 2
|
|
281
|
+
}}>
|
|
282
|
+
Uncontrolled Example (with Indicator)
|
|
283
|
+
</Typography>
|
|
284
|
+
<RadioTileGroup options={simpleOptions} defaultValue="option2" useIndicator={true} onChange={event => console.log(`Selected value: ${event.target.value}`)} />
|
|
285
|
+
</Box>
|
|
286
|
+
<Box sx={{
|
|
287
|
+
width: '100%',
|
|
288
|
+
maxWidth: 500
|
|
289
|
+
}}>
|
|
290
|
+
<Typography level="body-md" sx={{
|
|
291
|
+
mb: 2
|
|
292
|
+
}}>
|
|
293
|
+
Uncontrolled Example (without Indicator)
|
|
294
|
+
</Typography>
|
|
295
|
+
<RadioTileGroup options={simpleOptions} defaultValue="option3" useIndicator={false} onChange={event => console.log(`Selected value: ${event.target.value}`)} />
|
|
296
|
+
</Box>
|
|
297
|
+
</Box>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Disabled State
|
|
301
|
+
|
|
302
|
+
### Fully Disabled
|
|
303
|
+
|
|
304
|
+
Set the `disabled` prop on the group to disable all tiles.
|
|
305
|
+
|
|
306
|
+
```tsx
|
|
307
|
+
<RadioTileGroup
|
|
308
|
+
options={simpleOptions}
|
|
309
|
+
disabled
|
|
310
|
+
/>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Individual Disabled
|
|
314
|
+
|
|
315
|
+
Set `disabled: true` on specific option objects to disable only those tiles while keeping the rest interactive.
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
<RadioTileGroup
|
|
319
|
+
options={[{
|
|
320
|
+
value: 'option1',
|
|
321
|
+
label: 'Option 1'
|
|
322
|
+
}, {
|
|
323
|
+
value: 'option2',
|
|
324
|
+
label: 'Option 2',
|
|
325
|
+
disabled: true
|
|
326
|
+
}, {
|
|
327
|
+
value: 'option3',
|
|
328
|
+
label: 'Option 3'
|
|
329
|
+
}]}
|
|
330
|
+
/>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Error State
|
|
334
|
+
|
|
335
|
+
Use the `error` prop to indicate validation errors. Pair it with `helperText` to communicate the issue to the user.
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
<RadioTileGroup
|
|
339
|
+
options={simpleOptions}
|
|
340
|
+
useIndicator
|
|
341
|
+
label="Select an option"
|
|
342
|
+
helperText="Please select a valid option"
|
|
343
|
+
error
|
|
344
|
+
/>
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Form Validation Example
|
|
348
|
+
|
|
349
|
+
Combine `required`, `error`, and `helperText` to build a complete form validation flow. The example below validates that the user has made a selection before submitting.
|
|
350
|
+
|
|
351
|
+
```tsx
|
|
352
|
+
<Stack spacing={2} sx={{
|
|
353
|
+
width: '100%',
|
|
354
|
+
maxWidth: 500
|
|
355
|
+
}}>
|
|
356
|
+
<RadioTileGroup options={simpleOptions} value={selectedValue} onChange={handleChange} label="Choose your preferred option" helperText={error ? 'Please select an option' : 'This selection is required'} error={error} useIndicator={true} required={true} />
|
|
357
|
+
<Box sx={{
|
|
358
|
+
display: 'flex',
|
|
359
|
+
justifyContent: 'flex-end'
|
|
360
|
+
}}>
|
|
361
|
+
<Button onClick={handleSubmit}>Submit</Button>
|
|
362
|
+
</Box>
|
|
363
|
+
</Stack>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Common Use Cases
|
|
367
|
+
|
|
368
|
+
### Shipping Method Selection
|
|
369
|
+
|
|
370
|
+
Display delivery options with icons so users can choose intuitively.
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
import { RadioTileGroup } from '@ceed/cds';
|
|
374
|
+
import HomeIcon from '@mui/icons-material/Home';
|
|
375
|
+
import LocalShippingIcon from '@mui/icons-material/LocalShipping';
|
|
376
|
+
import BusinessIcon from '@mui/icons-material/Business';
|
|
377
|
+
|
|
378
|
+
<RadioTileGroup
|
|
379
|
+
label="Shipping Method"
|
|
380
|
+
helperText="Delivery fees may vary by method."
|
|
381
|
+
options={[
|
|
382
|
+
{ value: 'standard', label: 'Standard', startDecorator: <HomeIcon /> },
|
|
383
|
+
{ value: 'express', label: 'Express', startDecorator: <LocalShippingIcon /> },
|
|
384
|
+
{ value: 'business', label: 'Business', startDecorator: <BusinessIcon /> },
|
|
385
|
+
]}
|
|
386
|
+
value={selected}
|
|
387
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
388
|
+
size="md"
|
|
389
|
+
useIndicator
|
|
390
|
+
/>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
<Box sx={{
|
|
395
|
+
width: '100%',
|
|
396
|
+
maxWidth: 700
|
|
397
|
+
}}>
|
|
398
|
+
<RadioTileGroup label="배송 방법 선택" helperText="배송 방법에 따라 배송비가 달라질 수 있습니다." options={[{
|
|
399
|
+
value: 'standard',
|
|
400
|
+
label: '일반 배송',
|
|
401
|
+
startDecorator: <HomeIcon />
|
|
402
|
+
}, {
|
|
403
|
+
value: 'express',
|
|
404
|
+
label: '빠른 배송',
|
|
405
|
+
startDecorator: <LocalShippingIcon />
|
|
406
|
+
}, {
|
|
407
|
+
value: 'business',
|
|
408
|
+
label: '기업 배송',
|
|
409
|
+
startDecorator: <BusinessIcon />
|
|
410
|
+
}]} onChange={handleChange} value={selectedValue} size="md" useIndicator={true} />
|
|
411
|
+
</Box>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Survey / Preference Selection
|
|
415
|
+
|
|
416
|
+
Present survey options with icons in a multi-column grid for a clear, scannable layout.
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
<RadioTileGroup
|
|
420
|
+
label="What type of exercise do you prefer?"
|
|
421
|
+
options={[
|
|
422
|
+
{ value: 'cardio', label: 'Cardio', startDecorator: <DirectionsRunIcon /> },
|
|
423
|
+
{ value: 'strength', label: 'Strength', startDecorator: <FitnessCenterIcon /> },
|
|
424
|
+
{ value: 'flexibility', label: 'Flexibility', startDecorator: <SelfImprovementIcon /> },
|
|
425
|
+
{ value: 'balance', label: 'Balance', startDecorator: <BalanceIcon /> },
|
|
426
|
+
]}
|
|
427
|
+
columns={2}
|
|
428
|
+
size="md"
|
|
429
|
+
value={selected}
|
|
430
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
431
|
+
/>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
<Box sx={{
|
|
436
|
+
width: '100%',
|
|
437
|
+
maxWidth: 700
|
|
438
|
+
}}>
|
|
439
|
+
<RadioTileGroup label="선호하는 운동 유형을 선택해주세요" options={[{
|
|
440
|
+
value: 'cardio',
|
|
441
|
+
label: '유산소 운동',
|
|
442
|
+
startDecorator: <DirectionsRunIcon />
|
|
443
|
+
}, {
|
|
444
|
+
value: 'strength',
|
|
445
|
+
label: '근력 운동',
|
|
446
|
+
startDecorator: <FitnessCenterIcon />
|
|
447
|
+
}, {
|
|
448
|
+
value: 'flexibility',
|
|
449
|
+
label: '유연성 운동',
|
|
450
|
+
startDecorator: <SelfImprovementIcon />
|
|
451
|
+
}, {
|
|
452
|
+
value: 'balance',
|
|
453
|
+
label: '균형 운동',
|
|
454
|
+
startDecorator: <BalanceIcon />
|
|
455
|
+
}]} onChange={handleChange} value={selectedValue} columns={2} size="md" />
|
|
456
|
+
</Box>
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Pricing Plan Picker
|
|
460
|
+
|
|
461
|
+
Use a vertical layout with `columns={1}` for plan or tier selection.
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
<RadioTileGroup
|
|
465
|
+
label="Subscription Plan"
|
|
466
|
+
helperText="Select your preferred plan"
|
|
467
|
+
options={[
|
|
468
|
+
{ value: 'basic', label: 'Basic Plan', startDecorator: <AttachMoneyIcon /> },
|
|
469
|
+
{ value: 'pro', label: 'Pro Plan', startDecorator: <AttachMoneyIcon /> },
|
|
470
|
+
{ value: 'premium', label: 'Premium Plan', startDecorator: <StarIcon /> },
|
|
471
|
+
]}
|
|
472
|
+
columns={1}
|
|
473
|
+
size="md"
|
|
474
|
+
useIndicator
|
|
475
|
+
value={selected}
|
|
476
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
477
|
+
/>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Best Practices
|
|
481
|
+
|
|
482
|
+
1. **Use built-in form props instead of external labels.** The component's `label`, `helperText`, `required`, and `error` props ensure consistent layout and accessibility. Avoid wrapping with separate Typography elements.
|
|
483
|
+
|
|
484
|
+
```tsx
|
|
485
|
+
// ✅ Recommended
|
|
486
|
+
<RadioTileGroup label="Select option" helperText="Required field" required />
|
|
487
|
+
|
|
488
|
+
// ❌ Avoid
|
|
489
|
+
<Typography>Select option</Typography>
|
|
490
|
+
<RadioTileGroup options={options} />
|
|
491
|
+
<Typography color="danger">Required field</Typography>
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
2. **Choose the right layout for the number of options.** Use the default horizontal flow for 2-4 options. Use `columns` for 5+ options. Use `columns={1}` when each option has long labels or descriptions.
|
|
495
|
+
|
|
496
|
+
3. **Prefer controlled mode in forms.** When RadioTileGroup is part of a form with validation or submission logic, use `value` and `onChange` for predictable state management.
|
|
497
|
+
|
|
498
|
+
4. **Add icons only when they aid comprehension.** Icons via `startDecorator` are helpful for visually distinct categories (e.g., shipping methods). Avoid decorating every option if icons do not add meaning.
|
|
499
|
+
|
|
500
|
+
5. **Always provide an error message.** When `error` is true, update `helperText` to explain the validation issue so users know how to fix it.
|
|
501
|
+
|
|
502
|
+
## Accessibility
|
|
503
|
+
|
|
504
|
+
- RadioTileGroup renders a `<fieldset>` with a `<legend>` derived from the `label` prop, providing proper grouping for screen readers.
|
|
505
|
+
- Each tile is backed by a native `<input type="radio">`, ensuring full keyboard navigation (arrow keys to move between options, Space/Enter to select).
|
|
506
|
+
- The `disabled` prop correctly applies `aria-disabled` semantics to prevent interaction while keeping elements discoverable by assistive technologies.
|
|
507
|
+
- When `error` is set, pair it with descriptive `helperText` so that screen readers can announce the validation message via `aria-describedby`.
|