@delightui/components 0.1.104 → 0.1.106

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.
Files changed (120) hide show
  1. package/README.md +104 -1
  2. package/dist/cjs/components/molecules/Modal/DemoModal.d.ts +8 -0
  3. package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  4. package/dist/cjs/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  5. package/dist/cjs/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  6. package/dist/cjs/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  7. package/dist/cjs/components/molecules/Modal/index.d.ts +2 -0
  8. package/dist/cjs/components/molecules/Popover/Popover.presenter.d.ts +26 -0
  9. package/dist/cjs/components/molecules/Select/Option/Option.types.d.ts +6 -0
  10. package/dist/cjs/components/molecules/Select/Select.Context.d.ts +1 -1
  11. package/dist/cjs/components/molecules/Select/Select.d.ts +5 -5
  12. package/dist/cjs/components/molecules/Select/Select.presenter.d.ts +1 -0
  13. package/dist/cjs/components/molecules/Select/Select.types.d.ts +5 -0
  14. package/dist/cjs/components/molecules/Select/index.d.ts +2 -9
  15. package/dist/cjs/components/molecules/index.d.ts +2 -0
  16. package/dist/cjs/components/utils/accessibilityUtils.d.ts +41 -0
  17. package/dist/cjs/components/utils/index.d.ts +2 -0
  18. package/dist/cjs/library.css +13 -0
  19. package/dist/cjs/library.js +2 -2
  20. package/dist/cjs/library.js.map +1 -1
  21. package/dist/esm/components/molecules/Modal/DemoModal.d.ts +8 -0
  22. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.d.ts +41 -0
  23. package/dist/esm/components/molecules/Modal/ModalContext/ModalContext.types.d.ts +87 -0
  24. package/dist/esm/components/molecules/Modal/ModalContext/index.d.ts +3 -0
  25. package/dist/esm/components/molecules/Modal/ModalContext/useModal.d.ts +34 -0
  26. package/dist/esm/components/molecules/Modal/index.d.ts +2 -0
  27. package/dist/esm/components/molecules/Popover/Popover.presenter.d.ts +26 -0
  28. package/dist/esm/components/molecules/Select/Option/Option.types.d.ts +6 -0
  29. package/dist/esm/components/molecules/Select/Select.Context.d.ts +1 -1
  30. package/dist/esm/components/molecules/Select/Select.d.ts +5 -5
  31. package/dist/esm/components/molecules/Select/Select.presenter.d.ts +1 -0
  32. package/dist/esm/components/molecules/Select/Select.types.d.ts +5 -0
  33. package/dist/esm/components/molecules/Select/index.d.ts +2 -9
  34. package/dist/esm/components/molecules/index.d.ts +2 -0
  35. package/dist/esm/components/utils/accessibilityUtils.d.ts +41 -0
  36. package/dist/esm/components/utils/index.d.ts +2 -0
  37. package/dist/esm/library.css +13 -0
  38. package/dist/esm/library.js +3 -3
  39. package/dist/esm/library.js.map +1 -1
  40. package/dist/index.d.ts +156 -12
  41. package/docs/README.md +264 -0
  42. package/docs/components/atoms/ActionImage.md +119 -0
  43. package/docs/components/atoms/Button.md +197 -0
  44. package/docs/components/atoms/Checkbox.md +299 -0
  45. package/docs/components/atoms/CheckboxItem.md +314 -0
  46. package/docs/components/atoms/Chip.md +380 -0
  47. package/docs/components/atoms/CustomToggle.md +270 -0
  48. package/docs/components/atoms/Icon.md +365 -0
  49. package/docs/components/atoms/IconButton.md +407 -0
  50. package/docs/components/atoms/Image.md +448 -0
  51. package/docs/components/atoms/Input.md +430 -0
  52. package/docs/components/atoms/ListItem.md +502 -0
  53. package/docs/components/atoms/Password.md +472 -0
  54. package/docs/components/atoms/RadioButton.md +614 -0
  55. package/docs/components/atoms/RadioButtonItem.md +588 -0
  56. package/docs/components/atoms/ResponsiveComponent.md +612 -0
  57. package/docs/components/atoms/SelectListItem.md +609 -0
  58. package/docs/components/atoms/Slider.md +605 -0
  59. package/docs/components/atoms/Spinner.md +605 -0
  60. package/docs/components/atoms/Text.md +463 -0
  61. package/docs/components/atoms/TextArea.md +670 -0
  62. package/docs/components/atoms/ToastNotification.md +668 -0
  63. package/docs/components/atoms/Toggle.md +737 -0
  64. package/docs/components/atoms/ToggleButton.md +751 -0
  65. package/docs/components/atoms/Tooltip.md +391 -0
  66. package/docs/components/molecules/Accordion.md +440 -0
  67. package/docs/components/molecules/AccordionGroup.md +547 -0
  68. package/docs/components/molecules/ActionCard.md +546 -0
  69. package/docs/components/molecules/Breadcrumb.md +403 -0
  70. package/docs/components/molecules/Breadcrumbs.md +485 -0
  71. package/docs/components/molecules/ButtonGroup.md +383 -0
  72. package/docs/components/molecules/Card.md +298 -0
  73. package/docs/components/molecules/ChipInput.md +646 -0
  74. package/docs/components/molecules/ContextMenu.md +768 -0
  75. package/docs/components/molecules/CustomTimeSelector.md +116 -0
  76. package/docs/components/molecules/DatePicker.md +516 -0
  77. package/docs/components/molecules/DateTimeSelector.md +166 -0
  78. package/docs/components/molecules/FormField.md +312 -0
  79. package/docs/components/molecules/Grid.md +577 -0
  80. package/docs/components/molecules/GridItem.md +834 -0
  81. package/docs/components/molecules/GridList.md +244 -0
  82. package/docs/components/molecules/List.md +485 -0
  83. package/docs/components/molecules/Modal.md +470 -0
  84. package/docs/components/molecules/ModalFooter.md +702 -0
  85. package/docs/components/molecules/ModalHeader.md +756 -0
  86. package/docs/components/molecules/ModalProvider.md +205 -0
  87. package/docs/components/molecules/Nav.md +530 -0
  88. package/docs/components/molecules/NavItem.md +572 -0
  89. package/docs/components/molecules/NavLink.md +499 -0
  90. package/docs/components/molecules/Option.md +521 -0
  91. package/docs/components/molecules/Pagination.md +592 -0
  92. package/docs/components/molecules/PaginationNumberField.md +722 -0
  93. package/docs/components/molecules/Popover.md +516 -0
  94. package/docs/components/molecules/ProgressBar.md +624 -0
  95. package/docs/components/molecules/RadioGroup.md +831 -0
  96. package/docs/components/molecules/RepeaterList.md +185 -0
  97. package/docs/components/molecules/Select.md +402 -0
  98. package/docs/components/molecules/SortableTrigger.md +82 -0
  99. package/docs/components/molecules/useModal.md +379 -0
  100. package/docs/components/organisms/Dropzone.md +346 -0
  101. package/docs/components/organisms/DropzoneClear.md +135 -0
  102. package/docs/components/organisms/DropzoneContent.md +216 -0
  103. package/docs/components/organisms/DropzoneFilename.md +191 -0
  104. package/docs/components/organisms/DropzoneSupportedFormats.md +184 -0
  105. package/docs/components/organisms/DropzoneTrigger.md +209 -0
  106. package/docs/components/organisms/Form.md +533 -0
  107. package/docs/components/organisms/SlideOutPanel.md +662 -0
  108. package/docs/components/organisms/TabContent.md +902 -0
  109. package/docs/components/organisms/TabItem.md +1091 -0
  110. package/docs/components/organisms/Table.md +611 -0
  111. package/docs/components/organisms/TableBody.md +679 -0
  112. package/docs/components/organisms/TableCell.md +482 -0
  113. package/docs/components/organisms/TableHeader.md +513 -0
  114. package/docs/components/organisms/TableHeaderCell.md +661 -0
  115. package/docs/components/organisms/TableRow.md +715 -0
  116. package/docs/components/organisms/Tabs.md +1330 -0
  117. package/docs/components/utils/ConditionalView.md +568 -0
  118. package/docs/components/utils/RenderStateView.md +726 -0
  119. package/docs/components/utils/WrapTextNodes.md +614 -0
  120. package/package.json +3 -2
@@ -0,0 +1,702 @@
1
+ # ModalFooter
2
+
3
+ ## Description
4
+
5
+ A specialized footer component designed for modal dialogs that provides structured button layouts and styling options. Supports both single and dual button configurations with customizable styling variants including stroke options for visual separation from modal content.
6
+
7
+ ## Aliases
8
+
9
+ - ModalFooter
10
+ - DialogFooter
11
+ - ModalActions
12
+ - DialogActions
13
+ - ModalButtons
14
+
15
+ ## Props Breakdown
16
+
17
+ **Extends:** Omit<HTMLAttributes<HTMLDivElement>, 'style'> (inherits div properties except style)
18
+
19
+ | Prop | Type | Default | Required | Description |
20
+ |------|------|---------|----------|-------------|
21
+ | `type` | `'1Button' \| '2Buttons'` | `'1Button'` | No | Layout type - single or dual button configuration |
22
+ | `style` | `'NoStroke' \| 'WithStroke'` | `'NoStroke'` | No | Visual style variant with optional top border |
23
+ | `primaryButton` | `ReactNode` | - | No | Primary action button element |
24
+ | `secondaryButton` | `ReactNode` | - | No | Secondary action button (only for 2Buttons type) |
25
+ | `className` | `string` | - | No | Additional CSS class names |
26
+ | `children` | `ReactNode` | - | No | Custom content overriding button props |
27
+
28
+ ## Examples
29
+
30
+ ### Basic Single Button Footer
31
+ ```tsx
32
+ import { Modal, ModalFooter, Button, Text } from '@delightui/components';
33
+
34
+ function SingleButtonExample() {
35
+ const [showModal, setShowModal] = useState(false);
36
+
37
+ return (
38
+ <>
39
+ <Button onClick={() => setShowModal(true)}>
40
+ Show Info Modal
41
+ </Button>
42
+
43
+ <Modal show={showModal} onHide={() => setShowModal(false)}>
44
+ <div className="modal-content">
45
+ <Text type="Heading4">Information</Text>
46
+ <Text>This is an informational modal with a single action button.</Text>
47
+ </div>
48
+
49
+ <ModalFooter
50
+ type="1Button"
51
+ primaryButton={
52
+ <Button onClick={() => setShowModal(false)}>
53
+ Got it
54
+ </Button>
55
+ }
56
+ />
57
+ </Modal>
58
+ </>
59
+ );
60
+ }
61
+ ```
62
+
63
+ ### Confirmation Modal with Two Buttons
64
+ ```tsx
65
+ function ConfirmationExample() {
66
+ const [showModal, setShowModal] = useState(false);
67
+ const [loading, setLoading] = useState(false);
68
+
69
+ const handleConfirm = async () => {
70
+ setLoading(true);
71
+ // Simulate API call
72
+ await new Promise(resolve => setTimeout(resolve, 2000));
73
+ setLoading(false);
74
+ setShowModal(false);
75
+ };
76
+
77
+ return (
78
+ <>
79
+ <Button style="Destructive" onClick={() => setShowModal(true)}>
80
+ Delete Item
81
+ </Button>
82
+
83
+ <Modal
84
+ show={showModal}
85
+ onHide={() => setShowModal(false)}
86
+ disableBackdropDismiss={loading}
87
+ >
88
+ <div className="modal-content">
89
+ <Text type="Heading4">Confirm Deletion</Text>
90
+ <Text>
91
+ Are you sure you want to delete this item? This action cannot be undone.
92
+ </Text>
93
+ </div>
94
+
95
+ <ModalFooter
96
+ type="2Buttons"
97
+ style="WithStroke"
98
+ secondaryButton={
99
+ <Button
100
+ type="Outlined"
101
+ onClick={() => setShowModal(false)}
102
+ disabled={loading}
103
+ >
104
+ Cancel
105
+ </Button>
106
+ }
107
+ primaryButton={
108
+ <Button
109
+ style="Destructive"
110
+ loading={loading}
111
+ onClick={handleConfirm}
112
+ >
113
+ Delete
114
+ </Button>
115
+ }
116
+ />
117
+ </Modal>
118
+ </>
119
+ );
120
+ }
121
+ ```
122
+
123
+ ### Form Modal Footer
124
+ ```tsx
125
+ function FormModalExample() {
126
+ const [showModal, setShowModal] = useState(false);
127
+ const [formData, setFormData] = useState({ name: '', email: '' });
128
+ const [saving, setSaving] = useState(false);
129
+
130
+ const handleSubmit = async () => {
131
+ setSaving(true);
132
+ // Simulate form submission
133
+ await new Promise(resolve => setTimeout(resolve, 1500));
134
+ setSaving(false);
135
+ setShowModal(false);
136
+ setFormData({ name: '', email: '' });
137
+ };
138
+
139
+ const isFormValid = formData.name.trim() && formData.email.trim();
140
+
141
+ return (
142
+ <>
143
+ <Button onClick={() => setShowModal(true)}>
144
+ Add New User
145
+ </Button>
146
+
147
+ <Modal show={showModal} onHide={() => setShowModal(false)} size="Medium">
148
+ <div className="modal-content">
149
+ <Text type="Heading4">Add New User</Text>
150
+
151
+ <FormField label="Full Name" required>
152
+ <Input
153
+ value={formData.name}
154
+ onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
155
+ placeholder="Enter full name"
156
+ />
157
+ </FormField>
158
+
159
+ <FormField label="Email Address" required>
160
+ <Input
161
+ type="email"
162
+ value={formData.email}
163
+ onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
164
+ placeholder="Enter email address"
165
+ />
166
+ </FormField>
167
+ </div>
168
+
169
+ <ModalFooter
170
+ type="2Buttons"
171
+ style="WithStroke"
172
+ secondaryButton={
173
+ <Button
174
+ type="Outlined"
175
+ onClick={() => setShowModal(false)}
176
+ disabled={saving}
177
+ >
178
+ Cancel
179
+ </Button>
180
+ }
181
+ primaryButton={
182
+ <Button
183
+ onClick={handleSubmit}
184
+ loading={saving}
185
+ disabled={!isFormValid}
186
+ >
187
+ Add User
188
+ </Button>
189
+ }
190
+ />
191
+ </Modal>
192
+ </>
193
+ );
194
+ }
195
+ ```
196
+
197
+ ### Settings Modal Footer
198
+ ```tsx
199
+ function SettingsModalExample() {
200
+ const [showModal, setShowModal] = useState(false);
201
+ const [settings, setSettings] = useState({
202
+ notifications: true,
203
+ darkMode: false,
204
+ autoSave: true
205
+ });
206
+ const [hasChanges, setHasChanges] = useState(false);
207
+
208
+ const handleSettingChange = (key, value) => {
209
+ setSettings(prev => ({ ...prev, [key]: value }));
210
+ setHasChanges(true);
211
+ };
212
+
213
+ const handleSave = () => {
214
+ console.log('Saving settings:', settings);
215
+ setHasChanges(false);
216
+ setShowModal(false);
217
+ };
218
+
219
+ const handleReset = () => {
220
+ setSettings({
221
+ notifications: true,
222
+ darkMode: false,
223
+ autoSave: true
224
+ });
225
+ setHasChanges(false);
226
+ };
227
+
228
+ return (
229
+ <>
230
+ <Button onClick={() => setShowModal(true)}>
231
+ Settings
232
+ </Button>
233
+
234
+ <Modal show={showModal} onHide={() => setShowModal(false)}>
235
+ <div className="modal-content">
236
+ <Text type="Heading4">Application Settings</Text>
237
+
238
+ <div className="settings-list">
239
+ <FormField label="Enable Notifications">
240
+ <Toggle
241
+ checked={settings.notifications}
242
+ onChange={(checked) => handleSettingChange('notifications', checked)}
243
+ />
244
+ </FormField>
245
+
246
+ <FormField label="Dark Mode">
247
+ <Toggle
248
+ checked={settings.darkMode}
249
+ onChange={(checked) => handleSettingChange('darkMode', checked)}
250
+ />
251
+ </FormField>
252
+
253
+ <FormField label="Auto Save">
254
+ <Toggle
255
+ checked={settings.autoSave}
256
+ onChange={(checked) => handleSettingChange('autoSave', checked)}
257
+ />
258
+ </FormField>
259
+ </div>
260
+ </div>
261
+
262
+ <ModalFooter
263
+ type="2Buttons"
264
+ style="WithStroke"
265
+ secondaryButton={
266
+ <ButtonGroup>
267
+ <Button
268
+ type="Text"
269
+ onClick={handleReset}
270
+ disabled={!hasChanges}
271
+ >
272
+ Reset
273
+ </Button>
274
+ <Button
275
+ type="Outlined"
276
+ onClick={() => setShowModal(false)}
277
+ >
278
+ Cancel
279
+ </Button>
280
+ </ButtonGroup>
281
+ }
282
+ primaryButton={
283
+ <Button
284
+ onClick={handleSave}
285
+ disabled={!hasChanges}
286
+ >
287
+ Save Changes
288
+ </Button>
289
+ }
290
+ />
291
+ </Modal>
292
+ </>
293
+ );
294
+ }
295
+ ```
296
+
297
+ ### Custom Footer Content
298
+ ```tsx
299
+ function CustomFooterExample() {
300
+ const [showModal, setShowModal] = useState(false);
301
+ const [step, setStep] = useState(1);
302
+ const totalSteps = 3;
303
+
304
+ const nextStep = () => setStep(prev => Math.min(prev + 1, totalSteps));
305
+ const prevStep = () => setStep(prev => Math.max(prev - 1, 1));
306
+
307
+ return (
308
+ <>
309
+ <Button onClick={() => setShowModal(true)}>
310
+ Start Wizard
311
+ </Button>
312
+
313
+ <Modal show={showModal} onHide={() => setShowModal(false)}>
314
+ <div className="modal-content">
315
+ <Text type="Heading4">Setup Wizard - Step {step} of {totalSteps}</Text>
316
+
317
+ {step === 1 && (
318
+ <div>
319
+ <Text>Welcome to the setup wizard. We'll help you get started.</Text>
320
+ </div>
321
+ )}
322
+
323
+ {step === 2 && (
324
+ <div>
325
+ <Text>Please configure your preferences.</Text>
326
+ <FormField label="Your Name">
327
+ <Input placeholder="Enter your name" />
328
+ </FormField>
329
+ </div>
330
+ )}
331
+
332
+ {step === 3 && (
333
+ <div>
334
+ <Text>Setup complete! You're ready to go.</Text>
335
+ </div>
336
+ )}
337
+ </div>
338
+
339
+ <ModalFooter style="WithStroke">
340
+ <div className="wizard-footer">
341
+ <div className="step-indicator">
342
+ {Array.from({ length: totalSteps }, (_, i) => (
343
+ <div
344
+ key={i}
345
+ className={`step-dot ${i + 1 <= step ? 'active' : ''}`}
346
+ />
347
+ ))}
348
+ </div>
349
+
350
+ <div className="wizard-actions">
351
+ <Button
352
+ type="Outlined"
353
+ onClick={step === 1 ? () => setShowModal(false) : prevStep}
354
+ >
355
+ {step === 1 ? 'Cancel' : 'Back'}
356
+ </Button>
357
+
358
+ <Button
359
+ onClick={step === totalSteps ? () => setShowModal(false) : nextStep}
360
+ >
361
+ {step === totalSteps ? 'Finish' : 'Next'}
362
+ </Button>
363
+ </div>
364
+ </div>
365
+ </ModalFooter>
366
+ </Modal>
367
+ </>
368
+ );
369
+ }
370
+ ```
371
+
372
+ ### Save Draft Modal
373
+ ```tsx
374
+ function SaveDraftExample() {
375
+ const [showModal, setShowModal] = useState(false);
376
+ const [content, setContent] = useState('');
377
+ const [saving, setSaving] = useState(false);
378
+
379
+ const handleSaveDraft = async () => {
380
+ setSaving(true);
381
+ // Simulate saving draft
382
+ await new Promise(resolve => setTimeout(resolve, 1000));
383
+ setSaving(false);
384
+ setShowModal(false);
385
+ };
386
+
387
+ const handlePublish = async () => {
388
+ setSaving(true);
389
+ // Simulate publishing
390
+ await new Promise(resolve => setTimeout(resolve, 2000));
391
+ setSaving(false);
392
+ setShowModal(false);
393
+ };
394
+
395
+ return (
396
+ <>
397
+ <Button onClick={() => setShowModal(true)}>
398
+ Create Post
399
+ </Button>
400
+
401
+ <Modal show={showModal} onHide={() => setShowModal(false)} size="Large">
402
+ <div className="modal-content">
403
+ <Text type="Heading4">Create New Post</Text>
404
+
405
+ <FormField label="Post Content">
406
+ <TextArea
407
+ value={content}
408
+ onChange={(e) => setContent(e.target.value)}
409
+ placeholder="Write your post content here..."
410
+ rows={6}
411
+ />
412
+ </FormField>
413
+ </div>
414
+
415
+ <ModalFooter style="WithStroke">
416
+ <div className="post-footer">
417
+ <div className="post-info">
418
+ <Text type="BodySmall">
419
+ {content.length} characters
420
+ </Text>
421
+ </div>
422
+
423
+ <div className="post-actions">
424
+ <Button
425
+ type="Text"
426
+ onClick={() => setShowModal(false)}
427
+ disabled={saving}
428
+ >
429
+ Discard
430
+ </Button>
431
+
432
+ <Button
433
+ type="Outlined"
434
+ onClick={handleSaveDraft}
435
+ loading={saving}
436
+ disabled={!content.trim()}
437
+ >
438
+ Save Draft
439
+ </Button>
440
+
441
+ <Button
442
+ onClick={handlePublish}
443
+ loading={saving}
444
+ disabled={!content.trim()}
445
+ >
446
+ Publish
447
+ </Button>
448
+ </div>
449
+ </div>
450
+ </ModalFooter>
451
+ </Modal>
452
+ </>
453
+ );
454
+ }
455
+ ```
456
+
457
+ ### Error Modal Footer
458
+ ```tsx
459
+ function ErrorModalExample() {
460
+ const [showModal, setShowModal] = useState(false);
461
+ const [error] = useState({
462
+ title: 'Connection Error',
463
+ message: 'Unable to connect to the server. Please check your internet connection and try again.',
464
+ details: 'Error code: 500 - Internal Server Error'
465
+ });
466
+
467
+ const handleRetry = () => {
468
+ // Simulate retry logic
469
+ console.log('Retrying...');
470
+ setShowModal(false);
471
+ };
472
+
473
+ return (
474
+ <>
475
+ <Button onClick={() => setShowModal(true)}>
476
+ Trigger Error
477
+ </Button>
478
+
479
+ <Modal show={showModal} onHide={() => setShowModal(false)}>
480
+ <div className="modal-content error-modal">
481
+ <div className="error-icon">
482
+ <Icon icon="Error" size="Large" className="error-icon-style" />
483
+ </div>
484
+
485
+ <Text type="Heading4">{error.title}</Text>
486
+ <Text type="Body">{error.message}</Text>
487
+
488
+ <details className="error-details">
489
+ <summary>Technical Details</summary>
490
+ <Text type="BodySmall" className="error-details-text">
491
+ {error.details}
492
+ </Text>
493
+ </details>
494
+ </div>
495
+
496
+ <ModalFooter
497
+ type="2Buttons"
498
+ style="WithStroke"
499
+ secondaryButton={
500
+ <Button
501
+ type="Outlined"
502
+ onClick={() => setShowModal(false)}
503
+ >
504
+ Close
505
+ </Button>
506
+ }
507
+ primaryButton={
508
+ <Button onClick={handleRetry}>
509
+ Retry
510
+ </Button>
511
+ }
512
+ />
513
+ </Modal>
514
+ </>
515
+ );
516
+ }
517
+ ```
518
+
519
+ ### Terms and Conditions Modal
520
+ ```tsx
521
+ function TermsModalExample() {
522
+ const [showModal, setShowModal] = useState(false);
523
+ const [accepted, setAccepted] = useState(false);
524
+ const [scrolledToBottom, setScrolledToBottom] = useState(false);
525
+
526
+ const handleScroll = (event) => {
527
+ const { scrollTop, scrollHeight, clientHeight } = event.target;
528
+ const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10;
529
+ setScrolledToBottom(isAtBottom);
530
+ };
531
+
532
+ const handleAccept = () => {
533
+ setAccepted(true);
534
+ setShowModal(false);
535
+ console.log('Terms accepted');
536
+ };
537
+
538
+ return (
539
+ <>
540
+ <Button onClick={() => setShowModal(true)}>
541
+ View Terms & Conditions
542
+ </Button>
543
+
544
+ <Modal show={showModal} onHide={() => setShowModal(false)} size="Large">
545
+ <div className="modal-content">
546
+ <Text type="Heading4">Terms and Conditions</Text>
547
+
548
+ <div
549
+ className="terms-content"
550
+ onScroll={handleScroll}
551
+ style={{
552
+ maxHeight: '400px',
553
+ overflowY: 'auto',
554
+ border: '1px solid #ccc',
555
+ padding: '16px',
556
+ marginBottom: '16px'
557
+ }}
558
+ >
559
+ <Text type="Body">
560
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
561
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
562
+ veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
563
+ commodo consequat...
564
+ </Text>
565
+
566
+ <Text type="Body">
567
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
568
+ dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
569
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum...
570
+ </Text>
571
+
572
+ {/* Add more content to make it scrollable */}
573
+ {Array.from({ length: 20 }, (_, i) => (
574
+ <Text key={i} type="Body">
575
+ Section {i + 1}: Additional terms and conditions content that requires
576
+ scrolling to read completely...
577
+ </Text>
578
+ ))}
579
+ </div>
580
+
581
+ <FormField>
582
+ <Checkbox
583
+ checked={accepted}
584
+ onChange={setAccepted}
585
+ label="I have read and accept the terms and conditions"
586
+ disabled={!scrolledToBottom}
587
+ />
588
+ </FormField>
589
+ </div>
590
+
591
+ <ModalFooter
592
+ type="2Buttons"
593
+ style="WithStroke"
594
+ secondaryButton={
595
+ <Button
596
+ type="Outlined"
597
+ onClick={() => setShowModal(false)}
598
+ >
599
+ Cancel
600
+ </Button>
601
+ }
602
+ primaryButton={
603
+ <Button
604
+ onClick={handleAccept}
605
+ disabled={!accepted || !scrolledToBottom}
606
+ >
607
+ Accept & Continue
608
+ </Button>
609
+ }
610
+ />
611
+ </Modal>
612
+ </>
613
+ );
614
+ }
615
+ ```
616
+
617
+ ### Loading Modal Footer
618
+ ```tsx
619
+ function LoadingModalExample() {
620
+ const [showModal, setShowModal] = useState(false);
621
+ const [uploadProgress, setUploadProgress] = useState(0);
622
+ const [isUploading, setIsUploading] = useState(false);
623
+
624
+ const simulateUpload = async () => {
625
+ setIsUploading(true);
626
+ setUploadProgress(0);
627
+
628
+ // Simulate upload progress
629
+ for (let i = 0; i <= 100; i += 10) {
630
+ await new Promise(resolve => setTimeout(resolve, 200));
631
+ setUploadProgress(i);
632
+ }
633
+
634
+ setIsUploading(false);
635
+ };
636
+
637
+ const startUpload = () => {
638
+ setShowModal(true);
639
+ simulateUpload();
640
+ };
641
+
642
+ return (
643
+ <>
644
+ <Button onClick={startUpload}>
645
+ Upload File
646
+ </Button>
647
+
648
+ <Modal
649
+ show={showModal}
650
+ onHide={() => setShowModal(false)}
651
+ disableBackdropDismiss={isUploading}
652
+ >
653
+ <div className="modal-content">
654
+ <Text type="Heading4">File Upload</Text>
655
+
656
+ <div className="upload-progress">
657
+ <Text type="Body">
658
+ {isUploading ? 'Uploading...' : 'Upload Complete!'}
659
+ </Text>
660
+
661
+ <ProgressBar
662
+ value={uploadProgress}
663
+ max={100}
664
+ className="upload-progress-bar"
665
+ />
666
+
667
+ <Text type="BodySmall">
668
+ {uploadProgress}% complete
669
+ </Text>
670
+ </div>
671
+ </div>
672
+
673
+ <ModalFooter
674
+ type={isUploading ? "1Button" : "2Buttons"}
675
+ style="WithStroke"
676
+ secondaryButton={
677
+ !isUploading ? (
678
+ <Button
679
+ type="Outlined"
680
+ onClick={() => setShowModal(false)}
681
+ >
682
+ Close
683
+ </Button>
684
+ ) : undefined
685
+ }
686
+ primaryButton={
687
+ isUploading ? (
688
+ <Button disabled loading>
689
+ Uploading...
690
+ </Button>
691
+ ) : (
692
+ <Button onClick={() => setShowModal(false)}>
693
+ Done
694
+ </Button>
695
+ )
696
+ }
697
+ />
698
+ </Modal>
699
+ </>
700
+ );
701
+ }
702
+ ```