@ceed/ads 1.20.0 → 1.20.1-next.1
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/ProfileMenu/ProfileMenu.d.ts +1 -1
- package/dist/components/data-display/Markdown.md +832 -0
- package/dist/components/feedback/Dialog.md +605 -3
- package/dist/components/feedback/Modal.md +656 -24
- package/dist/components/feedback/llms.txt +1 -1
- package/dist/components/inputs/Autocomplete.md +734 -2
- package/dist/components/inputs/Calendar.md +655 -1
- package/dist/components/inputs/DatePicker.md +699 -3
- package/dist/components/inputs/DateRangePicker.md +815 -1
- package/dist/components/inputs/MonthPicker.md +626 -4
- package/dist/components/inputs/MonthRangePicker.md +682 -4
- package/dist/components/inputs/Select.md +600 -0
- package/dist/components/layout/Container.md +507 -0
- package/dist/components/navigation/Breadcrumbs.md +582 -0
- package/dist/components/navigation/IconMenuButton.md +693 -0
- package/dist/components/navigation/InsetDrawer.md +1150 -3
- package/dist/components/navigation/Link.md +526 -0
- package/dist/components/navigation/MenuButton.md +632 -0
- package/dist/components/navigation/NavigationGroup.md +401 -1
- package/dist/components/navigation/NavigationItem.md +311 -0
- package/dist/components/navigation/Navigator.md +373 -0
- package/dist/components/navigation/Pagination.md +521 -0
- package/dist/components/navigation/ProfileMenu.md +605 -0
- package/dist/components/navigation/Tabs.md +609 -7
- package/dist/components/surfaces/Accordions.md +947 -3
- package/dist/index.cjs +3 -1
- package/dist/index.js +3 -1
- package/dist/llms.txt +1 -1
- package/framer/index.js +1 -1
- package/package.json +3 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DialogFrame
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
+
DialogFrame is a compound component that provides a structured container for dialog content, including title, body, and action buttons. It's designed to be used within a Modal component to create consistent, accessible dialog interfaces for confirmations, forms, and informational displays. DialogFrame handles the layout and styling of dialog elements while the parent Modal manages the overlay and open/close behavior.
|
|
6
|
+
|
|
5
7
|
```tsx
|
|
6
8
|
<DialogFrame
|
|
7
9
|
title="Dialog Title"
|
|
@@ -17,12 +19,61 @@
|
|
|
17
19
|
| fullscreen | — | — |
|
|
18
20
|
| actions | — | actions |
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
> ⚠️ **Usage Warning** ⚠️
|
|
23
|
+
>
|
|
24
|
+
> Dialogs interrupt the user's workflow:
|
|
25
|
+
>
|
|
26
|
+
> - **Confirmations only**: Use dialogs for important decisions that need confirmation
|
|
27
|
+
> - **Keep it brief**: Dialog content should be scannable and actionable
|
|
28
|
+
> - **Consider alternatives**: Use Toast for feedback, Alert for inline messages
|
|
29
|
+
> - **Wrap with Modal**: DialogFrame should be used inside a Modal component for proper behavior
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { Modal, DialogFrame, Button } from '@ceed/ads';
|
|
35
|
+
|
|
36
|
+
function ConfirmationDialog({ open, onClose, onConfirm }) {
|
|
37
|
+
return (
|
|
38
|
+
<Modal open={open} onClose={onClose}>
|
|
39
|
+
<DialogFrame
|
|
40
|
+
title="Confirm Action"
|
|
41
|
+
actions={
|
|
42
|
+
<>
|
|
43
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
44
|
+
Cancel
|
|
45
|
+
</Button>
|
|
46
|
+
<Button onClick={onConfirm}>
|
|
47
|
+
Confirm
|
|
48
|
+
</Button>
|
|
49
|
+
</>
|
|
50
|
+
}
|
|
51
|
+
>
|
|
52
|
+
Are you sure you want to proceed with this action?
|
|
53
|
+
</DialogFrame>
|
|
54
|
+
</Modal>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
### Playground
|
|
62
|
+
|
|
63
|
+
Interactive example with title, content, and action buttons.
|
|
21
64
|
|
|
22
65
|
```tsx
|
|
23
|
-
|
|
66
|
+
<DialogFrame
|
|
67
|
+
title="Dialog Title"
|
|
68
|
+
children="Dialog Content"
|
|
69
|
+
actions={actions}
|
|
70
|
+
/>
|
|
24
71
|
```
|
|
25
72
|
|
|
73
|
+
### Fullscreen
|
|
74
|
+
|
|
75
|
+
Fullscreen mode for complex content or mobile views.
|
|
76
|
+
|
|
26
77
|
```tsx
|
|
27
78
|
<DialogFrame
|
|
28
79
|
title="Dialog Title"
|
|
@@ -31,3 +82,554 @@ import { Dialog } from '@ceed/ads';
|
|
|
31
82
|
fullscreen
|
|
32
83
|
/>
|
|
33
84
|
```
|
|
85
|
+
|
|
86
|
+
### With Input (KeyDown Handling)
|
|
87
|
+
|
|
88
|
+
Dialog with form inputs and keyboard event handling.
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<DialogFrame {...args} title="Dialog Title" actions={<Button variant="plain">Action</Button>}>
|
|
92
|
+
Dialog Content
|
|
93
|
+
<Input />
|
|
94
|
+
</DialogFrame>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## When to Use
|
|
98
|
+
|
|
99
|
+
### ✅ Good Use Cases
|
|
100
|
+
|
|
101
|
+
- **Confirmations**: Delete, submit, or irreversible action confirmations
|
|
102
|
+
- **Simple forms**: Login, quick input, or single-field forms
|
|
103
|
+
- **Alerts**: Important information that requires acknowledgment
|
|
104
|
+
- **Choices**: Binary or limited-option decisions
|
|
105
|
+
- **Terms acceptance**: Legal agreements that need explicit consent
|
|
106
|
+
|
|
107
|
+
### ❌ When Not to Use
|
|
108
|
+
|
|
109
|
+
- **Complex forms**: Use a dedicated page for multi-step forms
|
|
110
|
+
- **Notifications**: Use Toast for non-blocking feedback
|
|
111
|
+
- **Inline messages**: Use Alert for contextual information
|
|
112
|
+
- **Frequent interactions**: Don't interrupt common workflows
|
|
113
|
+
- **Large content**: Use a page or InsetDrawer for lengthy content
|
|
114
|
+
|
|
115
|
+
## Common Use Cases
|
|
116
|
+
|
|
117
|
+
### Delete Confirmation
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
function DeleteConfirmation({ itemName, open, onClose, onDelete }) {
|
|
121
|
+
return (
|
|
122
|
+
<Modal open={open} onClose={onClose}>
|
|
123
|
+
<DialogFrame
|
|
124
|
+
title="Delete Item"
|
|
125
|
+
actions={
|
|
126
|
+
<>
|
|
127
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
128
|
+
Cancel
|
|
129
|
+
</Button>
|
|
130
|
+
<Button color="danger" onClick={onDelete}>
|
|
131
|
+
Delete
|
|
132
|
+
</Button>
|
|
133
|
+
</>
|
|
134
|
+
}
|
|
135
|
+
>
|
|
136
|
+
<Typography>
|
|
137
|
+
Are you sure you want to delete <strong>{itemName}</strong>?
|
|
138
|
+
</Typography>
|
|
139
|
+
<Typography level="body-sm" color="neutral" sx={{ mt: 1 }}>
|
|
140
|
+
This action cannot be undone.
|
|
141
|
+
</Typography>
|
|
142
|
+
</DialogFrame>
|
|
143
|
+
</Modal>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Form Dialog
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
function QuickAddDialog({ open, onClose, onSubmit }) {
|
|
152
|
+
const [name, setName] = useState('');
|
|
153
|
+
const [email, setEmail] = useState('');
|
|
154
|
+
|
|
155
|
+
const handleSubmit = () => {
|
|
156
|
+
onSubmit({ name, email });
|
|
157
|
+
setName('');
|
|
158
|
+
setEmail('');
|
|
159
|
+
onClose();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<Modal open={open} onClose={onClose}>
|
|
164
|
+
<DialogFrame
|
|
165
|
+
title="Add New User"
|
|
166
|
+
actions={
|
|
167
|
+
<>
|
|
168
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
169
|
+
Cancel
|
|
170
|
+
</Button>
|
|
171
|
+
<Button onClick={handleSubmit} disabled={!name || !email}>
|
|
172
|
+
Add User
|
|
173
|
+
</Button>
|
|
174
|
+
</>
|
|
175
|
+
}
|
|
176
|
+
>
|
|
177
|
+
<Stack gap={2}>
|
|
178
|
+
<FormControl>
|
|
179
|
+
<FormLabel>Name</FormLabel>
|
|
180
|
+
<Input
|
|
181
|
+
value={name}
|
|
182
|
+
onChange={(e) => setName(e.target.value)}
|
|
183
|
+
placeholder="Enter name"
|
|
184
|
+
autoFocus
|
|
185
|
+
/>
|
|
186
|
+
</FormControl>
|
|
187
|
+
<FormControl>
|
|
188
|
+
<FormLabel>Email</FormLabel>
|
|
189
|
+
<Input
|
|
190
|
+
type="email"
|
|
191
|
+
value={email}
|
|
192
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
193
|
+
placeholder="Enter email"
|
|
194
|
+
/>
|
|
195
|
+
</FormControl>
|
|
196
|
+
</Stack>
|
|
197
|
+
</DialogFrame>
|
|
198
|
+
</Modal>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Unsaved Changes Warning
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
function UnsavedChangesDialog({ open, onClose, onDiscard, onSave }) {
|
|
207
|
+
return (
|
|
208
|
+
<Modal open={open} onClose={onClose}>
|
|
209
|
+
<DialogFrame
|
|
210
|
+
title="Unsaved Changes"
|
|
211
|
+
actions={
|
|
212
|
+
<>
|
|
213
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
214
|
+
Cancel
|
|
215
|
+
</Button>
|
|
216
|
+
<Button variant="outlined" color="danger" onClick={onDiscard}>
|
|
217
|
+
Discard
|
|
218
|
+
</Button>
|
|
219
|
+
<Button onClick={onSave}>
|
|
220
|
+
Save Changes
|
|
221
|
+
</Button>
|
|
222
|
+
</>
|
|
223
|
+
}
|
|
224
|
+
>
|
|
225
|
+
<Typography>
|
|
226
|
+
You have unsaved changes. Would you like to save them before leaving?
|
|
227
|
+
</Typography>
|
|
228
|
+
</DialogFrame>
|
|
229
|
+
</Modal>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Information Dialog
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
function InfoDialog({ open, onClose, title, message }) {
|
|
238
|
+
return (
|
|
239
|
+
<Modal open={open} onClose={onClose}>
|
|
240
|
+
<DialogFrame
|
|
241
|
+
title={title}
|
|
242
|
+
actions={
|
|
243
|
+
<Button onClick={onClose}>
|
|
244
|
+
Got it
|
|
245
|
+
</Button>
|
|
246
|
+
}
|
|
247
|
+
>
|
|
248
|
+
<Typography>{message}</Typography>
|
|
249
|
+
</DialogFrame>
|
|
250
|
+
</Modal>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Terms Acceptance
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
function TermsDialog({ open, onClose, onAccept }) {
|
|
259
|
+
const [accepted, setAccepted] = useState(false);
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<Modal open={open} onClose={onClose}>
|
|
263
|
+
<DialogFrame
|
|
264
|
+
title="Terms and Conditions"
|
|
265
|
+
actions={
|
|
266
|
+
<>
|
|
267
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
268
|
+
Decline
|
|
269
|
+
</Button>
|
|
270
|
+
<Button onClick={onAccept} disabled={!accepted}>
|
|
271
|
+
Accept
|
|
272
|
+
</Button>
|
|
273
|
+
</>
|
|
274
|
+
}
|
|
275
|
+
>
|
|
276
|
+
<Box sx={{ maxHeight: 300, overflow: 'auto', mb: 2 }}>
|
|
277
|
+
<Typography level="body-sm">
|
|
278
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit...
|
|
279
|
+
{/* Terms content */}
|
|
280
|
+
</Typography>
|
|
281
|
+
</Box>
|
|
282
|
+
<Checkbox
|
|
283
|
+
label="I have read and agree to the terms and conditions"
|
|
284
|
+
checked={accepted}
|
|
285
|
+
onChange={(e) => setAccepted(e.target.checked)}
|
|
286
|
+
/>
|
|
287
|
+
</DialogFrame>
|
|
288
|
+
</Modal>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Loading State Dialog
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
function ProcessingDialog({ open, status, onClose }) {
|
|
297
|
+
const isProcessing = status === 'processing';
|
|
298
|
+
const isSuccess = status === 'success';
|
|
299
|
+
const isError = status === 'error';
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<Modal open={open} onClose={isProcessing ? undefined : onClose}>
|
|
303
|
+
<DialogFrame
|
|
304
|
+
title={
|
|
305
|
+
isProcessing ? 'Processing...' :
|
|
306
|
+
isSuccess ? 'Success!' :
|
|
307
|
+
'Error'
|
|
308
|
+
}
|
|
309
|
+
actions={
|
|
310
|
+
!isProcessing && (
|
|
311
|
+
<Button onClick={onClose}>
|
|
312
|
+
{isSuccess ? 'Done' : 'Try Again'}
|
|
313
|
+
</Button>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
>
|
|
317
|
+
<Box sx={{ textAlign: 'center', py: 2 }}>
|
|
318
|
+
{isProcessing && <CircularProgress />}
|
|
319
|
+
{isSuccess && <CheckCircleIcon color="success" sx={{ fontSize: 48 }} />}
|
|
320
|
+
{isError && <ErrorIcon color="error" sx={{ fontSize: 48 }} />}
|
|
321
|
+
<Typography sx={{ mt: 2 }}>
|
|
322
|
+
{isProcessing && 'Please wait while we process your request...'}
|
|
323
|
+
{isSuccess && 'Your request has been processed successfully.'}
|
|
324
|
+
{isError && 'Something went wrong. Please try again.'}
|
|
325
|
+
</Typography>
|
|
326
|
+
</Box>
|
|
327
|
+
</DialogFrame>
|
|
328
|
+
</Modal>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Selection Dialog
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
function SelectOptionDialog({ open, onClose, options, onSelect }) {
|
|
337
|
+
return (
|
|
338
|
+
<Modal open={open} onClose={onClose}>
|
|
339
|
+
<DialogFrame
|
|
340
|
+
title="Select an Option"
|
|
341
|
+
actions={
|
|
342
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
343
|
+
Cancel
|
|
344
|
+
</Button>
|
|
345
|
+
}
|
|
346
|
+
>
|
|
347
|
+
<List>
|
|
348
|
+
{options.map((option) => (
|
|
349
|
+
<ListItem key={option.value}>
|
|
350
|
+
<ListItemButton
|
|
351
|
+
onClick={() => {
|
|
352
|
+
onSelect(option.value);
|
|
353
|
+
onClose();
|
|
354
|
+
}}
|
|
355
|
+
>
|
|
356
|
+
<ListItemDecorator>{option.icon}</ListItemDecorator>
|
|
357
|
+
<ListItemContent>
|
|
358
|
+
<Typography level="title-sm">{option.label}</Typography>
|
|
359
|
+
<Typography level="body-xs">{option.description}</Typography>
|
|
360
|
+
</ListItemContent>
|
|
361
|
+
</ListItemButton>
|
|
362
|
+
</ListItem>
|
|
363
|
+
))}
|
|
364
|
+
</List>
|
|
365
|
+
</DialogFrame>
|
|
366
|
+
</Modal>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Props and Customization
|
|
372
|
+
|
|
373
|
+
### Key Props
|
|
374
|
+
|
|
375
|
+
| Prop | Type | Default | Description |
|
|
376
|
+
| ------------ | -------------------------------- | ------- | -------------------------------------- |
|
|
377
|
+
| `title` | `string` | - | Dialog title displayed in the header |
|
|
378
|
+
| `children` | `ReactNode` | - | Dialog body content |
|
|
379
|
+
| `actions` | `ReactNode` | - | Action buttons displayed in the footer |
|
|
380
|
+
| `fullscreen` | `boolean` | `false` | Enable fullscreen mode |
|
|
381
|
+
| `onKeyDown` | `(event: KeyboardEvent) => void` | - | Keyboard event handler |
|
|
382
|
+
|
|
383
|
+
### Structure
|
|
384
|
+
|
|
385
|
+
DialogFrame provides a three-part layout:
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
<DialogFrame
|
|
389
|
+
title="Header" // Top: Title section
|
|
390
|
+
actions={...} // Bottom: Action buttons
|
|
391
|
+
>
|
|
392
|
+
{/* Middle: Content area */}
|
|
393
|
+
</DialogFrame>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### With Modal
|
|
397
|
+
|
|
398
|
+
DialogFrame should be wrapped in Modal for proper behavior:
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
<Modal open={open} onClose={onClose}>
|
|
402
|
+
<DialogFrame title="Title" actions={actions}>
|
|
403
|
+
Content
|
|
404
|
+
</DialogFrame>
|
|
405
|
+
</Modal>
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Action Button Patterns
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
// Confirmation (Cancel + Confirm)
|
|
412
|
+
<DialogFrame
|
|
413
|
+
actions={
|
|
414
|
+
<>
|
|
415
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
416
|
+
Cancel
|
|
417
|
+
</Button>
|
|
418
|
+
<Button onClick={onConfirm}>Confirm</Button>
|
|
419
|
+
</>
|
|
420
|
+
}
|
|
421
|
+
/>
|
|
422
|
+
|
|
423
|
+
// Destructive action
|
|
424
|
+
<DialogFrame
|
|
425
|
+
actions={
|
|
426
|
+
<>
|
|
427
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
428
|
+
Cancel
|
|
429
|
+
</Button>
|
|
430
|
+
<Button color="danger" onClick={onDelete}>Delete</Button>
|
|
431
|
+
</>
|
|
432
|
+
}
|
|
433
|
+
/>
|
|
434
|
+
|
|
435
|
+
// Single action (acknowledgment)
|
|
436
|
+
<DialogFrame
|
|
437
|
+
actions={
|
|
438
|
+
<Button onClick={onClose}>OK</Button>
|
|
439
|
+
}
|
|
440
|
+
/>
|
|
441
|
+
|
|
442
|
+
// Three options
|
|
443
|
+
<DialogFrame
|
|
444
|
+
actions={
|
|
445
|
+
<>
|
|
446
|
+
<Button variant="plain" color="neutral" onClick={onCancel}>Cancel</Button>
|
|
447
|
+
<Button variant="outlined" onClick={onSecondary}>Don't Save</Button>
|
|
448
|
+
<Button onClick={onPrimary}>Save</Button>
|
|
449
|
+
</>
|
|
450
|
+
}
|
|
451
|
+
/>
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Fullscreen Mode
|
|
455
|
+
|
|
456
|
+
```tsx
|
|
457
|
+
// Fullscreen for complex content or mobile
|
|
458
|
+
<Modal open={open} onClose={onClose}>
|
|
459
|
+
<DialogFrame fullscreen title="Edit Profile">
|
|
460
|
+
<Box sx={{ p: 2 }}>
|
|
461
|
+
{/* Large form or content */}
|
|
462
|
+
</Box>
|
|
463
|
+
</DialogFrame>
|
|
464
|
+
</Modal>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Accessibility
|
|
468
|
+
|
|
469
|
+
DialogFrame inherits accessibility features from Modal:
|
|
470
|
+
|
|
471
|
+
### ARIA Attributes
|
|
472
|
+
|
|
473
|
+
- Dialog has `role="dialog"` via Modal
|
|
474
|
+
- Title provides `aria-labelledby`
|
|
475
|
+
- Focus is trapped within the dialog
|
|
476
|
+
- Background is marked as `aria-hidden`
|
|
477
|
+
|
|
478
|
+
### Keyboard Navigation
|
|
479
|
+
|
|
480
|
+
- **Escape**: Close the dialog (via Modal)
|
|
481
|
+
- **Tab**: Navigate between focusable elements
|
|
482
|
+
- **Enter**: Activate focused button
|
|
483
|
+
|
|
484
|
+
### Focus Management
|
|
485
|
+
|
|
486
|
+
```tsx
|
|
487
|
+
// Auto-focus first input
|
|
488
|
+
<DialogFrame title="Add Item">
|
|
489
|
+
<Input autoFocus placeholder="Item name" />
|
|
490
|
+
</DialogFrame>
|
|
491
|
+
|
|
492
|
+
// Focus returns to trigger when closed
|
|
493
|
+
<Button onClick={() => setOpen(true)}>Open Dialog</Button>
|
|
494
|
+
<Modal open={open} onClose={() => setOpen(false)}>
|
|
495
|
+
<DialogFrame>...</DialogFrame>
|
|
496
|
+
</Modal>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Screen Reader Support
|
|
500
|
+
|
|
501
|
+
```tsx
|
|
502
|
+
// Title provides context
|
|
503
|
+
<DialogFrame title="Confirm Deletion">
|
|
504
|
+
{/* Content is read after title */}
|
|
505
|
+
</DialogFrame>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## Best Practices
|
|
509
|
+
|
|
510
|
+
### ✅ Do
|
|
511
|
+
|
|
512
|
+
1. **Use clear, action-oriented titles**: Tell users what they're confirming
|
|
513
|
+
|
|
514
|
+
```tsx
|
|
515
|
+
// ✅ Good: Specific title
|
|
516
|
+
<DialogFrame title="Delete Account" />
|
|
517
|
+
<DialogFrame title="Save Changes" />
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
2. **Provide clear button labels**: Use verbs that describe the action
|
|
521
|
+
|
|
522
|
+
```tsx
|
|
523
|
+
// ✅ Good: Clear action buttons
|
|
524
|
+
<Button color="danger">Delete</Button>
|
|
525
|
+
<Button>Save Changes</Button>
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
3. **Keep content concise**: Users should quickly understand and decide
|
|
529
|
+
|
|
530
|
+
```tsx
|
|
531
|
+
// ✅ Good: Brief, scannable content
|
|
532
|
+
<DialogFrame title="Delete Project">
|
|
533
|
+
<Typography>
|
|
534
|
+
This will permanently delete the project and all its data.
|
|
535
|
+
</Typography>
|
|
536
|
+
</DialogFrame>
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
4. **Always provide a cancel option**: Let users safely exit
|
|
540
|
+
|
|
541
|
+
```tsx
|
|
542
|
+
// ✅ Good: Cancel button available
|
|
543
|
+
<DialogFrame
|
|
544
|
+
actions={
|
|
545
|
+
<>
|
|
546
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
547
|
+
Cancel
|
|
548
|
+
</Button>
|
|
549
|
+
<Button onClick={onConfirm}>Confirm</Button>
|
|
550
|
+
</>
|
|
551
|
+
}
|
|
552
|
+
/>
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### ❌ Don't
|
|
556
|
+
|
|
557
|
+
1. **Don't use for notifications**: Use Toast instead
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
// ❌ Bad: Dialog for simple feedback
|
|
561
|
+
<DialogFrame title="Success">
|
|
562
|
+
<Typography>Item saved!</Typography>
|
|
563
|
+
</DialogFrame>
|
|
564
|
+
|
|
565
|
+
// ✅ Good: Use Toast
|
|
566
|
+
showToast({ message: 'Item saved!' });
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
2. **Don't include too much content**: Use a page for complex forms
|
|
570
|
+
|
|
571
|
+
```tsx
|
|
572
|
+
// ❌ Bad: Complex form in dialog
|
|
573
|
+
<DialogFrame title="Create Account">
|
|
574
|
+
{/* 20+ form fields */}
|
|
575
|
+
</DialogFrame>
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
3. **Don't use vague button labels**: Be specific about actions
|
|
579
|
+
|
|
580
|
+
```tsx
|
|
581
|
+
// ❌ Bad: Unclear actions
|
|
582
|
+
<Button>OK</Button>
|
|
583
|
+
<Button>Cancel</Button>
|
|
584
|
+
|
|
585
|
+
// ✅ Good: Clear actions
|
|
586
|
+
<Button>Delete Account</Button>
|
|
587
|
+
<Button variant="plain">Keep Account</Button>
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
4. **Don't block critical workflows**: Important features should be accessible
|
|
591
|
+
|
|
592
|
+
## Performance Considerations
|
|
593
|
+
|
|
594
|
+
### Lazy Load Dialog Content
|
|
595
|
+
|
|
596
|
+
For dialogs with heavy content:
|
|
597
|
+
|
|
598
|
+
```tsx
|
|
599
|
+
function HeavyDialog({ open, onClose }) {
|
|
600
|
+
return (
|
|
601
|
+
<Modal open={open} onClose={onClose}>
|
|
602
|
+
<DialogFrame title="Data Preview">
|
|
603
|
+
{open && <HeavyDataComponent />}
|
|
604
|
+
</DialogFrame>
|
|
605
|
+
</Modal>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Memoize Handlers
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
const handleConfirm = useCallback(() => {
|
|
614
|
+
performAction();
|
|
615
|
+
onClose();
|
|
616
|
+
}, [performAction, onClose]);
|
|
617
|
+
|
|
618
|
+
const handleCancel = useCallback(() => {
|
|
619
|
+
onClose();
|
|
620
|
+
}, [onClose]);
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Conditionally Render
|
|
624
|
+
|
|
625
|
+
Unmount dialog completely when not needed:
|
|
626
|
+
|
|
627
|
+
```tsx
|
|
628
|
+
{open && (
|
|
629
|
+
<Modal open={open} onClose={onClose}>
|
|
630
|
+
<DialogFrame>...</DialogFrame>
|
|
631
|
+
</Modal>
|
|
632
|
+
)}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
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.
|