@ceed/ads 1.23.3 → 1.23.4
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 +368 -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,280 @@
|
|
|
1
|
+
# Skeleton
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
The Skeleton component provides placeholder previews of content before data is loaded. It is based on Joy UI's Skeleton and helps reduce perceived loading time by showing an approximation of the page layout. Skeletons improve the user experience by preventing layout shifts and giving users a visual cue that content is on its way.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<Skeleton
|
|
9
|
+
variant="rectangular"
|
|
10
|
+
width={200}
|
|
11
|
+
height={24}
|
|
12
|
+
/>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Field | Description | Default |
|
|
16
|
+
| ---------------------------- | ----------- | ------- |
|
|
17
|
+
| Controls resolved at runtime | — | — |
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { Skeleton } from '@ceed/ads';
|
|
23
|
+
|
|
24
|
+
function MyComponent() {
|
|
25
|
+
return <Skeleton variant="rectangular" width={200} height={24} />;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Variants
|
|
30
|
+
|
|
31
|
+
Skeleton supports three variants: `rectangular`, `circular`, and `text`.
|
|
32
|
+
|
|
33
|
+
- **rectangular**: Block-shaped placeholder for images, cards, and content areas.
|
|
34
|
+
- **circular**: Round placeholder for avatars and icons.
|
|
35
|
+
- **text**: Matches the height and spacing of text content at a given `level`.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<>
|
|
39
|
+
<Skeleton variant="rectangular" width={200} height={24} />
|
|
40
|
+
<Skeleton variant="circular" width={48} height={48} />
|
|
41
|
+
<Skeleton variant="text" width={200} />
|
|
42
|
+
</>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Skeleton variant="rectangular" width={200} height={24} />
|
|
47
|
+
<Skeleton variant="circular" width={48} height={48} />
|
|
48
|
+
<Skeleton variant="text" width={200} />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Animations
|
|
52
|
+
|
|
53
|
+
Skeleton supports `wave` (default) and `pulse` animations. Set `animation={false}` to disable animation entirely.
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<Stack gap={3}>
|
|
57
|
+
<Box>
|
|
58
|
+
<Typography level="body-sm" sx={{
|
|
59
|
+
mb: 1
|
|
60
|
+
}}>
|
|
61
|
+
Wave (default)
|
|
62
|
+
</Typography>
|
|
63
|
+
<Skeleton animation="wave" variant="rectangular" width={200} height={24} />
|
|
64
|
+
</Box>
|
|
65
|
+
<Box>
|
|
66
|
+
<Typography level="body-sm" sx={{
|
|
67
|
+
mb: 1
|
|
68
|
+
}}>
|
|
69
|
+
Pulse
|
|
70
|
+
</Typography>
|
|
71
|
+
<Skeleton animation="pulse" variant="rectangular" width={200} height={24} />
|
|
72
|
+
</Box>
|
|
73
|
+
<Box>
|
|
74
|
+
<Typography level="body-sm" sx={{
|
|
75
|
+
mb: 1
|
|
76
|
+
}}>
|
|
77
|
+
No animation (false)
|
|
78
|
+
</Typography>
|
|
79
|
+
<Skeleton animation={false} variant="rectangular" width={200} height={24} />
|
|
80
|
+
</Box>
|
|
81
|
+
</Stack>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<Skeleton animation="wave" variant="rectangular" width={200} height={24} />
|
|
86
|
+
<Skeleton animation="pulse" variant="rectangular" width={200} height={24} />
|
|
87
|
+
<Skeleton animation={false} variant="rectangular" width={200} height={24} />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Text Skeleton
|
|
91
|
+
|
|
92
|
+
Use `variant="text"` with the `level` prop to match Typography sizing. This is useful for creating text content placeholders that accurately reflect the final layout.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Stack gap={1} sx={{
|
|
96
|
+
width: 300
|
|
97
|
+
}}>
|
|
98
|
+
<Skeleton variant="text" level="h3" />
|
|
99
|
+
<Skeleton variant="text" level="body-md" />
|
|
100
|
+
<Skeleton variant="text" level="body-md" />
|
|
101
|
+
<Skeleton variant="text" level="body-md" width="80%" />
|
|
102
|
+
</Stack>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<Skeleton variant="text" level="h3" />
|
|
107
|
+
<Skeleton variant="text" level="body-md" />
|
|
108
|
+
<Skeleton variant="text" level="body-md" />
|
|
109
|
+
<Skeleton variant="text" level="body-md" width="80%" />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Card Skeleton
|
|
113
|
+
|
|
114
|
+
Compose multiple Skeleton elements to create placeholder layouts for complex components like cards.
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
<Box sx={{
|
|
118
|
+
width: 300,
|
|
119
|
+
p: 2,
|
|
120
|
+
border: '1px solid',
|
|
121
|
+
borderColor: 'divider',
|
|
122
|
+
borderRadius: 'sm'
|
|
123
|
+
}}>
|
|
124
|
+
<Skeleton variant="rectangular" width="100%" height={140} sx={{
|
|
125
|
+
borderRadius: 'sm',
|
|
126
|
+
mb: 2
|
|
127
|
+
}} />
|
|
128
|
+
<Skeleton variant="text" level="title-md" sx={{
|
|
129
|
+
mb: 1
|
|
130
|
+
}} />
|
|
131
|
+
<Skeleton variant="text" level="body-sm" />
|
|
132
|
+
<Skeleton variant="text" level="body-sm" width="60%" />
|
|
133
|
+
<Stack direction="row" gap={1} sx={{
|
|
134
|
+
mt: 2
|
|
135
|
+
}}>
|
|
136
|
+
<Skeleton variant="rectangular" width={80} height={32} sx={{
|
|
137
|
+
borderRadius: 'sm'
|
|
138
|
+
}} />
|
|
139
|
+
<Skeleton variant="rectangular" width={80} height={32} sx={{
|
|
140
|
+
borderRadius: 'sm'
|
|
141
|
+
}} />
|
|
142
|
+
</Stack>
|
|
143
|
+
</Box>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Data Loading List
|
|
147
|
+
|
|
148
|
+
Combine circular and text skeletons to represent list items during loading.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<Stack gap={2} sx={{
|
|
152
|
+
width: 400
|
|
153
|
+
}}>
|
|
154
|
+
{[1, 2, 3].map(i => <Stack key={i} direction="row" gap={2} alignItems="center">
|
|
155
|
+
<Skeleton variant="circular" width={40} height={40} />
|
|
156
|
+
<Box sx={{
|
|
157
|
+
flex: 1
|
|
158
|
+
}}>
|
|
159
|
+
<Skeleton variant="text" level="title-sm" width="60%" />
|
|
160
|
+
<Skeleton variant="text" level="body-xs" width="40%" />
|
|
161
|
+
</Box>
|
|
162
|
+
</Stack>)}
|
|
163
|
+
</Stack>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Inline Wrapping
|
|
167
|
+
|
|
168
|
+
Wrap existing content with Skeleton to overlay it while loading. Set the `loading` prop to control visibility.
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
<Typography level="body-md">
|
|
172
|
+
<Skeleton loading>
|
|
173
|
+
This text will be hidden behind a skeleton while loading.
|
|
174
|
+
</Skeleton>
|
|
175
|
+
</Typography>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
<Typography level="body-md">
|
|
180
|
+
<Skeleton loading>
|
|
181
|
+
This text will be hidden behind a skeleton while loading.
|
|
182
|
+
</Skeleton>
|
|
183
|
+
</Typography>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Common Use Cases
|
|
187
|
+
|
|
188
|
+
### Page Content Loading
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
function PageSkeleton() {
|
|
192
|
+
return (
|
|
193
|
+
<Stack gap={3}>
|
|
194
|
+
<Skeleton variant="text" level="h1" width="50%" />
|
|
195
|
+
<Skeleton variant="text" level="body-md" />
|
|
196
|
+
<Skeleton variant="text" level="body-md" />
|
|
197
|
+
<Skeleton variant="text" level="body-md" width="75%" />
|
|
198
|
+
|
|
199
|
+
<Skeleton variant="rectangular" width="100%" height={200} sx={{ borderRadius: 'sm' }} />
|
|
200
|
+
|
|
201
|
+
<Skeleton variant="text" level="body-md" />
|
|
202
|
+
<Skeleton variant="text" level="body-md" />
|
|
203
|
+
</Stack>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### User List Loading
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
function UserListSkeleton({ count = 5 }: { count?: number }) {
|
|
212
|
+
return (
|
|
213
|
+
<Stack gap={2}>
|
|
214
|
+
{Array.from({ length: count }).map((_, i) => (
|
|
215
|
+
<Stack key={i} direction="row" gap={2} alignItems="center">
|
|
216
|
+
<Skeleton variant="circular" width={40} height={40} />
|
|
217
|
+
<Box sx={{ flex: 1 }}>
|
|
218
|
+
<Skeleton variant="text" level="title-sm" width="40%" />
|
|
219
|
+
<Skeleton variant="text" level="body-xs" width="25%" />
|
|
220
|
+
</Box>
|
|
221
|
+
</Stack>
|
|
222
|
+
))}
|
|
223
|
+
</Stack>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Conditional Rendering
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
function UserProfile({ loading, user }: { loading: boolean; user?: User }) {
|
|
232
|
+
return (
|
|
233
|
+
<Stack direction="row" gap={2} alignItems="center">
|
|
234
|
+
{loading ? (
|
|
235
|
+
<Skeleton variant="circular" width={48} height={48} />
|
|
236
|
+
) : (
|
|
237
|
+
<Avatar src={user?.avatar} />
|
|
238
|
+
)}
|
|
239
|
+
<Box>
|
|
240
|
+
<Typography level="title-md">
|
|
241
|
+
<Skeleton loading={loading}>{user?.name || 'Placeholder Name'}</Skeleton>
|
|
242
|
+
</Typography>
|
|
243
|
+
<Typography level="body-sm">
|
|
244
|
+
<Skeleton loading={loading}>{user?.email || 'email@example.com'}</Skeleton>
|
|
245
|
+
</Typography>
|
|
246
|
+
</Box>
|
|
247
|
+
</Stack>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Best Practices
|
|
253
|
+
|
|
254
|
+
1. **Match the final layout**: Skeleton placeholders should closely approximate the size and position of the real content to prevent layout shifts.
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
// ✅ Matches the actual content structure
|
|
258
|
+
<Stack gap={1}>
|
|
259
|
+
<Skeleton variant="text" level="title-md" width="60%" />
|
|
260
|
+
<Skeleton variant="text" level="body-sm" />
|
|
261
|
+
</Stack>
|
|
262
|
+
|
|
263
|
+
// ❌ Generic rectangle that doesn't match
|
|
264
|
+
<Skeleton variant="rectangular" width={300} height={100} />
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
2. **Use `variant="text"` with `level`**: When replacing Typography, use the text variant with the matching level to get accurate line heights.
|
|
268
|
+
|
|
269
|
+
3. **Avoid over-skeletonizing**: Only skeleton the main content areas. Don't add skeletons for static elements like navigation or headers that are always present.
|
|
270
|
+
|
|
271
|
+
4. **Use consistent animation**: Keep the same animation type (`wave` or `pulse`) across the entire application for a cohesive loading experience.
|
|
272
|
+
|
|
273
|
+
5. **Set appropriate widths**: Vary skeleton widths (e.g., 60%, 80%, 100%) to mimic natural text line lengths rather than using uniform widths.
|
|
274
|
+
|
|
275
|
+
## Accessibility
|
|
276
|
+
|
|
277
|
+
- Skeleton elements are purely decorative. Screen readers should focus on the loading state announcement, not individual skeleton elements.
|
|
278
|
+
- Use `aria-busy="true"` on the container element while content is loading.
|
|
279
|
+
- Provide an `aria-label` or visually hidden text that describes the loading state (e.g., "Loading user profile").
|
|
280
|
+
- Ensure the animation respects `prefers-reduced-motion` — Joy UI handles this automatically.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
ButtonGroup
|
|
5
|
+
ButtonGroup is a layout component that visually and semantically groups related buttons into a single cohesive unit. It automatically handles border merging, spacing, and consistent styling across all child buttons, making it ideal for toolbars, action sets, and segmented controls.
|
|
6
|
+
|
|
7
|
+
By applying shared props like `color`, `variant`, `size`, and `orientation` at the group level, you ensure visual consistency without having to repeat props on every individual button. ButtonGroup is built on top of Joy UI's ButtonGroup and inherits all of its capabilities.
|
|
6
8
|
|
|
7
9
|
```tsx
|
|
8
10
|
<ButtonGroup {...args}>
|
|
@@ -28,37 +30,17 @@ import { ButtonGroup, Button } from '@ceed/ads';
|
|
|
28
30
|
function MyComponent() {
|
|
29
31
|
return (
|
|
30
32
|
<ButtonGroup>
|
|
31
|
-
<Button
|
|
32
|
-
<Button
|
|
33
|
-
<Button
|
|
33
|
+
<Button>First</Button>
|
|
34
|
+
<Button>Second</Button>
|
|
35
|
+
<Button>Third</Button>
|
|
34
36
|
</ButtonGroup>
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
### Basic Usage
|
|
41
|
+
## Colors
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
<div style={{
|
|
47
|
-
display: 'flex',
|
|
48
|
-
gap: '2rem',
|
|
49
|
-
flexDirection: 'column'
|
|
50
|
-
}}>
|
|
51
|
-
<ButtonGroup>
|
|
52
|
-
<Button>첫번째</Button>
|
|
53
|
-
<Button>두번째</Button>
|
|
54
|
-
<Button>세번째</Button>
|
|
55
|
-
</ButtonGroup>
|
|
56
|
-
</div>
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Colors
|
|
60
|
-
|
|
61
|
-
다양한 색상을 적용할 수 있습니다.
|
|
43
|
+
Apply a unified color theme to all buttons in the group using the `color` prop. Available colors are `primary`, `neutral`, `danger`, `success`, and `warning`.
|
|
62
44
|
|
|
63
45
|
```tsx
|
|
64
46
|
<div style={{
|
|
@@ -98,9 +80,9 @@ function MyComponent() {
|
|
|
98
80
|
</div>
|
|
99
81
|
```
|
|
100
82
|
|
|
101
|
-
|
|
83
|
+
## Variants
|
|
102
84
|
|
|
103
|
-
|
|
85
|
+
ButtonGroup supports four visual variants: `solid`, `soft`, `outlined`, and `plain`. The variant prop propagates to all child buttons, ensuring a consistent look.
|
|
104
86
|
|
|
105
87
|
```tsx
|
|
106
88
|
<div style={{
|
|
@@ -134,9 +116,9 @@ function MyComponent() {
|
|
|
134
116
|
</div>
|
|
135
117
|
```
|
|
136
118
|
|
|
137
|
-
|
|
119
|
+
## Sizes
|
|
138
120
|
|
|
139
|
-
|
|
121
|
+
Control the size of all buttons in the group with the `size` prop. Available sizes are `sm`, `md` (default), and `lg`.
|
|
140
122
|
|
|
141
123
|
```tsx
|
|
142
124
|
<div style={{
|
|
@@ -165,9 +147,9 @@ function MyComponent() {
|
|
|
165
147
|
</div>
|
|
166
148
|
```
|
|
167
149
|
|
|
168
|
-
|
|
150
|
+
## Orientations
|
|
169
151
|
|
|
170
|
-
|
|
152
|
+
Buttons can be arranged horizontally (default) or vertically. Use `orientation="vertical"` for stacked layouts such as side panels or narrow containers.
|
|
171
153
|
|
|
172
154
|
```tsx
|
|
173
155
|
<div style={{
|
|
@@ -195,9 +177,9 @@ function MyComponent() {
|
|
|
195
177
|
</div>
|
|
196
178
|
```
|
|
197
179
|
|
|
198
|
-
|
|
180
|
+
## With Icons
|
|
199
181
|
|
|
200
|
-
|
|
182
|
+
Combine text labels with icons using the `startDecorator` prop on individual buttons for a richer, more descriptive interface.
|
|
201
183
|
|
|
202
184
|
```tsx
|
|
203
185
|
<div style={{
|
|
@@ -219,49 +201,9 @@ function MyComponent() {
|
|
|
219
201
|
</div>
|
|
220
202
|
```
|
|
221
203
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
실제 사용 사례별 액션 그룹들입니다.
|
|
225
|
-
|
|
226
|
-
```tsx
|
|
227
|
-
<div style={{
|
|
228
|
-
display: 'flex',
|
|
229
|
-
gap: '2rem',
|
|
230
|
-
flexDirection: 'column'
|
|
231
|
-
}}>
|
|
232
|
-
<div>
|
|
233
|
-
<h4>File Actions</h4>
|
|
234
|
-
<ButtonGroup variant="outlined">
|
|
235
|
-
<Button onClick={() => alert('Save')}>저장</Button>
|
|
236
|
-
<Button onClick={() => alert('Save As')}>다른 이름으로 저장</Button>
|
|
237
|
-
<Button onClick={() => alert('Export')}>내보내기</Button>
|
|
238
|
-
</ButtonGroup>
|
|
239
|
-
</div>
|
|
240
|
-
|
|
241
|
-
<div>
|
|
242
|
-
<h4>Text Formatting</h4>
|
|
243
|
-
<ButtonGroup size="sm" variant="soft">
|
|
244
|
-
<Button>B</Button>
|
|
245
|
-
<Button>I</Button>
|
|
246
|
-
<Button>U</Button>
|
|
247
|
-
<Button>S</Button>
|
|
248
|
-
</ButtonGroup>
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<div>
|
|
252
|
-
<h4>View Options</h4>
|
|
253
|
-
<ButtonGroup color="neutral" variant="outlined">
|
|
254
|
-
<Button>목록</Button>
|
|
255
|
-
<Button>격자</Button>
|
|
256
|
-
<Button>타일</Button>
|
|
257
|
-
</ButtonGroup>
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### States
|
|
204
|
+
## States
|
|
263
205
|
|
|
264
|
-
|
|
206
|
+
ButtonGroup supports disabled states at the group level (affecting all children) or on individual buttons for mixed-state scenarios.
|
|
265
207
|
|
|
266
208
|
```tsx
|
|
267
209
|
<div style={{
|
|
@@ -298,9 +240,9 @@ function MyComponent() {
|
|
|
298
240
|
</div>
|
|
299
241
|
```
|
|
300
242
|
|
|
301
|
-
|
|
243
|
+
## Toggle Group
|
|
302
244
|
|
|
303
|
-
|
|
245
|
+
By dynamically switching the `variant` prop on individual buttons based on selection state, you can create a segmented toggle control for exclusive selection patterns.
|
|
304
246
|
|
|
305
247
|
```tsx
|
|
306
248
|
<div>
|
|
@@ -325,58 +267,125 @@ function MyComponent() {
|
|
|
325
267
|
### Toolbar Actions
|
|
326
268
|
|
|
327
269
|
```tsx
|
|
270
|
+
import { ButtonGroup, Button } from '@ceed/ads';
|
|
271
|
+
import SaveIcon from '@mui/icons-material/Save';
|
|
272
|
+
import UndoIcon from '@mui/icons-material/Undo';
|
|
273
|
+
import RedoIcon from '@mui/icons-material/Redo';
|
|
274
|
+
|
|
328
275
|
<ButtonGroup variant="outlined">
|
|
329
|
-
<Button startDecorator={<SaveIcon />}
|
|
330
|
-
<Button startDecorator={<UndoIcon />}
|
|
331
|
-
<Button startDecorator={<RedoIcon />}
|
|
276
|
+
<Button startDecorator={<SaveIcon />}>Save</Button>
|
|
277
|
+
<Button startDecorator={<UndoIcon />}>Undo</Button>
|
|
278
|
+
<Button startDecorator={<RedoIcon />}>Redo</Button>
|
|
332
279
|
</ButtonGroup>
|
|
333
280
|
```
|
|
334
281
|
|
|
335
|
-
###
|
|
282
|
+
### View Switcher
|
|
336
283
|
|
|
337
284
|
```tsx
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
<Button
|
|
285
|
+
const [view, setView] = useState('list');
|
|
286
|
+
|
|
287
|
+
<ButtonGroup variant="outlined" color="neutral">
|
|
288
|
+
<Button
|
|
289
|
+
variant={view === 'list' ? 'solid' : 'outlined'}
|
|
290
|
+
onClick={() => setView('list')}
|
|
291
|
+
>
|
|
292
|
+
List
|
|
293
|
+
</Button>
|
|
294
|
+
<Button
|
|
295
|
+
variant={view === 'grid' ? 'solid' : 'outlined'}
|
|
296
|
+
onClick={() => setView('grid')}
|
|
297
|
+
>
|
|
298
|
+
Grid
|
|
299
|
+
</Button>
|
|
300
|
+
<Button
|
|
301
|
+
variant={view === 'card' ? 'solid' : 'outlined'}
|
|
302
|
+
onClick={() => setView('card')}
|
|
303
|
+
>
|
|
304
|
+
Card
|
|
305
|
+
</Button>
|
|
342
306
|
</ButtonGroup>
|
|
343
307
|
```
|
|
344
308
|
|
|
345
|
-
###
|
|
309
|
+
### Pagination Controls
|
|
346
310
|
|
|
347
311
|
```tsx
|
|
348
|
-
<ButtonGroup
|
|
349
|
-
<Button onClick={() =>
|
|
350
|
-
|
|
351
|
-
|
|
312
|
+
<ButtonGroup variant="outlined" size="sm">
|
|
313
|
+
<Button disabled={page === 1} onClick={() => setPage(page - 1)}>
|
|
314
|
+
Previous
|
|
315
|
+
</Button>
|
|
316
|
+
<Button disabled>{page}</Button>
|
|
317
|
+
<Button onClick={() => setPage(page + 1)}>
|
|
318
|
+
Next
|
|
319
|
+
</Button>
|
|
352
320
|
</ButtonGroup>
|
|
353
321
|
```
|
|
354
322
|
|
|
355
|
-
|
|
323
|
+
## Best Practices
|
|
324
|
+
|
|
325
|
+
1. **Group only related actions together.** Each ButtonGroup should represent a logically connected set of operations.
|
|
356
326
|
|
|
357
327
|
```tsx
|
|
328
|
+
// ✅ Related file operations grouped together
|
|
358
329
|
<ButtonGroup variant="outlined">
|
|
359
|
-
<Button
|
|
360
|
-
|
|
361
|
-
</Button>
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
330
|
+
<Button>Save</Button>
|
|
331
|
+
<Button>Save As</Button>
|
|
332
|
+
<Button>Export</Button>
|
|
333
|
+
</ButtonGroup>
|
|
334
|
+
|
|
335
|
+
// ❌ Unrelated actions mixed in one group
|
|
336
|
+
<ButtonGroup>
|
|
337
|
+
<Button>Save</Button>
|
|
338
|
+
<Button>Delete Account</Button>
|
|
339
|
+
<Button>Help</Button>
|
|
365
340
|
</ButtonGroup>
|
|
366
341
|
```
|
|
367
342
|
|
|
368
|
-
|
|
343
|
+
2. **Keep the button count between 2 and 5.** Too many buttons reduce clarity and overwhelm users.
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
// ✅ Concise group
|
|
347
|
+
<ButtonGroup>
|
|
348
|
+
<Button>Bold</Button>
|
|
349
|
+
<Button>Italic</Button>
|
|
350
|
+
<Button>Underline</Button>
|
|
351
|
+
</ButtonGroup>
|
|
369
352
|
|
|
370
|
-
|
|
353
|
+
// ❌ Too many options in a single group
|
|
354
|
+
<ButtonGroup>
|
|
355
|
+
<Button>Bold</Button>
|
|
356
|
+
<Button>Italic</Button>
|
|
357
|
+
<Button>Underline</Button>
|
|
358
|
+
<Button>Strikethrough</Button>
|
|
359
|
+
<Button>Superscript</Button>
|
|
360
|
+
<Button>Subscript</Button>
|
|
361
|
+
<Button>Code</Button>
|
|
362
|
+
</ButtonGroup>
|
|
363
|
+
```
|
|
371
364
|
|
|
372
|
-
|
|
365
|
+
3. **Use consistent variant and color across the group.** Avoid overriding individual button styles unless implementing toggle selection.
|
|
373
366
|
|
|
374
|
-
|
|
367
|
+
```tsx
|
|
368
|
+
// ✅ Consistent styling via group props
|
|
369
|
+
<ButtonGroup variant="outlined" color="neutral">
|
|
370
|
+
<Button>A</Button>
|
|
371
|
+
<Button>B</Button>
|
|
372
|
+
</ButtonGroup>
|
|
373
|
+
|
|
374
|
+
// ❌ Mixed styles with no semantic reason
|
|
375
|
+
<ButtonGroup>
|
|
376
|
+
<Button variant="solid">A</Button>
|
|
377
|
+
<Button variant="outlined">B</Button>
|
|
378
|
+
<Button variant="plain">C</Button>
|
|
379
|
+
</ButtonGroup>
|
|
380
|
+
```
|
|
375
381
|
|
|
376
|
-
4.
|
|
382
|
+
4. **Clearly indicate the active state in toggle groups.** Use a distinct variant (e.g., `solid` vs `outlined`) so users can immediately see which option is selected.
|
|
377
383
|
|
|
378
|
-
5.
|
|
384
|
+
5. **Consider vertical orientation for narrow layouts.** On mobile or in sidebars, `orientation="vertical"` prevents horizontal overflow and improves usability.
|
|
379
385
|
|
|
380
|
-
|
|
386
|
+
## Accessibility
|
|
381
387
|
|
|
382
|
-
|
|
388
|
+
- **Keyboard navigation**: All buttons within the group are focusable and navigable using the `Tab` key. Each button is a standard focusable element that responds to `Enter` and `Space`.
|
|
389
|
+
- **ARIA grouping**: Wrap the ButtonGroup in a container with `role="group"` and an `aria-label` when the group represents a distinct set of controls (e.g., text formatting toolbar).
|
|
390
|
+
- **Disabled state**: When the entire group is disabled, all child buttons receive `aria-disabled="true"` automatically, preventing interaction and communicating the state to assistive technologies.
|
|
391
|
+
- **Toggle groups**: When using ButtonGroup as a toggle, consider adding `aria-pressed` to each button to communicate the selection state to screen readers.
|