@sproutsocial/seeds-react-modal 1.1.0 → 2.0.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/.turbo/turbo-build.log +23 -23
- package/CHANGELOG.md +182 -0
- package/dist/ModalAction-BB7qJtQj.d.mts +445 -0
- package/dist/ModalAction-BB7qJtQj.d.ts +445 -0
- package/dist/esm/chunk-ETVICNHP.js +1353 -0
- package/dist/esm/chunk-ETVICNHP.js.map +1 -0
- package/dist/esm/index.js +11 -11
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/v2/index.js +12 -20
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1154 -546
- package/dist/index.js.map +1 -1
- package/dist/v2/index.d.mts +4 -11
- package/dist/v2/index.d.ts +4 -11
- package/dist/v2/index.js +1152 -545
- package/dist/v2/index.js.map +1 -1
- package/package.json +8 -7
- package/src/index.ts +11 -12
- package/src/shared/constants.ts +11 -7
- package/src/v2/Modal.tsx +169 -0
- package/src/v2/ModalTypes.ts +343 -0
- package/src/v2/ModalV2.stories.tsx +413 -128
- package/src/v2/MotionConfig.ts +185 -0
- package/src/v2/components/ModalAction.tsx +94 -0
- package/src/v2/components/ModalBody.tsx +54 -0
- package/src/v2/components/ModalCloseWrapper.tsx +35 -0
- package/src/v2/components/ModalContent.tsx +288 -11
- package/src/v2/components/ModalDescription.tsx +14 -2
- package/src/v2/components/ModalFooter.tsx +94 -13
- package/src/v2/components/ModalHeader.tsx +77 -34
- package/src/v2/components/ModalOverlay.tsx +52 -0
- package/src/v2/components/ModalRail.tsx +35 -99
- package/src/v2/components/index.ts +11 -7
- package/src/v2/index.ts +13 -16
- package/dist/ModalRail-5PeilhW7.d.mts +0 -186
- package/dist/ModalRail-5PeilhW7.d.ts +0 -186
- package/dist/esm/chunk-4ITF4DBY.js +0 -717
- package/dist/esm/chunk-4ITF4DBY.js.map +0 -1
- package/src/v2/ModalV2.tsx +0 -388
- package/src/v2/ModalV2Styles.tsx +0 -180
- package/src/v2/ModalV2Types.ts +0 -154
- package/src/v2/components/ModalClose.tsx +0 -29
- package/src/v2/components/ModalCloseButton.tsx +0 -100
- package/src/v2/components/ModalTrigger.tsx +0 -39
|
@@ -4,10 +4,17 @@ import { Box } from "@sproutsocial/seeds-react-box";
|
|
|
4
4
|
import { Button } from "@sproutsocial/seeds-react-button";
|
|
5
5
|
import Text from "@sproutsocial/seeds-react-text";
|
|
6
6
|
import { FormField } from "@sproutsocial/seeds-react-form-field";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Modal,
|
|
9
|
+
ModalHeader,
|
|
10
|
+
ModalFooter,
|
|
11
|
+
ModalBody,
|
|
12
|
+
ModalCloseWrapper,
|
|
13
|
+
ModalCustomFooter,
|
|
14
|
+
} from ".";
|
|
8
15
|
|
|
9
16
|
const meta: Meta<typeof Modal> = {
|
|
10
|
-
title: "Components/Modal
|
|
17
|
+
title: "Components/Modal V2",
|
|
11
18
|
component: Modal,
|
|
12
19
|
parameters: {
|
|
13
20
|
docs: {
|
|
@@ -51,16 +58,6 @@ const meta: Meta<typeof Modal> = {
|
|
|
51
58
|
control: { type: "text" },
|
|
52
59
|
description: "Modal description (auto-wrapped in Dialog.Description)",
|
|
53
60
|
},
|
|
54
|
-
size: {
|
|
55
|
-
control: { type: "select" },
|
|
56
|
-
options: ["small", "medium", "large", "full", "400px", "800px"],
|
|
57
|
-
description: "Modal size preset or custom width",
|
|
58
|
-
},
|
|
59
|
-
priority: {
|
|
60
|
-
control: { type: "select" },
|
|
61
|
-
options: ["low", "medium", "high"],
|
|
62
|
-
description: "Priority level for z-index",
|
|
63
|
-
},
|
|
64
61
|
draggable: {
|
|
65
62
|
control: { type: "boolean" },
|
|
66
63
|
description: "Enable draggable functionality",
|
|
@@ -69,7 +66,6 @@ const meta: Meta<typeof Modal> = {
|
|
|
69
66
|
args: {
|
|
70
67
|
"aria-label": "Example Modal",
|
|
71
68
|
showOverlay: true,
|
|
72
|
-
size: "medium",
|
|
73
69
|
draggable: false,
|
|
74
70
|
},
|
|
75
71
|
};
|
|
@@ -82,34 +78,19 @@ export const Default: Story = {
|
|
|
82
78
|
return (
|
|
83
79
|
<Modal
|
|
84
80
|
{...args}
|
|
81
|
+
closeButtonAriaLabel="Close Modal"
|
|
85
82
|
modalTrigger={<Button appearance="primary">Open Modal</Button>}
|
|
86
83
|
>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
>
|
|
96
|
-
|
|
97
|
-
This modal uses uncontrolled state - no need to manage open/close
|
|
98
|
-
state! The modalTrigger prop and floating close button handle
|
|
99
|
-
everything.
|
|
100
|
-
</Text>
|
|
101
|
-
</Box>
|
|
102
|
-
<ModalFooter>
|
|
103
|
-
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
104
|
-
<ModalClose asChild>
|
|
105
|
-
<Button>Cancel</Button>
|
|
106
|
-
</ModalClose>
|
|
107
|
-
<ModalClose asChild>
|
|
108
|
-
<Button appearance="primary">Confirm</Button>
|
|
109
|
-
</ModalClose>
|
|
110
|
-
</Box>
|
|
111
|
-
</ModalFooter>
|
|
112
|
-
</ModalContent>
|
|
84
|
+
<ModalHeader title="Modal Title" subtitle="This is a subtitle" />
|
|
85
|
+
<ModalBody>
|
|
86
|
+
This modal uses uncontrolled state - no need to manage open/close
|
|
87
|
+
state! The modalTrigger prop and floating close button handle
|
|
88
|
+
everything.
|
|
89
|
+
</ModalBody>
|
|
90
|
+
<ModalFooter
|
|
91
|
+
cancelButton={<Button>Cancel</Button>}
|
|
92
|
+
primaryButton={<Button appearance="primary">Confirm</Button>}
|
|
93
|
+
/>
|
|
113
94
|
</Modal>
|
|
114
95
|
);
|
|
115
96
|
},
|
|
@@ -119,7 +100,7 @@ export const WithActions: Story = {
|
|
|
119
100
|
render: () => {
|
|
120
101
|
return (
|
|
121
102
|
<Modal
|
|
122
|
-
|
|
103
|
+
aria-label="Modal With Actions"
|
|
123
104
|
modalTrigger={
|
|
124
105
|
<Button appearance="primary">Open Modal With Actions</Button>
|
|
125
106
|
}
|
|
@@ -136,35 +117,21 @@ export const WithActions: Story = {
|
|
|
136
117
|
},
|
|
137
118
|
]}
|
|
138
119
|
>
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
button is always present, and additional actions appear below it.
|
|
155
|
-
</Text>
|
|
156
|
-
</Box>
|
|
157
|
-
<ModalFooter>
|
|
158
|
-
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
159
|
-
<ModalClose asChild>
|
|
160
|
-
<Button>Cancel</Button>
|
|
161
|
-
</ModalClose>
|
|
162
|
-
<ModalClose asChild>
|
|
163
|
-
<Button appearance="primary">Confirm</Button>
|
|
164
|
-
</ModalClose>
|
|
165
|
-
</Box>
|
|
166
|
-
</ModalFooter>
|
|
167
|
-
</ModalContent>
|
|
120
|
+
<ModalHeader
|
|
121
|
+
title="Modal With Actions"
|
|
122
|
+
subtitle="This modal has extra floating actions"
|
|
123
|
+
/>
|
|
124
|
+
<ModalBody>
|
|
125
|
+
<Text>
|
|
126
|
+
This modal shows the floating close by default and includes extra
|
|
127
|
+
actions on the rail via the actions prop. The floating close button
|
|
128
|
+
is always present, and additional actions appear below it.
|
|
129
|
+
</Text>
|
|
130
|
+
</ModalBody>
|
|
131
|
+
<ModalFooter
|
|
132
|
+
cancelButton={<Button>Cancel</Button>}
|
|
133
|
+
primaryButton={<Button appearance="primary">Confirm</Button>}
|
|
134
|
+
/>
|
|
168
135
|
</Modal>
|
|
169
136
|
);
|
|
170
137
|
},
|
|
@@ -175,23 +142,22 @@ export const DraggableModal: Story = {
|
|
|
175
142
|
return (
|
|
176
143
|
<Modal
|
|
177
144
|
aria-label="Draggable Modal"
|
|
178
|
-
size="medium"
|
|
179
145
|
draggable={true}
|
|
180
146
|
showOverlay={false}
|
|
181
147
|
modalTrigger={
|
|
182
148
|
<Button appearance="primary">Open Draggable Modal</Button>
|
|
183
149
|
}
|
|
184
150
|
>
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
151
|
+
<ModalHeader
|
|
152
|
+
title="Draggable Modal"
|
|
153
|
+
subtitle="Click and drag anywhere on this modal to move it around"
|
|
154
|
+
/>
|
|
155
|
+
<ModalBody>
|
|
190
156
|
<Box
|
|
191
157
|
border="1px dashed"
|
|
192
|
-
borderColor="
|
|
158
|
+
borderColor="container.border.info"
|
|
193
159
|
p={500}
|
|
194
|
-
bg="
|
|
160
|
+
bg="container.background.info"
|
|
195
161
|
borderRadius="6px"
|
|
196
162
|
>
|
|
197
163
|
<Text>
|
|
@@ -201,14 +167,11 @@ export const DraggableModal: Story = {
|
|
|
201
167
|
different corners of the screen.
|
|
202
168
|
</Text>
|
|
203
169
|
</Box>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
</Box>
|
|
210
|
-
</ModalFooter>
|
|
211
|
-
</ModalContent>
|
|
170
|
+
</ModalBody>
|
|
171
|
+
<ModalFooter
|
|
172
|
+
cancelButton={<Button>Cancel</Button>}
|
|
173
|
+
primaryButton={<Button appearance="primary">Got it!</Button>}
|
|
174
|
+
/>
|
|
212
175
|
</Modal>
|
|
213
176
|
);
|
|
214
177
|
},
|
|
@@ -235,41 +198,46 @@ export const ControlledState: Story = {
|
|
|
235
198
|
if (open) setCount((c) => c + 1);
|
|
236
199
|
}}
|
|
237
200
|
aria-label="Controlled Modal"
|
|
238
|
-
size="medium"
|
|
239
201
|
modalTrigger={
|
|
240
202
|
<Button appearance="primary">Open Controlled Modal</Button>
|
|
241
203
|
}
|
|
242
204
|
>
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
205
|
+
<ModalHeader
|
|
206
|
+
title="Controlled Modal"
|
|
207
|
+
subtitle="This modal's state is managed externally"
|
|
208
|
+
/>
|
|
209
|
+
<ModalBody>
|
|
248
210
|
<Box
|
|
249
211
|
border="1px dashed"
|
|
250
|
-
borderColor="
|
|
212
|
+
borderColor="container.border.base"
|
|
251
213
|
p={500}
|
|
252
|
-
bg="
|
|
214
|
+
bg="container.background.decorative.neutral"
|
|
253
215
|
borderRadius="6px"
|
|
254
216
|
>
|
|
255
217
|
<Text>
|
|
256
218
|
This modal's state is controlled by the parent component. You
|
|
257
|
-
can track when it opens/closes and perform side effects.
|
|
258
|
-
|
|
259
|
-
|
|
219
|
+
can track when it opens/closes and perform side effects. This
|
|
220
|
+
example uses ModalCustomFooter for full control over the footer
|
|
221
|
+
layout and behavior.
|
|
260
222
|
</Text>
|
|
261
223
|
</Box>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
</
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
224
|
+
</ModalBody>
|
|
225
|
+
<ModalCustomFooter>
|
|
226
|
+
<Box display="flex" justifyContent="flex-end" gap={300}>
|
|
227
|
+
<ModalCloseWrapper>
|
|
228
|
+
<Button>Cancel</Button>
|
|
229
|
+
</ModalCloseWrapper>
|
|
230
|
+
<Button
|
|
231
|
+
appearance="primary"
|
|
232
|
+
onClick={() => {
|
|
233
|
+
console.log("Confirmed!");
|
|
234
|
+
setIsOpen(false);
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
Confirm
|
|
238
|
+
</Button>
|
|
239
|
+
</Box>
|
|
240
|
+
</ModalCustomFooter>
|
|
273
241
|
</Modal>
|
|
274
242
|
|
|
275
243
|
<Box mt={300}>
|
|
@@ -319,10 +287,10 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
319
287
|
|
|
320
288
|
<Box
|
|
321
289
|
p={500}
|
|
322
|
-
bg="neutral
|
|
290
|
+
bg="container.background.decorative.neutral"
|
|
323
291
|
borderRadius="8px"
|
|
324
292
|
border="2px solid"
|
|
325
|
-
borderColor="
|
|
293
|
+
borderColor="container.border.base"
|
|
326
294
|
>
|
|
327
295
|
<Text fontSize={400} fontWeight="bold" mb={300}>
|
|
328
296
|
Contact Form
|
|
@@ -442,18 +410,17 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
442
410
|
|
|
443
411
|
<Modal
|
|
444
412
|
aria-label="Reference Modal"
|
|
445
|
-
size="medium"
|
|
446
413
|
draggable={true}
|
|
447
|
-
showOverlay={
|
|
414
|
+
showOverlay={false}
|
|
448
415
|
modalTrigger={
|
|
449
416
|
<Button appearance="secondary">Open Reference Modal</Button>
|
|
450
417
|
}
|
|
451
418
|
>
|
|
452
|
-
<
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
419
|
+
<ModalHeader
|
|
420
|
+
title="Reference Information"
|
|
421
|
+
subtitle="Drag this modal by the header to move it around"
|
|
422
|
+
/>
|
|
423
|
+
<ModalBody>
|
|
457
424
|
<Box p={300}>
|
|
458
425
|
<Text fontSize={400} fontWeight="bold" mb={200}>
|
|
459
426
|
Instructions for filling out the form:
|
|
@@ -484,10 +451,10 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
484
451
|
<Box
|
|
485
452
|
mt={400}
|
|
486
453
|
p={300}
|
|
487
|
-
bg="
|
|
454
|
+
bg="container.background.info"
|
|
488
455
|
borderRadius="6px"
|
|
489
456
|
border="1px solid"
|
|
490
|
-
borderColor="
|
|
457
|
+
borderColor="container.border.info"
|
|
491
458
|
>
|
|
492
459
|
<Text fontWeight="bold" mb={100}>
|
|
493
460
|
Try this:
|
|
@@ -505,14 +472,13 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
505
472
|
</Text>
|
|
506
473
|
</Box>
|
|
507
474
|
</Box>
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
</ModalContent>
|
|
475
|
+
</ModalBody>
|
|
476
|
+
<ModalFooter
|
|
477
|
+
cancelButton={<Button>Cancel</Button>}
|
|
478
|
+
primaryButton={
|
|
479
|
+
<Button appearance="primary">Got it!</Button>
|
|
480
|
+
}
|
|
481
|
+
/>
|
|
516
482
|
</Modal>
|
|
517
483
|
</Box>
|
|
518
484
|
</Box>
|
|
@@ -522,10 +488,10 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
522
488
|
<Box
|
|
523
489
|
mt={400}
|
|
524
490
|
p={400}
|
|
525
|
-
bg="
|
|
491
|
+
bg="container.background.success"
|
|
526
492
|
borderRadius="6px"
|
|
527
493
|
border="2px solid"
|
|
528
|
-
borderColor="
|
|
494
|
+
borderColor="container.border.success"
|
|
529
495
|
>
|
|
530
496
|
<Text fontSize={400} fontWeight="bold" mb={200}>
|
|
531
497
|
Form Submitted!
|
|
@@ -551,3 +517,322 @@ export const DraggableWithFormInteraction: Story = {
|
|
|
551
517
|
);
|
|
552
518
|
},
|
|
553
519
|
};
|
|
520
|
+
|
|
521
|
+
export const TallContentMobile: Story = {
|
|
522
|
+
render: () => {
|
|
523
|
+
return (
|
|
524
|
+
<Modal
|
|
525
|
+
aria-label="Tall Content Modal"
|
|
526
|
+
modalTrigger={
|
|
527
|
+
<Button appearance="primary">Open Tall Modal (Test Mobile)</Button>
|
|
528
|
+
}
|
|
529
|
+
>
|
|
530
|
+
<ModalHeader
|
|
531
|
+
title="Very Tall Modal"
|
|
532
|
+
subtitle="Resize your browser to ≤780px to see mobile bottom sheet behavior"
|
|
533
|
+
/>
|
|
534
|
+
<ModalBody>
|
|
535
|
+
<Box p={300}>
|
|
536
|
+
<Text fontSize={400} fontWeight="bold" mb={300}>
|
|
537
|
+
Testing Mobile Responsiveness
|
|
538
|
+
</Text>
|
|
539
|
+
<Text mb={300}>
|
|
540
|
+
This modal has a lot of content to test how it behaves on mobile
|
|
541
|
+
screens. The modal should:
|
|
542
|
+
</Text>
|
|
543
|
+
<Box as="ul" pl={400} mb={400}>
|
|
544
|
+
<li>
|
|
545
|
+
<Text mb={200}>
|
|
546
|
+
✅ Anchor to the bottom of the screen on mobile (≤780px)
|
|
547
|
+
</Text>
|
|
548
|
+
</li>
|
|
549
|
+
<li>
|
|
550
|
+
<Text mb={200}>
|
|
551
|
+
✅ Have rounded top corners and flat bottom corners
|
|
552
|
+
</Text>
|
|
553
|
+
</li>
|
|
554
|
+
<li>
|
|
555
|
+
<Text mb={200}>✅ Show the action rail above the modal</Text>
|
|
556
|
+
</li>
|
|
557
|
+
<li>
|
|
558
|
+
<Text mb={200}>✅ Hug the content height</Text>
|
|
559
|
+
</li>
|
|
560
|
+
<li>
|
|
561
|
+
<Text mb={200}>
|
|
562
|
+
✅ Scroll the content area when content exceeds max-height
|
|
563
|
+
</Text>
|
|
564
|
+
</li>
|
|
565
|
+
</Box>
|
|
566
|
+
|
|
567
|
+
<Box
|
|
568
|
+
bg="container.background.decorative.purple"
|
|
569
|
+
border="1px solid"
|
|
570
|
+
borderColor="container.border.decorative.purple"
|
|
571
|
+
borderRadius="8px"
|
|
572
|
+
p={400}
|
|
573
|
+
mb={400}
|
|
574
|
+
>
|
|
575
|
+
<Text fontWeight="bold" mb={200}>
|
|
576
|
+
Section 1: Lorem Ipsum
|
|
577
|
+
</Text>
|
|
578
|
+
<Text mb={200}>
|
|
579
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
|
580
|
+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
|
|
581
|
+
enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
|
582
|
+
nisi ut aliquip ex ea commodo consequat.
|
|
583
|
+
</Text>
|
|
584
|
+
<Text>
|
|
585
|
+
Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
586
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
|
587
|
+
cupidatat non proident, sunt in culpa qui officia deserunt
|
|
588
|
+
mollit anim id est laborum.
|
|
589
|
+
</Text>
|
|
590
|
+
</Box>
|
|
591
|
+
|
|
592
|
+
<Box
|
|
593
|
+
bg="container.background.decorative.blue"
|
|
594
|
+
border="1px solid"
|
|
595
|
+
borderColor="container.border.decorative.blue"
|
|
596
|
+
borderRadius="8px"
|
|
597
|
+
p={400}
|
|
598
|
+
mb={400}
|
|
599
|
+
>
|
|
600
|
+
<Text fontWeight="bold" mb={200}>
|
|
601
|
+
Section 2: More Content
|
|
602
|
+
</Text>
|
|
603
|
+
<Text mb={200}>
|
|
604
|
+
Pellentesque habitant morbi tristique senectus et netus et
|
|
605
|
+
malesuada fames ac turpis egestas. Vestibulum tortor quam,
|
|
606
|
+
feugiat vitae, ultricies eget, tempor sit amet, ante.
|
|
607
|
+
</Text>
|
|
608
|
+
<Text>
|
|
609
|
+
Donec eu libero sit amet quam egestas semper. Aenean ultricies
|
|
610
|
+
mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est
|
|
611
|
+
et sapien ullamcorper pharetra.
|
|
612
|
+
</Text>
|
|
613
|
+
</Box>
|
|
614
|
+
|
|
615
|
+
<Box
|
|
616
|
+
bg="container.background.decorative.green"
|
|
617
|
+
border="1px solid"
|
|
618
|
+
borderColor="container.border.decorative.green"
|
|
619
|
+
borderRadius="8px"
|
|
620
|
+
p={400}
|
|
621
|
+
mb={400}
|
|
622
|
+
>
|
|
623
|
+
<Text fontWeight="bold" mb={200}>
|
|
624
|
+
Section 3: Even More Content
|
|
625
|
+
</Text>
|
|
626
|
+
<Text mb={200}>
|
|
627
|
+
Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit
|
|
628
|
+
amet, wisi. Aenean fermentum, elit eget tincidunt condimentum,
|
|
629
|
+
eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.
|
|
630
|
+
</Text>
|
|
631
|
+
<Text>
|
|
632
|
+
Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent
|
|
633
|
+
dapibus, neque id cursus faucibus, tortor neque egestas augue,
|
|
634
|
+
eu vulputate magna eros eu erat.
|
|
635
|
+
</Text>
|
|
636
|
+
</Box>
|
|
637
|
+
|
|
638
|
+
<Box
|
|
639
|
+
bg="container.background.decorative.orange"
|
|
640
|
+
border="1px solid"
|
|
641
|
+
borderColor="container.border.decorative.orange"
|
|
642
|
+
borderRadius="8px"
|
|
643
|
+
p={400}
|
|
644
|
+
mb={400}
|
|
645
|
+
>
|
|
646
|
+
<Text fontWeight="bold" mb={200}>
|
|
647
|
+
Section 4: Final Section
|
|
648
|
+
</Text>
|
|
649
|
+
<Text mb={200}>
|
|
650
|
+
Aliquam erat volutpat. Nunc eleifend leo vitae magna. In id erat
|
|
651
|
+
non orci commodo lobortis. Proin neque massa, cursus ut, gravida
|
|
652
|
+
ut, lobortis eget, lacus.
|
|
653
|
+
</Text>
|
|
654
|
+
<Text>
|
|
655
|
+
Sed diam. Praesent fermentum tempor tellus. Nullam tempus.
|
|
656
|
+
Mauris ac felis vel velit tristique imperdiet. Donec at pede.
|
|
657
|
+
Etiam vel neque nec dui dignissim bibendum.
|
|
658
|
+
</Text>
|
|
659
|
+
</Box>
|
|
660
|
+
|
|
661
|
+
<Box
|
|
662
|
+
bg="container.background.decorative.red"
|
|
663
|
+
border="1px solid"
|
|
664
|
+
borderColor="container.border.decorative.red"
|
|
665
|
+
borderRadius="8px"
|
|
666
|
+
p={400}
|
|
667
|
+
>
|
|
668
|
+
<Text fontWeight="bold" mb={200}>
|
|
669
|
+
🎯 How to Test
|
|
670
|
+
</Text>
|
|
671
|
+
<Text fontSize={200} mb={200}>
|
|
672
|
+
1. Open your browser's DevTools (F12 or Cmd+Option+I on Mac)
|
|
673
|
+
</Text>
|
|
674
|
+
<Text fontSize={200} mb={200}>
|
|
675
|
+
2. Toggle device toolbar (mobile view icon)
|
|
676
|
+
</Text>
|
|
677
|
+
<Text fontSize={200} mb={200}>
|
|
678
|
+
3. Set width to 780px or less (tablet/mobile size)
|
|
679
|
+
</Text>
|
|
680
|
+
<Text fontSize={200}>
|
|
681
|
+
4. Observe the bottom sheet behavior with flat bottom corners
|
|
682
|
+
</Text>
|
|
683
|
+
</Box>
|
|
684
|
+
</Box>
|
|
685
|
+
</ModalBody>
|
|
686
|
+
<ModalFooter
|
|
687
|
+
cancelButton={<Button>Cancel</Button>}
|
|
688
|
+
primaryButton={<Button appearance="primary">Confirm</Button>}
|
|
689
|
+
/>
|
|
690
|
+
</Modal>
|
|
691
|
+
);
|
|
692
|
+
},
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
export const SimplifiedFooterAPI: Story = {
|
|
696
|
+
render: () => {
|
|
697
|
+
return (
|
|
698
|
+
<Modal
|
|
699
|
+
aria-label="Simplified Footer API Example"
|
|
700
|
+
modalTrigger={
|
|
701
|
+
<Button appearance="primary">Open Modal with Simple Footer</Button>
|
|
702
|
+
}
|
|
703
|
+
>
|
|
704
|
+
<ModalHeader
|
|
705
|
+
title="Simplified Footer API"
|
|
706
|
+
subtitle="No need to wrap buttons or manage layout"
|
|
707
|
+
/>
|
|
708
|
+
<ModalBody>
|
|
709
|
+
<Text mb={300}>
|
|
710
|
+
The new ModalFooter API makes it easy to add standard footer buttons
|
|
711
|
+
without boilerplate code.
|
|
712
|
+
</Text>
|
|
713
|
+
<Text>
|
|
714
|
+
Simply pass your Button elements to cancelButton and primaryButton
|
|
715
|
+
props. Both buttons automatically close the modal. You can add
|
|
716
|
+
custom onClick handlers that run before closing (e.g., for
|
|
717
|
+
validation or saving data).
|
|
718
|
+
</Text>
|
|
719
|
+
</ModalBody>
|
|
720
|
+
<ModalFooter
|
|
721
|
+
cancelButton={<Button>Cancel</Button>}
|
|
722
|
+
primaryButton={
|
|
723
|
+
<Button
|
|
724
|
+
appearance="primary"
|
|
725
|
+
onClick={() => {
|
|
726
|
+
console.log("Saving data...");
|
|
727
|
+
// Your custom logic here runs before the modal closes
|
|
728
|
+
}}
|
|
729
|
+
>
|
|
730
|
+
Save
|
|
731
|
+
</Button>
|
|
732
|
+
}
|
|
733
|
+
/>
|
|
734
|
+
</Modal>
|
|
735
|
+
);
|
|
736
|
+
},
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
export const FooterWithLeftAction: Story = {
|
|
740
|
+
render: () => {
|
|
741
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
742
|
+
|
|
743
|
+
return (
|
|
744
|
+
<Modal
|
|
745
|
+
open={isOpen}
|
|
746
|
+
onOpenChange={setIsOpen}
|
|
747
|
+
aria-label="Footer with Left Action"
|
|
748
|
+
modalTrigger={
|
|
749
|
+
<Button appearance="primary">Open Modal with Delete Action</Button>
|
|
750
|
+
}
|
|
751
|
+
>
|
|
752
|
+
<ModalHeader
|
|
753
|
+
title="Edit Document"
|
|
754
|
+
subtitle="Make changes or delete this document"
|
|
755
|
+
/>
|
|
756
|
+
<ModalBody>
|
|
757
|
+
<Text mb={300}>
|
|
758
|
+
This modal showcases the leftAction prop, perfect for destructive
|
|
759
|
+
actions like Delete that should be separated from the primary
|
|
760
|
+
actions.
|
|
761
|
+
</Text>
|
|
762
|
+
<Text>
|
|
763
|
+
The Delete button appears on the far left, while Cancel and Save are
|
|
764
|
+
grouped together on the right. Note that leftAction is NOT
|
|
765
|
+
auto-wrapped, so you control when/how to close the modal.
|
|
766
|
+
</Text>
|
|
767
|
+
</ModalBody>
|
|
768
|
+
<ModalFooter
|
|
769
|
+
leftAction={
|
|
770
|
+
<Button
|
|
771
|
+
appearance="destructive"
|
|
772
|
+
onClick={() => {
|
|
773
|
+
if (confirm("Are you sure you want to delete?")) {
|
|
774
|
+
console.log("Deleting...");
|
|
775
|
+
setIsOpen(false);
|
|
776
|
+
}
|
|
777
|
+
}}
|
|
778
|
+
>
|
|
779
|
+
Delete
|
|
780
|
+
</Button>
|
|
781
|
+
}
|
|
782
|
+
cancelButton={<Button>Cancel</Button>}
|
|
783
|
+
primaryButton={
|
|
784
|
+
<Button
|
|
785
|
+
appearance="primary"
|
|
786
|
+
onClick={() => {
|
|
787
|
+
console.log("Saving...");
|
|
788
|
+
// Modal auto-closes after this runs
|
|
789
|
+
}}
|
|
790
|
+
>
|
|
791
|
+
Save Changes
|
|
792
|
+
</Button>
|
|
793
|
+
}
|
|
794
|
+
/>
|
|
795
|
+
</Modal>
|
|
796
|
+
);
|
|
797
|
+
},
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
export const CustomFooterOverride: Story = {
|
|
801
|
+
render: () => {
|
|
802
|
+
return (
|
|
803
|
+
<Modal
|
|
804
|
+
aria-label="Custom Footer Override"
|
|
805
|
+
modalTrigger={<Button appearance="primary">Open Custom Footer</Button>}
|
|
806
|
+
>
|
|
807
|
+
<ModalHeader
|
|
808
|
+
title="Custom Footer Layout"
|
|
809
|
+
subtitle="Use ModalCustomFooter for complete control"
|
|
810
|
+
/>
|
|
811
|
+
<ModalBody>
|
|
812
|
+
<Text>
|
|
813
|
+
For edge cases where you need complete control over the footer
|
|
814
|
+
layout, you can use ModalCustomFooter instead of ModalFooter. This
|
|
815
|
+
gives you the base styled footer component with full control over
|
|
816
|
+
the content.
|
|
817
|
+
</Text>
|
|
818
|
+
</ModalBody>
|
|
819
|
+
<ModalCustomFooter>
|
|
820
|
+
<Box display="flex" justifyContent="space-between" width="100%">
|
|
821
|
+
<Box display="flex" gap={200}>
|
|
822
|
+
<Button appearance="secondary">Help</Button>
|
|
823
|
+
<Button appearance="secondary">More Info</Button>
|
|
824
|
+
</Box>
|
|
825
|
+
<Box display="flex" gap={300}>
|
|
826
|
+
<ModalCloseWrapper>
|
|
827
|
+
<Button>Cancel</Button>
|
|
828
|
+
</ModalCloseWrapper>
|
|
829
|
+
<ModalCloseWrapper>
|
|
830
|
+
<Button appearance="primary">Confirm</Button>
|
|
831
|
+
</ModalCloseWrapper>
|
|
832
|
+
</Box>
|
|
833
|
+
</Box>
|
|
834
|
+
</ModalCustomFooter>
|
|
835
|
+
</Modal>
|
|
836
|
+
);
|
|
837
|
+
},
|
|
838
|
+
};
|