@ceed/ads 1.23.3 → 1.23.5
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/components/data-display/Badge.md +71 -39
- package/dist/components/data-display/InfoSign.md +74 -98
- package/dist/components/data-display/Typography.md +310 -61
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/ButtonGroup.md +115 -106
- package/dist/components/inputs/Calendar.md +98 -459
- package/dist/components/inputs/CurrencyInput.md +181 -8
- package/dist/components/inputs/DatePicker.md +108 -436
- package/dist/components/inputs/DateRangePicker.md +130 -496
- package/dist/components/inputs/FilterMenu.md +169 -19
- package/dist/components/inputs/FilterableCheckboxGroup.md +119 -24
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/MonthPicker.md +95 -427
- package/dist/components/inputs/MonthRangePicker.md +89 -471
- package/dist/components/inputs/PercentageInput.md +183 -19
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +146 -62
- package/dist/components/inputs/Select.md +219 -328
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +136 -376
- package/dist/components/inputs/Textarea.md +209 -11
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -0
- package/dist/components/navigation/Breadcrumbs.md +80 -322
- package/dist/components/navigation/Dropdown.md +92 -221
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +68 -738
- package/dist/components/navigation/Link.md +39 -298
- 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/ProfileMenu.md +45 -268
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Sheet.md +150 -333
- package/dist/guides/ThemeProvider.md +116 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/llms.txt +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# FormControl
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
FormControl is a wrapper component that provides context to form elements such as Input, Textarea, Select, and more. It manages shared states like `error`, `disabled`, `required`, and `size`, passing them down to its children automatically. Use it with FormLabel and FormHelperText to build accessible, consistent form fields.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<FormControl {...args}>
|
|
9
|
+
<FormLabel>Label</FormLabel>
|
|
10
|
+
<Input placeholder="Enter text…" />
|
|
11
|
+
<FormHelperText>This is helper text.</FormHelperText>
|
|
12
|
+
</FormControl>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Field | Description | Default |
|
|
16
|
+
| ----------- | ----------- | ------- |
|
|
17
|
+
| size | — | — |
|
|
18
|
+
| error | — | — |
|
|
19
|
+
| disabled | — | — |
|
|
20
|
+
| required | — | — |
|
|
21
|
+
| orientation | — | — |
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { FormControl, FormLabel, FormHelperText, Input } from '@ceed/ads';
|
|
27
|
+
|
|
28
|
+
function MyForm() {
|
|
29
|
+
return (
|
|
30
|
+
<FormControl>
|
|
31
|
+
<FormLabel>Username</FormLabel>
|
|
32
|
+
<Input placeholder="Enter username" />
|
|
33
|
+
<FormHelperText>Choose a unique username.</FormHelperText>
|
|
34
|
+
</FormControl>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## With Input
|
|
40
|
+
|
|
41
|
+
The most common pattern — wrapping an Input with a label and helper text.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<FormControl>
|
|
45
|
+
<FormLabel>Username</FormLabel>
|
|
46
|
+
<Input placeholder="Enter username" />
|
|
47
|
+
<FormHelperText>Choose a unique username.</FormHelperText>
|
|
48
|
+
</FormControl>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## With Textarea
|
|
52
|
+
|
|
53
|
+
FormControl works equally well with Textarea components.
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<FormControl>
|
|
57
|
+
<FormLabel>Description</FormLabel>
|
|
58
|
+
<Textarea placeholder="Enter description…" minRows={3} />
|
|
59
|
+
<FormHelperText>Provide a detailed description.</FormHelperText>
|
|
60
|
+
</FormControl>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## With Select
|
|
64
|
+
|
|
65
|
+
Use FormControl with Select for dropdown fields.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<FormControl>
|
|
69
|
+
<FormLabel>Role</FormLabel>
|
|
70
|
+
<Select placeholder="Select a role" options={roleOptions} />
|
|
71
|
+
<FormHelperText>Select the user role.</FormHelperText>
|
|
72
|
+
</FormControl>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Error State
|
|
76
|
+
|
|
77
|
+
Set `error` on FormControl to propagate the error state to all child components. FormHelperText automatically changes color to indicate the error.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<FormControl error>
|
|
81
|
+
<FormLabel>Email</FormLabel>
|
|
82
|
+
<Input placeholder="email@example.com" defaultValue="invalid-email" />
|
|
83
|
+
<FormHelperText>Please enter a valid email address.</FormHelperText>
|
|
84
|
+
</FormControl>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<FormControl error>
|
|
89
|
+
<FormLabel>Email</FormLabel>
|
|
90
|
+
<Input placeholder="email@example.com" value="invalid-email" />
|
|
91
|
+
<FormHelperText>Please enter a valid email address.</FormHelperText>
|
|
92
|
+
</FormControl>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Disabled State
|
|
96
|
+
|
|
97
|
+
Set `disabled` to disable all child form elements at once.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<FormControl disabled>
|
|
101
|
+
<FormLabel>Name</FormLabel>
|
|
102
|
+
<Input placeholder="Enter name" defaultValue="John Doe" />
|
|
103
|
+
<FormHelperText>This field is disabled.</FormHelperText>
|
|
104
|
+
</FormControl>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<FormControl disabled>
|
|
109
|
+
<FormLabel>Name</FormLabel>
|
|
110
|
+
<Input placeholder="Enter name" value="John Doe" />
|
|
111
|
+
<FormHelperText>This field is disabled.</FormHelperText>
|
|
112
|
+
</FormControl>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Required Field
|
|
116
|
+
|
|
117
|
+
Set `required` to add an asterisk (\*) to the label and mark the field as required.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
<FormControl required>
|
|
121
|
+
<FormLabel>Full Name</FormLabel>
|
|
122
|
+
<Input placeholder="Enter your full name" />
|
|
123
|
+
<FormHelperText>This field is required.</FormHelperText>
|
|
124
|
+
</FormControl>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
<FormControl required>
|
|
129
|
+
<FormLabel>Full Name</FormLabel>
|
|
130
|
+
<Input placeholder="Enter your full name" />
|
|
131
|
+
</FormControl>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Sizes
|
|
135
|
+
|
|
136
|
+
FormControl supports `sm`, `md`, and `lg` sizes. The size is inherited by child components.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Stack gap={3}>
|
|
140
|
+
<FormControl size="sm">
|
|
141
|
+
<FormLabel>Small</FormLabel>
|
|
142
|
+
<Input placeholder="Small input" />
|
|
143
|
+
</FormControl>
|
|
144
|
+
<FormControl size="md">
|
|
145
|
+
<FormLabel>Medium</FormLabel>
|
|
146
|
+
<Input placeholder="Medium input" />
|
|
147
|
+
</FormControl>
|
|
148
|
+
<FormControl size="lg">
|
|
149
|
+
<FormLabel>Large</FormLabel>
|
|
150
|
+
<Input placeholder="Large input" />
|
|
151
|
+
</FormControl>
|
|
152
|
+
</Stack>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Horizontal Layout
|
|
156
|
+
|
|
157
|
+
Use `orientation="horizontal"` for inline form controls, such as Switch or Checkbox toggles.
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
<FormControl orientation="horizontal" sx={{
|
|
161
|
+
gap: 2
|
|
162
|
+
}}>
|
|
163
|
+
<FormLabel>Subscribe</FormLabel>
|
|
164
|
+
<Switch />
|
|
165
|
+
</FormControl>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<FormControl orientation="horizontal" sx={{ gap: 2 }}>
|
|
170
|
+
<FormLabel>Subscribe</FormLabel>
|
|
171
|
+
<Switch />
|
|
172
|
+
</FormControl>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Form Example
|
|
176
|
+
|
|
177
|
+
A complete form demonstrating FormControl with multiple input types.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<Stack gap={2} sx={{
|
|
181
|
+
maxWidth: 400
|
|
182
|
+
}}>
|
|
183
|
+
<FormControl required>
|
|
184
|
+
<FormLabel>Name</FormLabel>
|
|
185
|
+
<Input placeholder="Enter your name" />
|
|
186
|
+
</FormControl>
|
|
187
|
+
<FormControl required>
|
|
188
|
+
<FormLabel>Email</FormLabel>
|
|
189
|
+
<Input type="email" placeholder="email@example.com" />
|
|
190
|
+
<FormHelperText>We will never share your email.</FormHelperText>
|
|
191
|
+
</FormControl>
|
|
192
|
+
<FormControl>
|
|
193
|
+
<FormLabel>Role</FormLabel>
|
|
194
|
+
<Select placeholder="Select a role" options={roleOptions} />
|
|
195
|
+
</FormControl>
|
|
196
|
+
<FormControl>
|
|
197
|
+
<FormLabel>Bio</FormLabel>
|
|
198
|
+
<Textarea placeholder="Tell us about yourself…" minRows={3} />
|
|
199
|
+
<FormHelperText>Maximum 500 characters.</FormHelperText>
|
|
200
|
+
</FormControl>
|
|
201
|
+
</Stack>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## FormLabel
|
|
205
|
+
|
|
206
|
+
FormLabel renders a `<label>` element associated with its sibling input. It automatically displays an asterisk when the parent FormControl has `required` set.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<FormControl required>
|
|
210
|
+
<FormLabel>Email</FormLabel>
|
|
211
|
+
<Input />
|
|
212
|
+
</FormControl>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## FormHelperText
|
|
216
|
+
|
|
217
|
+
FormHelperText provides supplementary guidance below the input. It automatically switches to the error color when the parent FormControl has `error` set.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<FormControl error>
|
|
221
|
+
<FormLabel>Password</FormLabel>
|
|
222
|
+
<Input type="password" />
|
|
223
|
+
<FormHelperText>Password must be at least 8 characters.</FormHelperText>
|
|
224
|
+
</FormControl>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Common Use Cases
|
|
228
|
+
|
|
229
|
+
### Registration Form
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
function RegistrationForm() {
|
|
233
|
+
const [errors, setErrors] = React.useState({});
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Stack gap={2} component="form">
|
|
237
|
+
<FormControl required error={!!errors.name}>
|
|
238
|
+
<FormLabel>Name</FormLabel>
|
|
239
|
+
<Input placeholder="Enter your name" />
|
|
240
|
+
{errors.name && <FormHelperText>{errors.name}</FormHelperText>}
|
|
241
|
+
</FormControl>
|
|
242
|
+
|
|
243
|
+
<FormControl required error={!!errors.email}>
|
|
244
|
+
<FormLabel>Email</FormLabel>
|
|
245
|
+
<Input type="email" placeholder="email@example.com" />
|
|
246
|
+
{errors.email && <FormHelperText>{errors.email}</FormHelperText>}
|
|
247
|
+
</FormControl>
|
|
248
|
+
|
|
249
|
+
<FormControl required error={!!errors.password}>
|
|
250
|
+
<FormLabel>Password</FormLabel>
|
|
251
|
+
<Input type="password" placeholder="Enter password" />
|
|
252
|
+
<FormHelperText>{errors.password || 'Must be at least 8 characters.'}</FormHelperText>
|
|
253
|
+
</FormControl>
|
|
254
|
+
|
|
255
|
+
<FormControl orientation="horizontal">
|
|
256
|
+
<Checkbox label="I agree to the terms and conditions" />
|
|
257
|
+
</FormControl>
|
|
258
|
+
</Stack>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Settings Form with Mixed Controls
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
function SettingsForm() {
|
|
267
|
+
return (
|
|
268
|
+
<Stack gap={2}>
|
|
269
|
+
<FormControl orientation="horizontal" sx={{ justifyContent: 'space-between' }}>
|
|
270
|
+
<FormLabel>Email notifications</FormLabel>
|
|
271
|
+
<Switch />
|
|
272
|
+
</FormControl>
|
|
273
|
+
|
|
274
|
+
<FormControl>
|
|
275
|
+
<FormLabel>Language</FormLabel>
|
|
276
|
+
<Select
|
|
277
|
+
defaultValue="en"
|
|
278
|
+
options={[
|
|
279
|
+
{ value: 'en', label: 'English' },
|
|
280
|
+
{ value: 'ko', label: 'Korean' },
|
|
281
|
+
]}
|
|
282
|
+
/>
|
|
283
|
+
</FormControl>
|
|
284
|
+
|
|
285
|
+
<FormControl>
|
|
286
|
+
<FormLabel>Notes</FormLabel>
|
|
287
|
+
<Textarea minRows={3} placeholder="Additional notes…" />
|
|
288
|
+
<FormHelperText>Optional</FormHelperText>
|
|
289
|
+
</FormControl>
|
|
290
|
+
</Stack>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
> **Tip: Use built-in form props instead**
|
|
296
|
+
>
|
|
297
|
+
> Input, Select, Textarea, DatePicker 등 Input 계열 컴포넌트는 `label`, `helperText` prop을 자체적으로 지원합니다.
|
|
298
|
+
> 간단한 form 필드에는 각 컴포넌트의 내장 prop을 사용하는 것이 더 간결합니다.
|
|
299
|
+
>
|
|
300
|
+
> ```tsx
|
|
301
|
+
> // ✅ Simpler: built-in props
|
|
302
|
+
> <Input label="Username" helperText="Choose a unique username." />
|
|
303
|
+
>
|
|
304
|
+
> // ⬆️ Equivalent to:
|
|
305
|
+
> <FormControl>
|
|
306
|
+
> <FormLabel>Username</FormLabel>
|
|
307
|
+
> <Input placeholder="Enter username" />
|
|
308
|
+
> <FormHelperText>Choose a unique username.</FormHelperText>
|
|
309
|
+
> </FormControl>
|
|
310
|
+
> ```
|
|
311
|
+
>
|
|
312
|
+
> FormControl is most useful when you need to compose multiple elements, share state across siblings, or use horizontal orientation.
|
|
313
|
+
|
|
314
|
+
## Best Practices
|
|
315
|
+
|
|
316
|
+
1. **Always pair with FormLabel**: Every form field should have a label for accessibility. Use FormLabel inside FormControl.
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
// ✅ Accessible — label is associated with input
|
|
320
|
+
<FormControl>
|
|
321
|
+
<FormLabel>Email</FormLabel>
|
|
322
|
+
<Input />
|
|
323
|
+
</FormControl>
|
|
324
|
+
|
|
325
|
+
// ❌ No label — screen readers cannot identify the input
|
|
326
|
+
<FormControl>
|
|
327
|
+
<Input placeholder="Email" />
|
|
328
|
+
</FormControl>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
2. **Use `error` prop on FormControl, not children**: Set error state on FormControl so all children react consistently.
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
// ✅ Error propagated from FormControl
|
|
335
|
+
<FormControl error>
|
|
336
|
+
<FormLabel>Email</FormLabel>
|
|
337
|
+
<Input />
|
|
338
|
+
<FormHelperText>Invalid email</FormHelperText>
|
|
339
|
+
</FormControl>
|
|
340
|
+
|
|
341
|
+
// ❌ Inconsistent — only Input shows error
|
|
342
|
+
<FormControl>
|
|
343
|
+
<FormLabel>Email</FormLabel>
|
|
344
|
+
<Input error />
|
|
345
|
+
<FormHelperText>Invalid email</FormHelperText>
|
|
346
|
+
</FormControl>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
3. **Use consistent sizing**: Set `size` on FormControl to ensure all children (label, input, helper text) are proportionally sized.
|
|
350
|
+
|
|
351
|
+
4. **Mark required fields**: Use the `required` prop to automatically add visual indicators and `aria-required` attributes.
|
|
352
|
+
|
|
353
|
+
5. **Provide helpful error messages**: When using the error state, always include a FormHelperText explaining what went wrong and how to fix it.
|
|
354
|
+
|
|
355
|
+
## Accessibility
|
|
356
|
+
|
|
357
|
+
- FormControl automatically associates FormLabel with the input using `htmlFor` and `id`.
|
|
358
|
+
- The `required` prop adds `aria-required="true"` to the input and an asterisk to the label.
|
|
359
|
+
- The `error` prop adds `aria-invalid="true"` to the input.
|
|
360
|
+
- FormHelperText is linked to the input via `aria-describedby` so screen readers announce it.
|
|
361
|
+
- Use the `disabled` prop on FormControl to set `aria-disabled` on child elements.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
IconButton
|
|
5
|
+
IconButton is a compact, icon-only button component designed for actions where a visual symbol alone is sufficient to communicate meaning. It is ideal for toolbars, table row actions, card headers, navigation bars, and any context where space efficiency is critical.
|
|
6
|
+
|
|
7
|
+
Because IconButton omits visible text, it relies entirely on the icon's clarity and supplementary accessibility attributes to convey its purpose. Always pair it with `aria-label` or a Tooltip to ensure all users can understand the button's function.
|
|
6
8
|
|
|
7
9
|
```tsx
|
|
8
10
|
<IconButton {...args} />
|
|
@@ -22,56 +24,26 @@ IconButton 컴포넌트는 아이콘만으로 구성된 버튼입니다. 공간
|
|
|
22
24
|
```tsx
|
|
23
25
|
import { IconButton } from '@ceed/ads';
|
|
24
26
|
import AddIcon from '@mui/icons-material/Add';
|
|
27
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
25
28
|
|
|
26
29
|
function MyComponent() {
|
|
27
30
|
return (
|
|
28
|
-
<div>
|
|
29
|
-
<IconButton onClick={
|
|
31
|
+
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
32
|
+
<IconButton aria-label="Add item" onClick={handleAdd}>
|
|
30
33
|
<AddIcon />
|
|
31
34
|
</IconButton>
|
|
32
35
|
|
|
33
|
-
<IconButton color="danger" onClick={
|
|
36
|
+
<IconButton color="danger" aria-label="Delete item" onClick={handleDelete}>
|
|
34
37
|
<DeleteIcon />
|
|
35
38
|
</IconButton>
|
|
36
|
-
|
|
37
|
-
<IconButton disabled>
|
|
38
|
-
<EditIcon />
|
|
39
|
-
</IconButton>
|
|
40
39
|
</div>
|
|
41
40
|
);
|
|
42
41
|
}
|
|
43
42
|
```
|
|
44
43
|
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
### Basic Usage
|
|
48
|
-
|
|
49
|
-
기본적인 IconButton 사용법입니다.
|
|
50
|
-
|
|
51
|
-
```tsx
|
|
52
|
-
<div style={{
|
|
53
|
-
display: 'flex',
|
|
54
|
-
gap: '1rem',
|
|
55
|
-
alignItems: 'center'
|
|
56
|
-
}}>
|
|
57
|
-
<IconButton>
|
|
58
|
-
<Add />
|
|
59
|
-
</IconButton>
|
|
60
|
-
<IconButton>
|
|
61
|
-
<Edit />
|
|
62
|
-
</IconButton>
|
|
63
|
-
<IconButton>
|
|
64
|
-
<Delete />
|
|
65
|
-
</IconButton>
|
|
66
|
-
<IconButton>
|
|
67
|
-
<Settings />
|
|
68
|
-
</IconButton>
|
|
69
|
-
</div>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Colors
|
|
44
|
+
## Colors
|
|
73
45
|
|
|
74
|
-
|
|
46
|
+
IconButton supports five semantic colors: `primary` (default), `neutral`, `danger`, `success`, and `warning`. Choose a color that reflects the nature of the action.
|
|
75
47
|
|
|
76
48
|
```tsx
|
|
77
49
|
<div style={{
|
|
@@ -97,9 +69,9 @@ function MyComponent() {
|
|
|
97
69
|
</div>
|
|
98
70
|
```
|
|
99
71
|
|
|
100
|
-
|
|
72
|
+
## Variants
|
|
101
73
|
|
|
102
|
-
|
|
74
|
+
Four visual variants are available: `solid` (filled background), `soft` (tinted background), `outlined` (border only), and `plain` (no background). Use `outlined` or `plain` for secondary actions, and `solid` for primary or high-emphasis actions.
|
|
103
75
|
|
|
104
76
|
```tsx
|
|
105
77
|
<div style={{
|
|
@@ -122,9 +94,9 @@ function MyComponent() {
|
|
|
122
94
|
</div>
|
|
123
95
|
```
|
|
124
96
|
|
|
125
|
-
|
|
97
|
+
## Sizes
|
|
126
98
|
|
|
127
|
-
|
|
99
|
+
The `size` prop controls the button dimensions. Available sizes are `sm`, `md` (default), and `lg`. Ensure touch targets meet the minimum 44px recommended size for touch interfaces.
|
|
128
100
|
|
|
129
101
|
```tsx
|
|
130
102
|
<div style={{
|
|
@@ -144,9 +116,9 @@ function MyComponent() {
|
|
|
144
116
|
</div>
|
|
145
117
|
```
|
|
146
118
|
|
|
147
|
-
|
|
119
|
+
## States
|
|
148
120
|
|
|
149
|
-
|
|
121
|
+
IconButton supports multiple interactive states including normal, disabled, and loading. Use `disabled` to prevent interaction and `loading` to indicate an in-progress async operation.
|
|
150
122
|
|
|
151
123
|
```tsx
|
|
152
124
|
<div style={{
|
|
@@ -202,9 +174,9 @@ function MyComponent() {
|
|
|
202
174
|
</div>
|
|
203
175
|
```
|
|
204
176
|
|
|
205
|
-
|
|
177
|
+
## With Badge
|
|
206
178
|
|
|
207
|
-
Badge
|
|
179
|
+
Combine IconButton with Badge to display notification counts, status indicators, or unread markers alongside the button.
|
|
208
180
|
|
|
209
181
|
```tsx
|
|
210
182
|
<div style={{
|
|
@@ -230,9 +202,9 @@ Badge와 함께 사용하여 알림 수나 상태를 표시할 수 있습니다.
|
|
|
230
202
|
</div>
|
|
231
203
|
```
|
|
232
204
|
|
|
233
|
-
|
|
205
|
+
## With Tooltip
|
|
234
206
|
|
|
235
|
-
Tooltip
|
|
207
|
+
Wrap IconButton in a Tooltip to provide a text description on hover. This is especially important for icon-only buttons since the icon alone may not be self-explanatory for all users.
|
|
236
208
|
|
|
237
209
|
```tsx
|
|
238
210
|
<div style={{
|
|
@@ -263,9 +235,40 @@ Tooltip과 함께 사용하여 버튼의 기능을 설명할 수 있습니다.
|
|
|
263
235
|
</div>
|
|
264
236
|
```
|
|
265
237
|
|
|
266
|
-
|
|
238
|
+
## Circular Variants
|
|
239
|
+
|
|
240
|
+
IconButton renders as a circular shape by default, making it visually distinct from rectangular text buttons and well-suited for floating action buttons and compact action rows.
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
<div style={{
|
|
244
|
+
display: 'flex',
|
|
245
|
+
gap: '2rem',
|
|
246
|
+
alignItems: 'center'
|
|
247
|
+
}}>
|
|
248
|
+
<div>
|
|
249
|
+
<h4>Default</h4>
|
|
250
|
+
<div style={{
|
|
251
|
+
display: 'flex',
|
|
252
|
+
gap: '1rem',
|
|
253
|
+
alignItems: 'center'
|
|
254
|
+
}}>
|
|
255
|
+
<IconButton>
|
|
256
|
+
<Add />
|
|
257
|
+
</IconButton>
|
|
258
|
+
<IconButton variant="outlined">
|
|
259
|
+
<Edit />
|
|
260
|
+
</IconButton>
|
|
261
|
+
<IconButton variant="soft">
|
|
262
|
+
<Delete />
|
|
263
|
+
</IconButton>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Action Buttons
|
|
267
270
|
|
|
268
|
-
|
|
271
|
+
A practical example of IconButton used for common CRUD operations with distinct colors signaling the nature of each action.
|
|
269
272
|
|
|
270
273
|
```tsx
|
|
271
274
|
<div style={{
|
|
@@ -293,69 +296,115 @@ Tooltip과 함께 사용하여 버튼의 기능을 설명할 수 있습니다.
|
|
|
293
296
|
|
|
294
297
|
## Common Use Cases
|
|
295
298
|
|
|
296
|
-
###
|
|
299
|
+
### Table Row Actions
|
|
297
300
|
|
|
298
301
|
```tsx
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
302
|
+
import { IconButton, Tooltip } from '@ceed/ads';
|
|
303
|
+
import EditIcon from '@mui/icons-material/Edit';
|
|
304
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
305
|
+
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
306
|
+
|
|
307
|
+
function RowActions({ onView, onEdit, onDelete }) {
|
|
308
|
+
return (
|
|
309
|
+
<div style={{ display: 'flex', gap: '0.25rem' }}>
|
|
310
|
+
<Tooltip title="View details">
|
|
311
|
+
<IconButton size="sm" variant="plain" color="neutral" onClick={onView}>
|
|
312
|
+
<VisibilityIcon />
|
|
313
|
+
</IconButton>
|
|
314
|
+
</Tooltip>
|
|
315
|
+
<Tooltip title="Edit">
|
|
316
|
+
<IconButton size="sm" variant="plain" color="neutral" onClick={onEdit}>
|
|
317
|
+
<EditIcon />
|
|
318
|
+
</IconButton>
|
|
319
|
+
</Tooltip>
|
|
320
|
+
<Tooltip title="Delete">
|
|
321
|
+
<IconButton size="sm" variant="plain" color="danger" onClick={onDelete}>
|
|
322
|
+
<DeleteIcon />
|
|
323
|
+
</IconButton>
|
|
324
|
+
</Tooltip>
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
310
328
|
```
|
|
311
329
|
|
|
312
|
-
###
|
|
330
|
+
### Dialog Close Button
|
|
313
331
|
|
|
314
332
|
```tsx
|
|
315
|
-
|
|
316
|
-
|
|
333
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
334
|
+
|
|
335
|
+
<IconButton
|
|
336
|
+
variant="plain"
|
|
337
|
+
color="neutral"
|
|
338
|
+
size="sm"
|
|
339
|
+
aria-label="Close dialog"
|
|
340
|
+
onClick={onClose}
|
|
341
|
+
sx={{ position: 'absolute', top: 8, right: 8 }}
|
|
342
|
+
>
|
|
343
|
+
<CloseIcon />
|
|
317
344
|
</IconButton>
|
|
318
345
|
```
|
|
319
346
|
|
|
320
|
-
###
|
|
347
|
+
### Navigation Back Button
|
|
321
348
|
|
|
322
349
|
```tsx
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
350
|
+
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|
351
|
+
|
|
352
|
+
<IconButton
|
|
353
|
+
variant="outlined"
|
|
354
|
+
color="neutral"
|
|
355
|
+
aria-label="Go back"
|
|
356
|
+
onClick={() => router.back()}
|
|
357
|
+
>
|
|
358
|
+
<ArrowBackIcon />
|
|
328
359
|
</IconButton>
|
|
329
360
|
```
|
|
330
361
|
|
|
331
|
-
|
|
362
|
+
## Best Practices
|
|
363
|
+
|
|
364
|
+
1. **Always provide an accessible label.** Since there is no visible text, use `aria-label` or wrap with a Tooltip to describe the action.
|
|
332
365
|
|
|
333
366
|
```tsx
|
|
334
|
-
|
|
335
|
-
|
|
367
|
+
// ✅ Accessible icon button
|
|
368
|
+
<IconButton aria-label="Delete item" color="danger">
|
|
369
|
+
<DeleteIcon />
|
|
336
370
|
</IconButton>
|
|
337
|
-
|
|
338
|
-
|
|
371
|
+
|
|
372
|
+
// ❌ No accessible label
|
|
373
|
+
<IconButton color="danger">
|
|
374
|
+
<DeleteIcon />
|
|
339
375
|
</IconButton>
|
|
340
376
|
```
|
|
341
377
|
|
|
342
|
-
|
|
378
|
+
2. **Use semantic colors to convey intent.** Match the color to the action's nature: `danger` for destructive actions, `success` for confirmations, `neutral` for general-purpose actions.
|
|
343
379
|
|
|
344
|
-
|
|
380
|
+
```tsx
|
|
381
|
+
// ✅ Color matches the action
|
|
382
|
+
<IconButton color="danger" aria-label="Delete">
|
|
383
|
+
<DeleteIcon />
|
|
384
|
+
</IconButton>
|
|
345
385
|
|
|
346
|
-
|
|
386
|
+
// ❌ Misleading color
|
|
387
|
+
<IconButton color="success" aria-label="Delete">
|
|
388
|
+
<DeleteIcon />
|
|
389
|
+
</IconButton>
|
|
390
|
+
```
|
|
347
391
|
|
|
348
|
-
3.
|
|
349
|
-
|
|
350
|
-
|
|
392
|
+
3. **Use the `loading` prop for async operations.** This prevents double-clicks and provides visual feedback during network requests or processing.
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
// ✅ Shows loading spinner during async action
|
|
396
|
+
<IconButton loading={isSaving} aria-label="Save" onClick={handleSave}>
|
|
397
|
+
<SaveIcon />
|
|
398
|
+
</IconButton>
|
|
399
|
+
```
|
|
351
400
|
|
|
352
|
-
4.
|
|
401
|
+
4. **Prefer universally recognized icons.** Choose icons that are widely understood (e.g., trash can for delete, pencil for edit, plus for add). Avoid ambiguous symbols without a Tooltip.
|
|
353
402
|
|
|
354
|
-
5.
|
|
355
|
-
- 빨간색(danger): 삭제, 취소 등의 위험한 액션
|
|
356
|
-
- 초록색(success): 저장, 승인 등의 긍정적인 액션
|
|
357
|
-
- 회색(neutral): 일반적인 액션
|
|
403
|
+
5. **Maintain consistent sizing within the same context.** Use the same `size` and `variant` for all IconButtons in a toolbar or action group to keep the interface clean and predictable.
|
|
358
404
|
|
|
359
|
-
|
|
405
|
+
## Accessibility
|
|
360
406
|
|
|
361
|
-
|
|
407
|
+
- **`aria-label` is essential**: Every IconButton must have either an `aria-label`, `aria-labelledby`, or a wrapping Tooltip with `title` to provide a text alternative for screen readers.
|
|
408
|
+
- **Keyboard interaction**: IconButton is focusable via `Tab` and can be activated with `Enter` or `Space`. Focus indicators are displayed by default.
|
|
409
|
+
- **Disabled state**: When `disabled` is set, the button receives `aria-disabled="true"` and becomes non-interactive. It remains visible in the DOM for screen readers to announce.
|
|
410
|
+
- **Touch targets**: Ensure the rendered button is at least 44x44 pixels for touch accessibility. The `sm` size may need additional padding in touch-heavy interfaces.
|