@ceed/cds 1.21.0 → 1.22.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/components/RadioTileGroup/RadioTileGroup.d.ts +56 -0
- package/dist/components/RadioTileGroup/index.d.ts +3 -0
- package/dist/components/feedback/Dialog.md +72 -62
- package/dist/components/index.d.ts +1 -0
- package/dist/components/inputs/RadioTileGroup.md +418 -0
- package/dist/components/inputs/llms.txt +1 -0
- package/dist/index.browser.js +5 -5
- package/dist/index.browser.js.map +4 -4
- package/dist/index.cjs +345 -181
- package/dist/index.d.ts +1 -1
- package/dist/index.js +212 -48
- package/dist/llms.txt +1 -0
- package/framer/index.js +39 -39
- package/package.json +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import type { SxProps } from '@mui/joy/styles/types';
|
|
3
|
+
export interface RadioTileOption<T = string> {
|
|
4
|
+
value: T;
|
|
5
|
+
label: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
startDecorator?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
export interface RadioTileGroupProps<T = string> {
|
|
10
|
+
/**
|
|
11
|
+
* @default 'sm'
|
|
12
|
+
*/
|
|
13
|
+
size?: 'sm' | 'md' | 'lg';
|
|
14
|
+
options: RadioTileOption<T>[];
|
|
15
|
+
value?: T;
|
|
16
|
+
defaultValue?: T;
|
|
17
|
+
name?: string;
|
|
18
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
19
|
+
sx?: SxProps;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
className?: string;
|
|
22
|
+
useIndicator?: boolean;
|
|
23
|
+
flex?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 지정하지 않으면 한 row에 모든 옵션이 렌더링된다.
|
|
26
|
+
* - 특정 rows 이내로 렌더링 하고 싶으면 `columns: Math.ceil(options.length / 원하는 rows)`로 설정할 수 있다.
|
|
27
|
+
* - 수직으로 렌더링 하고 싶으면 `columns: 1`로 설정할 수 있다.
|
|
28
|
+
* - `flex` 옵션과 함께 사용할수 있다.
|
|
29
|
+
*/
|
|
30
|
+
columns?: number;
|
|
31
|
+
/**
|
|
32
|
+
* @default 'center'
|
|
33
|
+
*/
|
|
34
|
+
textAlign?: 'start' | 'center';
|
|
35
|
+
/**
|
|
36
|
+
* Label for the RadioTileGroup
|
|
37
|
+
*/
|
|
38
|
+
label?: React.ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Helper text for the RadioTileGroup
|
|
41
|
+
*/
|
|
42
|
+
helperText?: React.ReactNode;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the RadioTileGroup has an error
|
|
45
|
+
*/
|
|
46
|
+
error?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the RadioTileGroup is required
|
|
49
|
+
*/
|
|
50
|
+
required?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare function RadioTileGroup<T extends string | number = string>(props: RadioTileGroupProps<T>): React.JSX.Element;
|
|
53
|
+
declare namespace RadioTileGroup {
|
|
54
|
+
var displayName: string;
|
|
55
|
+
}
|
|
56
|
+
export { RadioTileGroup };
|
|
@@ -43,9 +43,7 @@ function ConfirmationDialog({ open, onClose, onConfirm }) {
|
|
|
43
43
|
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
44
44
|
Cancel
|
|
45
45
|
</Button>
|
|
46
|
-
<Button onClick={onConfirm}>
|
|
47
|
-
Confirm
|
|
48
|
-
</Button>
|
|
46
|
+
<Button onClick={onConfirm}>Confirm</Button>
|
|
49
47
|
</>
|
|
50
48
|
}
|
|
51
49
|
>
|
|
@@ -94,6 +92,57 @@ Dialog Content
|
|
|
94
92
|
</DialogFrame>
|
|
95
93
|
```
|
|
96
94
|
|
|
95
|
+
### Standalone Usage
|
|
96
|
+
|
|
97
|
+
DialogFrame can be used without a Modal wrapper for embedding dialog-style layouts directly within a page.
|
|
98
|
+
|
|
99
|
+
> ⚠️ **Important** ⚠️
|
|
100
|
+
>
|
|
101
|
+
> When using DialogFrame without Modal, the parent container **must** provide explicit `width` and `height` values.
|
|
102
|
+
> DialogFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay.
|
|
103
|
+
> Without these constraints, the component will not render with correct dimensions.
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<Box sx={{
|
|
107
|
+
width: 480,
|
|
108
|
+
height: 300
|
|
109
|
+
}}>
|
|
110
|
+
<DialogFrame {...args} title="Standalone Dialog" actions={<>
|
|
111
|
+
<Button variant="plain" color="neutral">
|
|
112
|
+
Cancel
|
|
113
|
+
</Button>
|
|
114
|
+
<Button variant="plain">Confirm</Button>
|
|
115
|
+
</>}>
|
|
116
|
+
DialogFrame used without Modal. The parent container must provide explicit width and height.
|
|
117
|
+
</DialogFrame>
|
|
118
|
+
</Box>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { DialogFrame, Button, Box } from '@ceed/cds';
|
|
123
|
+
|
|
124
|
+
// Standalone usage requires explicit container dimensions
|
|
125
|
+
function EmbeddedDialog() {
|
|
126
|
+
return (
|
|
127
|
+
<Box sx={{ width: 480, height: 300 }}>
|
|
128
|
+
<DialogFrame
|
|
129
|
+
title="Settings"
|
|
130
|
+
actions={
|
|
131
|
+
<>
|
|
132
|
+
<Button variant="plain" color="neutral">
|
|
133
|
+
Cancel
|
|
134
|
+
</Button>
|
|
135
|
+
<Button>Save</Button>
|
|
136
|
+
</>
|
|
137
|
+
}
|
|
138
|
+
>
|
|
139
|
+
This dialog is embedded directly in the page layout.
|
|
140
|
+
</DialogFrame>
|
|
141
|
+
</Box>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
97
146
|
## When to Use
|
|
98
147
|
|
|
99
148
|
### ✅ Good Use Cases
|
|
@@ -177,21 +226,11 @@ function QuickAddDialog({ open, onClose, onSubmit }) {
|
|
|
177
226
|
<Stack gap={2}>
|
|
178
227
|
<FormControl>
|
|
179
228
|
<FormLabel>Name</FormLabel>
|
|
180
|
-
<Input
|
|
181
|
-
value={name}
|
|
182
|
-
onChange={(e) => setName(e.target.value)}
|
|
183
|
-
placeholder="Enter name"
|
|
184
|
-
autoFocus
|
|
185
|
-
/>
|
|
229
|
+
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder="Enter name" autoFocus />
|
|
186
230
|
</FormControl>
|
|
187
231
|
<FormControl>
|
|
188
232
|
<FormLabel>Email</FormLabel>
|
|
189
|
-
<Input
|
|
190
|
-
type="email"
|
|
191
|
-
value={email}
|
|
192
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
193
|
-
placeholder="Enter email"
|
|
194
|
-
/>
|
|
233
|
+
<Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Enter email" />
|
|
195
234
|
</FormControl>
|
|
196
235
|
</Stack>
|
|
197
236
|
</DialogFrame>
|
|
@@ -216,15 +255,11 @@ function UnsavedChangesDialog({ open, onClose, onDiscard, onSave }) {
|
|
|
216
255
|
<Button variant="outlined" color="danger" onClick={onDiscard}>
|
|
217
256
|
Discard
|
|
218
257
|
</Button>
|
|
219
|
-
<Button onClick={onSave}>
|
|
220
|
-
Save Changes
|
|
221
|
-
</Button>
|
|
258
|
+
<Button onClick={onSave}>Save Changes</Button>
|
|
222
259
|
</>
|
|
223
260
|
}
|
|
224
261
|
>
|
|
225
|
-
<Typography>
|
|
226
|
-
You have unsaved changes. Would you like to save them before leaving?
|
|
227
|
-
</Typography>
|
|
262
|
+
<Typography>You have unsaved changes. Would you like to save them before leaving?</Typography>
|
|
228
263
|
</DialogFrame>
|
|
229
264
|
</Modal>
|
|
230
265
|
);
|
|
@@ -237,14 +272,7 @@ function UnsavedChangesDialog({ open, onClose, onDiscard, onSave }) {
|
|
|
237
272
|
function InfoDialog({ open, onClose, title, message }) {
|
|
238
273
|
return (
|
|
239
274
|
<Modal open={open} onClose={onClose}>
|
|
240
|
-
<DialogFrame
|
|
241
|
-
title={title}
|
|
242
|
-
actions={
|
|
243
|
-
<Button onClick={onClose}>
|
|
244
|
-
Got it
|
|
245
|
-
</Button>
|
|
246
|
-
}
|
|
247
|
-
>
|
|
275
|
+
<DialogFrame title={title} actions={<Button onClick={onClose}>Got it</Button>}>
|
|
248
276
|
<Typography>{message}</Typography>
|
|
249
277
|
</DialogFrame>
|
|
250
278
|
</Modal>
|
|
@@ -301,18 +329,8 @@ function ProcessingDialog({ open, status, onClose }) {
|
|
|
301
329
|
return (
|
|
302
330
|
<Modal open={open} onClose={isProcessing ? undefined : onClose}>
|
|
303
331
|
<DialogFrame
|
|
304
|
-
title={
|
|
305
|
-
|
|
306
|
-
isSuccess ? 'Success!' :
|
|
307
|
-
'Error'
|
|
308
|
-
}
|
|
309
|
-
actions={
|
|
310
|
-
!isProcessing && (
|
|
311
|
-
<Button onClick={onClose}>
|
|
312
|
-
{isSuccess ? 'Done' : 'Try Again'}
|
|
313
|
-
</Button>
|
|
314
|
-
)
|
|
315
|
-
}
|
|
332
|
+
title={isProcessing ? 'Processing...' : isSuccess ? 'Success!' : 'Error'}
|
|
333
|
+
actions={!isProcessing && <Button onClick={onClose}>{isSuccess ? 'Done' : 'Try Again'}</Button>}
|
|
316
334
|
>
|
|
317
335
|
<Box sx={{ textAlign: 'center', py: 2 }}>
|
|
318
336
|
{isProcessing && <CircularProgress />}
|
|
@@ -457,9 +475,7 @@ DialogFrame should be wrapped in Modal for proper behavior:
|
|
|
457
475
|
// Fullscreen for complex content or mobile
|
|
458
476
|
<Modal open={open} onClose={onClose}>
|
|
459
477
|
<DialogFrame fullscreen title="Edit Profile">
|
|
460
|
-
<Box sx={{ p: 2 }}>
|
|
461
|
-
{/* Large form or content */}
|
|
462
|
-
</Box>
|
|
478
|
+
<Box sx={{ p: 2 }}>{/* Large form or content */}</Box>
|
|
463
479
|
</DialogFrame>
|
|
464
480
|
</Modal>
|
|
465
481
|
```
|
|
@@ -500,9 +516,7 @@ DialogFrame inherits accessibility features from Modal:
|
|
|
500
516
|
|
|
501
517
|
```tsx
|
|
502
518
|
// Title provides context
|
|
503
|
-
<DialogFrame title="Confirm Deletion">
|
|
504
|
-
{/* Content is read after title */}
|
|
505
|
-
</DialogFrame>
|
|
519
|
+
<DialogFrame title="Confirm Deletion">{/* Content is read after title */}</DialogFrame>
|
|
506
520
|
```
|
|
507
521
|
|
|
508
522
|
## Best Practices
|
|
@@ -530,9 +544,7 @@ DialogFrame inherits accessibility features from Modal:
|
|
|
530
544
|
```tsx
|
|
531
545
|
// ✅ Good: Brief, scannable content
|
|
532
546
|
<DialogFrame title="Delete Project">
|
|
533
|
-
<Typography>
|
|
534
|
-
This will permanently delete the project and all its data.
|
|
535
|
-
</Typography>
|
|
547
|
+
<Typography>This will permanently delete the project and all its data.</Typography>
|
|
536
548
|
</DialogFrame>
|
|
537
549
|
```
|
|
538
550
|
|
|
@@ -560,7 +572,7 @@ DialogFrame inherits accessibility features from Modal:
|
|
|
560
572
|
// ❌ Bad: Dialog for simple feedback
|
|
561
573
|
<DialogFrame title="Success">
|
|
562
574
|
<Typography>Item saved!</Typography>
|
|
563
|
-
</DialogFrame
|
|
575
|
+
</DialogFrame>;
|
|
564
576
|
|
|
565
577
|
// ✅ Good: Use Toast
|
|
566
578
|
showToast({ message: 'Item saved!' });
|
|
@@ -570,9 +582,7 @@ showToast({ message: 'Item saved!' });
|
|
|
570
582
|
|
|
571
583
|
```tsx
|
|
572
584
|
// ❌ Bad: Complex form in dialog
|
|
573
|
-
<DialogFrame title="Create Account">
|
|
574
|
-
{/* 20+ form fields */}
|
|
575
|
-
</DialogFrame>
|
|
585
|
+
<DialogFrame title="Create Account">{/* 20+ form fields */}</DialogFrame>
|
|
576
586
|
```
|
|
577
587
|
|
|
578
588
|
3. **Don't use vague button labels**: Be specific about actions
|
|
@@ -599,9 +609,7 @@ For dialogs with heavy content:
|
|
|
599
609
|
function HeavyDialog({ open, onClose }) {
|
|
600
610
|
return (
|
|
601
611
|
<Modal open={open} onClose={onClose}>
|
|
602
|
-
<DialogFrame title="Data Preview">
|
|
603
|
-
{open && <HeavyDataComponent />}
|
|
604
|
-
</DialogFrame>
|
|
612
|
+
<DialogFrame title="Data Preview">{open && <HeavyDataComponent />}</DialogFrame>
|
|
605
613
|
</Modal>
|
|
606
614
|
);
|
|
607
615
|
}
|
|
@@ -625,11 +633,13 @@ const handleCancel = useCallback(() => {
|
|
|
625
633
|
Unmount dialog completely when not needed:
|
|
626
634
|
|
|
627
635
|
```tsx
|
|
628
|
-
{
|
|
629
|
-
|
|
630
|
-
<
|
|
631
|
-
|
|
632
|
-
|
|
636
|
+
{
|
|
637
|
+
open && (
|
|
638
|
+
<Modal open={open} onClose={onClose}>
|
|
639
|
+
<DialogFrame>...</DialogFrame>
|
|
640
|
+
</Modal>
|
|
641
|
+
);
|
|
642
|
+
}
|
|
633
643
|
```
|
|
634
644
|
|
|
635
645
|
DialogFrame provides a consistent structure for dialog content. Combine it with Modal for proper overlay behavior, keep content concise and actionable, and always provide clear options for users to proceed or cancel.
|
|
@@ -43,6 +43,7 @@ export { Navigator } from './Navigator';
|
|
|
43
43
|
export { Pagination } from './Pagination';
|
|
44
44
|
export { PercentageInput } from './PercentageInput';
|
|
45
45
|
export { Radio, RadioGroup } from './Radio';
|
|
46
|
+
export { RadioTileGroup } from './RadioTileGroup';
|
|
46
47
|
export { RadioList } from './RadioList';
|
|
47
48
|
export { Select, Option } from './Select';
|
|
48
49
|
export { Sheet } from './Sheet';
|