@react-ui-org/react-ui 0.50.2 → 0.52.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/lib.development.js +157 -49
- package/dist/lib.js +1 -1
- package/package.json +1 -1
- package/src/lib/components/Alert/Alert.jsx +1 -3
- package/src/lib/components/Alert/Alert.scss +1 -9
- package/src/lib/components/Alert/README.mdx +0 -20
- package/src/lib/components/Alert/_settings.scss +1 -1
- package/src/lib/components/Alert/_theme.scss +0 -10
- package/src/lib/components/Badge/Badge.jsx +1 -3
- package/src/lib/components/Badge/Badge.scss +25 -44
- package/src/lib/components/Badge/README.mdx +6 -14
- package/src/lib/components/Button/Button.jsx +20 -10
- package/src/lib/components/Button/README.mdx +8 -3
- package/src/lib/components/Button/_base.scss +21 -12
- package/src/lib/components/Button/_priorities.scss +13 -18
- package/src/lib/components/Button/_settings.scss +1 -1
- package/src/lib/components/Button/_theme.scss +0 -10
- package/src/lib/components/ButtonGroup/ButtonGroup.jsx +5 -3
- package/src/lib/components/ButtonGroup/ButtonGroup.scss +26 -1
- package/src/lib/components/ButtonGroup/README.mdx +85 -59
- package/src/lib/components/ButtonGroup/_theme.scss +13 -0
- package/src/lib/components/Card/Card.jsx +1 -3
- package/src/lib/components/Card/Card.scss +0 -9
- package/src/lib/components/Card/README.mdx +0 -16
- package/src/lib/components/Card/_theme.scss +0 -10
- package/src/lib/components/FormLayout/README.mdx +22 -8
- package/src/lib/components/Grid/_helpers/generateResponsiveCustomProperties.js +1 -1
- package/src/lib/components/InputGroup/InputGroup.jsx +170 -0
- package/src/lib/components/InputGroup/InputGroup.scss +92 -0
- package/src/lib/components/InputGroup/InputGroupContext.js +3 -0
- package/src/lib/components/InputGroup/README.mdx +278 -0
- package/src/lib/components/InputGroup/_theme.scss +2 -0
- package/src/lib/components/InputGroup/index.js +2 -0
- package/src/lib/components/Modal/Modal.jsx +58 -97
- package/src/lib/components/Modal/ModalCloseButton.scss +2 -2
- package/src/lib/components/Modal/README.mdx +392 -128
- package/src/lib/components/Modal/_helpers/getPositionClassName.js +7 -0
- package/src/lib/components/Modal/_helpers/getSizeClassName.js +19 -0
- package/src/lib/components/Modal/_hooks/useModalFocus.js +126 -0
- package/src/lib/components/Modal/_hooks/useModalScrollPrevention.js +35 -0
- package/src/lib/components/Modal/_settings.scss +2 -2
- package/src/lib/components/Popover/README.mdx +7 -4
- package/src/lib/components/Radio/README.mdx +9 -1
- package/src/lib/components/Radio/Radio.jsx +39 -31
- package/src/lib/components/Radio/Radio.scss +11 -1
- package/src/lib/components/ScrollView/README.mdx +2 -2
- package/src/lib/components/SelectField/SelectField.jsx +21 -8
- package/src/lib/components/SelectField/SelectField.scss +5 -0
- package/src/lib/components/Table/_components/TableCell.scss +6 -5
- package/src/lib/components/Table/_components/TableHeaderCell/TableHeaderCell.jsx +8 -5
- package/src/lib/components/Table/_settings.scss +5 -6
- package/src/lib/components/Text/README.mdx +14 -8
- package/src/lib/components/TextField/TextField.jsx +21 -8
- package/src/lib/components/TextField/TextField.scss +5 -0
- package/src/lib/components/TextLink/README.mdx +8 -6
- package/src/lib/components/TextLink/TextLink.scss +5 -0
- package/src/lib/components/TextLink/_theme.scss +2 -0
- package/src/lib/components/Toolbar/README.mdx +19 -11
- package/src/lib/components/_helpers/getRootColorClassName.js +4 -0
- package/src/lib/index.js +1 -0
- package/src/lib/styles/elements/_code.scss +1 -3
- package/src/lib/styles/elements/_page.scss +1 -0
- package/src/lib/styles/elements/_rulers.scss +1 -3
- package/src/lib/styles/elements/_small.scss +1 -1
- package/src/lib/styles/settings/_form-fields.scss +1 -1
- package/src/lib/styles/settings/_utilities.scss +46 -14
- package/src/lib/styles/theme/_accessibility.scss +4 -4
- package/src/lib/styles/theme/_borders.scss +3 -2
- package/src/lib/styles/theme/_code.scss +2 -2
- package/src/lib/styles/theme/_links.scss +6 -4
- package/src/lib/styles/theme/_lists.scss +1 -1
- package/src/lib/styles/theme/_page.scss +2 -2
- package/src/lib/styles/theme/_spacing.scss +11 -11
- package/src/lib/styles/theme/_typography.scss +19 -18
- package/src/lib/styles/theme-constants/_colors.scss +23 -23
- package/src/lib/styles/tools/_spacing.scss +1 -1
- package/src/lib/styles/tools/form-fields/_box-field-elements.scss +19 -2
- package/src/lib/styles/tools/form-fields/_box-field-sizes.scss +11 -8
- package/src/lib/styles/tools/form-fields/_foundation.scss +7 -0
- package/src/lib/theme.scss +650 -567
- package/src/lib/styles/theme/_colors.scss +0 -65
- /package/src/lib/components/{Button/helpers → _helpers}/getRootPriorityClassName.js +0 -0
@@ -14,6 +14,7 @@ import {
|
|
14
14
|
} from 'docz'
|
15
15
|
import {
|
16
16
|
Button,
|
17
|
+
CheckboxField,
|
17
18
|
FormLayout,
|
18
19
|
Modal,
|
19
20
|
ModalBody,
|
@@ -22,7 +23,9 @@ import {
|
|
22
23
|
ModalFooter,
|
23
24
|
ModalHeader,
|
24
25
|
ModalTitle,
|
26
|
+
Radio,
|
25
27
|
ScrollView,
|
28
|
+
TextArea,
|
26
29
|
TextField,
|
27
30
|
Toolbar,
|
28
31
|
ToolbarGroup,
|
@@ -55,11 +58,7 @@ And use it:
|
|
55
58
|
const modalCloseButtonRef = React.useRef();
|
56
59
|
return (
|
57
60
|
<>
|
58
|
-
<Button
|
59
|
-
label="Launch modal"
|
60
|
-
onClick={() => setModalOpen(true)}
|
61
|
-
priority="outline"
|
62
|
-
/>
|
61
|
+
<Button label="Launch modal" onClick={() => setModalOpen(true)} />
|
63
62
|
<div>
|
64
63
|
{modalOpen && (
|
65
64
|
<Modal
|
@@ -86,6 +85,7 @@ And use it:
|
|
86
85
|
ref={modalPrimaryButtonRef}
|
87
86
|
/>
|
88
87
|
<Button
|
88
|
+
color="secondary"
|
89
89
|
label="Close"
|
90
90
|
onClick={() => setModalOpen(false)}
|
91
91
|
priority="outline"
|
@@ -117,12 +117,15 @@ See [API](#api) for all available options.
|
|
117
117
|
|
118
118
|
- Modal **automatically focuses the first non-disabled form field** by default
|
119
119
|
which allows users to confirm the modal by hitting the enter key. When no
|
120
|
-
field is found then the primary button (in the footer) is focused.
|
121
|
-
|
120
|
+
field is found then the primary button (in the footer) is focused. If there
|
121
|
+
are neither, it tries to focus any other focusable elements. In case there
|
122
|
+
are none, or [autoFocus](#autoFocus) is disabled, Modal itself is focused.
|
122
123
|
|
123
124
|
- **Avoid stacking** of modals. While it may technically work, the modal is just
|
124
125
|
not designed for that.
|
125
126
|
|
127
|
+
📖 [Read more about modals at Nielsen Norman Group.][nng-modal]
|
128
|
+
|
126
129
|
## Composition
|
127
130
|
|
128
131
|
Modal is decomposed into the following components:
|
@@ -152,7 +155,6 @@ e.g. dialog modal, blocking modal, scrollable modal, etc.
|
|
152
155
|
setModalOpen(1);
|
153
156
|
setTimeout(() => setModalOpen(null), 2500);
|
154
157
|
}}
|
155
|
-
priority="outline"
|
156
158
|
/>
|
157
159
|
<Button
|
158
160
|
label="Launch blocking modal with title"
|
@@ -160,17 +162,14 @@ e.g. dialog modal, blocking modal, scrollable modal, etc.
|
|
160
162
|
setModalOpen(2);
|
161
163
|
setTimeout(() => setModalOpen(null), 3500);
|
162
164
|
}}
|
163
|
-
priority="outline"
|
164
165
|
/>
|
165
166
|
<Button
|
166
167
|
label="Launch modal as dialog"
|
167
168
|
onClick={() => setModalOpen(3)}
|
168
|
-
priority="outline"
|
169
169
|
/>
|
170
170
|
<Button
|
171
171
|
label="Launch modal as form"
|
172
172
|
onClick={() => setModalOpen(4)}
|
173
|
-
priority="outline"
|
174
173
|
/>
|
175
174
|
<div>
|
176
175
|
{modalOpen === 1 && (
|
@@ -227,6 +226,7 @@ e.g. dialog modal, blocking modal, scrollable modal, etc.
|
|
227
226
|
ref={modalPrimaryButtonRef}
|
228
227
|
/>
|
229
228
|
<Button
|
229
|
+
color="secondary"
|
230
230
|
label="Close"
|
231
231
|
onClick={() => setModalOpen(false)}
|
232
232
|
priority="outline"
|
@@ -259,6 +259,7 @@ e.g. dialog modal, blocking modal, scrollable modal, etc.
|
|
259
259
|
ref={modalPrimaryButtonRef}
|
260
260
|
/>
|
261
261
|
<Button
|
262
|
+
color="secondary"
|
262
263
|
label="Close"
|
263
264
|
onClick={() => setModalOpen(false)}
|
264
265
|
priority="outline"
|
@@ -305,7 +306,6 @@ There are two ways how to position elements within the ModalHeader:
|
|
305
306
|
setModalOpen(true);
|
306
307
|
setVariant(1);
|
307
308
|
}}
|
308
|
-
priority="outline"
|
309
309
|
/>
|
310
310
|
<Button
|
311
311
|
label="Launch without close button"
|
@@ -313,7 +313,6 @@ There are two ways how to position elements within the ModalHeader:
|
|
313
313
|
setModalOpen(true);
|
314
314
|
setVariant(2);
|
315
315
|
}}
|
316
|
-
priority="outline"
|
317
316
|
/>
|
318
317
|
<Button
|
319
318
|
label="Launch without close button and with centered title"
|
@@ -321,7 +320,6 @@ There are two ways how to position elements within the ModalHeader:
|
|
321
320
|
setModalOpen(true);
|
322
321
|
setVariant(3);
|
323
322
|
}}
|
324
|
-
priority="outline"
|
325
323
|
/>
|
326
324
|
<Button
|
327
325
|
label="Launch with custom layout"
|
@@ -329,7 +327,6 @@ There are two ways how to position elements within the ModalHeader:
|
|
329
327
|
setModalOpen(true);
|
330
328
|
setVariant(4);
|
331
329
|
}}
|
332
|
-
priority="outline"
|
333
330
|
/>
|
334
331
|
<div>
|
335
332
|
{modalOpen && (
|
@@ -384,6 +381,7 @@ There are two ways how to position elements within the ModalHeader:
|
|
384
381
|
ref={modalPrimaryButtonRef}
|
385
382
|
/>
|
386
383
|
<Button
|
384
|
+
color="secondary"
|
387
385
|
label="Close"
|
388
386
|
onClick={() => setModalOpen(false)}
|
389
387
|
priority="outline"
|
@@ -432,44 +430,8 @@ There are two ways to position buttons within the ModalFooter:
|
|
432
430
|
return (
|
433
431
|
<>
|
434
432
|
<Button
|
435
|
-
label="Launch with footer
|
436
|
-
onClick={() =>
|
437
|
-
setModalOpen(true);
|
438
|
-
setModalJustify('start');
|
439
|
-
}}
|
440
|
-
priority="outline"
|
441
|
-
/>
|
442
|
-
<Button
|
443
|
-
label="Launch with footer justified to center"
|
444
|
-
onClick={() => {
|
445
|
-
setModalOpen(true);
|
446
|
-
setModalJustify('center');
|
447
|
-
}}
|
448
|
-
priority="outline"
|
449
|
-
/>
|
450
|
-
<Button
|
451
|
-
label="Launch with footer justified to end"
|
452
|
-
onClick={() => {
|
453
|
-
setModalOpen(true);
|
454
|
-
setModalJustify('end');
|
455
|
-
}}
|
456
|
-
priority="outline"
|
457
|
-
/>
|
458
|
-
<Button
|
459
|
-
label="Launch with footer justified with space between"
|
460
|
-
onClick={() => {
|
461
|
-
setModalOpen(true);
|
462
|
-
setModalJustify('space-between');
|
463
|
-
}}
|
464
|
-
priority="outline"
|
465
|
-
/>
|
466
|
-
<Button
|
467
|
-
label="Launch with footer with custom layout"
|
468
|
-
onClick={() => {
|
469
|
-
setModalOpen(true);
|
470
|
-
setModalJustify('stretch');
|
471
|
-
}}
|
472
|
-
priority="outline"
|
433
|
+
label="Launch modal with footer variants"
|
434
|
+
onClick={() => setModalOpen(true)}
|
473
435
|
/>
|
474
436
|
<div>
|
475
437
|
{modalOpen && (
|
@@ -478,15 +440,38 @@ There are two ways to position buttons within the ModalFooter:
|
|
478
440
|
primaryButtonRef={modalPrimaryButtonRef}
|
479
441
|
>
|
480
442
|
<ModalHeader>
|
481
|
-
<ModalTitle>
|
443
|
+
<ModalTitle>Footer justification</ModalTitle>
|
482
444
|
<ModalCloseButton onClick={() => setModalOpen(false)} />
|
483
445
|
</ModalHeader>
|
484
446
|
<ModalBody>
|
485
447
|
<ModalContent>
|
486
|
-
<
|
487
|
-
|
488
|
-
|
489
|
-
|
448
|
+
<Radio
|
449
|
+
label="Footer justification"
|
450
|
+
onChange={(e) => setModalJustify(e.target.value)}
|
451
|
+
options={[
|
452
|
+
{
|
453
|
+
label: 'start',
|
454
|
+
value: 'start',
|
455
|
+
},
|
456
|
+
{
|
457
|
+
label: 'center',
|
458
|
+
value: 'center',
|
459
|
+
},
|
460
|
+
{
|
461
|
+
label: 'end',
|
462
|
+
value: 'end',
|
463
|
+
},
|
464
|
+
{
|
465
|
+
label: 'space-between',
|
466
|
+
value: 'space-between',
|
467
|
+
},
|
468
|
+
{
|
469
|
+
label: 'stretch (with a custom layout)',
|
470
|
+
value: 'stretch',
|
471
|
+
},
|
472
|
+
]}
|
473
|
+
value={modalJustify}
|
474
|
+
/>
|
490
475
|
</ModalContent>
|
491
476
|
</ModalBody>
|
492
477
|
<ModalFooter justify={modalJustify}>
|
@@ -514,6 +499,7 @@ There are two ways to position buttons within the ModalFooter:
|
|
514
499
|
</ToolbarGroup>
|
515
500
|
<ToolbarItem>
|
516
501
|
<Button
|
502
|
+
color="secondary"
|
517
503
|
label="Close"
|
518
504
|
onClick={() => setModalOpen(false)}
|
519
505
|
priority="outline"
|
@@ -524,12 +510,12 @@ There are two ways to position buttons within the ModalFooter:
|
|
524
510
|
) : (
|
525
511
|
<>
|
526
512
|
<Button
|
527
|
-
|
528
|
-
label="Delete"
|
513
|
+
label="OK"
|
529
514
|
onClick={() => setModalOpen(false)}
|
530
515
|
ref={modalPrimaryButtonRef}
|
531
516
|
/>
|
532
517
|
<Button
|
518
|
+
color="secondary"
|
533
519
|
label="Close"
|
534
520
|
onClick={() => setModalOpen(false)}
|
535
521
|
priority="outline"
|
@@ -566,7 +552,6 @@ Modals of any size automatically shrink when they cannot fit the screen width.
|
|
566
552
|
setModalSize('small');
|
567
553
|
setModalOpen(true);
|
568
554
|
}}
|
569
|
-
priority="outline"
|
570
555
|
/>
|
571
556
|
<Button
|
572
557
|
label="Launch medium modal"
|
@@ -574,7 +559,6 @@ Modals of any size automatically shrink when they cannot fit the screen width.
|
|
574
559
|
setModalSize('medium');
|
575
560
|
setModalOpen(true);
|
576
561
|
}}
|
577
|
-
priority="outline"
|
578
562
|
/>
|
579
563
|
<Button
|
580
564
|
label="Launch large modal"
|
@@ -582,7 +566,6 @@ Modals of any size automatically shrink when they cannot fit the screen width.
|
|
582
566
|
setModalSize('large');
|
583
567
|
setModalOpen(true);
|
584
568
|
}}
|
585
|
-
priority="outline"
|
586
569
|
/>
|
587
570
|
<Button
|
588
571
|
label="Launch fullscreen modal"
|
@@ -590,7 +573,6 @@ Modals of any size automatically shrink when they cannot fit the screen width.
|
|
590
573
|
setModalSize('fullscreen');
|
591
574
|
setModalOpen(true);
|
592
575
|
}}
|
593
|
-
priority="outline"
|
594
576
|
/>
|
595
577
|
<div>
|
596
578
|
{modalOpen && (
|
@@ -619,6 +601,7 @@ Modals of any size automatically shrink when they cannot fit the screen width.
|
|
619
601
|
ref={modalPrimaryButtonRef}
|
620
602
|
/>
|
621
603
|
<Button
|
604
|
+
color="secondary"
|
622
605
|
label="Close"
|
623
606
|
onClick={() => setModalOpen(false)}
|
624
607
|
priority="outline"
|
@@ -645,7 +628,6 @@ On top of that, the modal can adjust to the width of its content.
|
|
645
628
|
<Button
|
646
629
|
label="Launch auto-width modal"
|
647
630
|
onClick={() => setModalOpen(true)}
|
648
|
-
priority="outline"
|
649
631
|
/>
|
650
632
|
<div>
|
651
633
|
{modalOpen && (
|
@@ -674,6 +656,7 @@ On top of that, the modal can adjust to the width of its content.
|
|
674
656
|
ref={modalPrimaryButtonRef}
|
675
657
|
/>
|
676
658
|
<Button
|
659
|
+
color="secondary"
|
677
660
|
label="Close"
|
678
661
|
onClick={() => setModalOpen(false)}
|
679
662
|
priority="outline"
|
@@ -688,57 +671,61 @@ On top of that, the modal can adjust to the width of its content.
|
|
688
671
|
}}
|
689
672
|
</Playground>
|
690
673
|
|
691
|
-
|
692
|
-
other auto-layout mechanisms, e.g. the auto-width
|
693
|
-
[FormLayout](/components/form-layout#label-width). It's just too much
|
694
|
-
magic that doesn't work together (yet?) 🎩.
|
674
|
+
## Position
|
695
675
|
|
696
|
-
|
697
|
-
overflow handling comes to the rescue in this kind of scenario, you will be
|
698
|
-
better off with the combination of auto-sized modal and horizontal FormLayout
|
699
|
-
with a fixed label width (i.e. any other than `auto`, see the previous note).
|
676
|
+
Modal can be aligned either to the top or center of the screen.
|
700
677
|
|
701
678
|
<Playground>
|
702
679
|
{() => {
|
703
680
|
const [modalOpen, setModalOpen] = React.useState(false);
|
681
|
+
const [modalPosition, setModalPosition] = React.useState('center');
|
704
682
|
const modalPrimaryButtonRef = React.useRef();
|
705
683
|
const modalCloseButtonRef = React.useRef();
|
706
684
|
return (
|
707
685
|
<>
|
708
686
|
<Button
|
709
|
-
label="Launch
|
710
|
-
onClick={() =>
|
711
|
-
|
687
|
+
label="Launch modal at center"
|
688
|
+
onClick={() => {
|
689
|
+
setModalPosition('center');
|
690
|
+
setModalOpen(true);
|
691
|
+
}}
|
692
|
+
/>
|
693
|
+
<Button
|
694
|
+
label="Launch modal at top"
|
695
|
+
onClick={() => {
|
696
|
+
setModalPosition('top');
|
697
|
+
setModalOpen(true);
|
698
|
+
}}
|
712
699
|
/>
|
713
700
|
<div>
|
714
701
|
{modalOpen && (
|
715
702
|
<Modal
|
716
703
|
closeButtonRef={modalCloseButtonRef}
|
704
|
+
position={modalPosition}
|
717
705
|
primaryButtonRef={modalPrimaryButtonRef}
|
718
|
-
size="auto"
|
719
706
|
>
|
720
707
|
<ModalHeader>
|
721
|
-
<ModalTitle>
|
708
|
+
<ModalTitle>Delete the user?</ModalTitle>
|
722
709
|
<ModalCloseButton onClick={() => setModalOpen(false)} />
|
723
710
|
</ModalHeader>
|
724
711
|
<ModalBody>
|
725
712
|
<ModalContent>
|
726
|
-
<
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
</FormLayout>
|
713
|
+
<p>
|
714
|
+
Do you really want to delete the user <code>admin</code>?
|
715
|
+
This cannot be undone.
|
716
|
+
</p>
|
731
717
|
</ModalContent>
|
732
718
|
</ModalBody>
|
733
719
|
<ModalFooter>
|
734
720
|
<Button
|
735
|
-
color="
|
736
|
-
label="
|
721
|
+
color="danger"
|
722
|
+
label="Delete"
|
737
723
|
onClick={() => setModalOpen(false)}
|
738
724
|
ref={modalPrimaryButtonRef}
|
739
725
|
/>
|
740
726
|
<Button
|
741
|
-
|
727
|
+
color="secondary"
|
728
|
+
label="Close"
|
742
729
|
onClick={() => setModalOpen(false)}
|
743
730
|
priority="outline"
|
744
731
|
ref={modalCloseButtonRef}
|
@@ -752,62 +739,64 @@ with a fixed label width (i.e. any other than `auto`, see the previous note).
|
|
752
739
|
}}
|
753
740
|
</Playground>
|
754
741
|
|
755
|
-
##
|
742
|
+
## Forms in Modals
|
756
743
|
|
757
|
-
|
744
|
+
You can safely place a FormLayout into a Modal of any size, including the
|
745
|
+
auto-width Modal.
|
758
746
|
|
759
747
|
<Playground>
|
760
748
|
{() => {
|
761
749
|
const [modalOpen, setModalOpen] = React.useState(false);
|
762
|
-
const [
|
750
|
+
const [agree, setAgree] = React.useState(true);
|
763
751
|
const modalPrimaryButtonRef = React.useRef();
|
764
752
|
const modalCloseButtonRef = React.useRef();
|
765
753
|
return (
|
766
754
|
<>
|
767
755
|
<Button
|
768
|
-
label="Launch modal
|
769
|
-
onClick={() =>
|
770
|
-
setModalPosition('center');
|
771
|
-
setModalOpen(true);
|
772
|
-
}}
|
773
|
-
priority="outline"
|
774
|
-
/>
|
775
|
-
<Button
|
776
|
-
label="Launch modal at top"
|
777
|
-
onClick={() => {
|
778
|
-
setModalPosition('top');
|
779
|
-
setModalOpen(true);
|
780
|
-
}}
|
781
|
-
priority="outline"
|
756
|
+
label="Launch auto-width modal with auto-width form"
|
757
|
+
onClick={() => setModalOpen(true)}
|
782
758
|
/>
|
783
759
|
<div>
|
784
760
|
{modalOpen && (
|
785
761
|
<Modal
|
786
762
|
closeButtonRef={modalCloseButtonRef}
|
787
|
-
position={modalPosition}
|
788
763
|
primaryButtonRef={modalPrimaryButtonRef}
|
764
|
+
size="auto"
|
789
765
|
>
|
790
766
|
<ModalHeader>
|
791
|
-
<ModalTitle>
|
767
|
+
<ModalTitle>Auto-width form inside auto-width modal</ModalTitle>
|
792
768
|
<ModalCloseButton onClick={() => setModalOpen(false)} />
|
793
769
|
</ModalHeader>
|
794
770
|
<ModalBody>
|
795
771
|
<ModalContent>
|
796
|
-
<
|
797
|
-
|
798
|
-
|
799
|
-
|
772
|
+
<FormLayout autoWidth fieldLayout="horizontal">
|
773
|
+
<TextField
|
774
|
+
label="A form element"
|
775
|
+
validationState="warning"
|
776
|
+
validationText={`Account with this name already exists,
|
777
|
+
pick a different one.`
|
778
|
+
}
|
779
|
+
/>
|
780
|
+
<TextField label="Another form element" />
|
781
|
+
<TextField label="Yet another one" />
|
782
|
+
<CheckboxField
|
783
|
+
checked={agree}
|
784
|
+
label="I agree"
|
785
|
+
onChange={() => setAgree(!agree)}
|
786
|
+
/>
|
787
|
+
</FormLayout>
|
800
788
|
</ModalContent>
|
801
789
|
</ModalBody>
|
802
790
|
<ModalFooter>
|
803
791
|
<Button
|
804
|
-
color="
|
805
|
-
label="
|
792
|
+
color="primary"
|
793
|
+
label="Save"
|
806
794
|
onClick={() => setModalOpen(false)}
|
807
795
|
ref={modalPrimaryButtonRef}
|
808
796
|
/>
|
809
797
|
<Button
|
810
|
-
|
798
|
+
color="secondary"
|
799
|
+
label="Cancel"
|
811
800
|
onClick={() => setModalOpen(false)}
|
812
801
|
priority="outline"
|
813
802
|
ref={modalCloseButtonRef}
|
@@ -821,6 +810,14 @@ Modal can be aligned either to the top or center of the screen.
|
|
821
810
|
}}
|
822
811
|
</Playground>
|
823
812
|
|
813
|
+
👉 Inside Modal, we recommend using the `autoWidth` option of FormLayout. This
|
814
|
+
prevents the Modal from unwanted horizontal expansion when a long validation
|
815
|
+
text pops up during user's interaction with the form.
|
816
|
+
|
817
|
+
👉 Beware of horizontal FormLayout inside `small` modals. While automatic
|
818
|
+
overflow handling comes to the rescue in this kind of scenario, you will be
|
819
|
+
better off with the combination of auto-sized modal and horizontal FormLayout.
|
820
|
+
|
824
821
|
## Keyboard Control
|
825
822
|
|
826
823
|
Modal can be controlled either by mouse or keyboard. To enhance user
|
@@ -829,24 +826,147 @@ can be closed by pressing the `Escape` key.
|
|
829
826
|
|
830
827
|
To enable it, you just need to pass a reference to the buttons using
|
831
828
|
`primaryButtonRef` and `closeButtonRef` props on Modal. The advantage of passing
|
832
|
-
|
833
|
-
not fire the event.
|
834
|
-
|
835
|
-
👉 We strongly recommend using this feature together with Autofocus for a better
|
836
|
-
user experience.
|
829
|
+
the reference to the button is that if the button is disabled, the key press
|
830
|
+
will not fire the event.
|
837
831
|
|
838
832
|
## Autofocus
|
839
833
|
|
840
834
|
Autofocus is implemented to enhance the user experience by automatically
|
841
|
-
|
835
|
+
focusing an element within the Modal.
|
842
836
|
|
843
837
|
How does it work? It tries to find `input`, `textarea`, and `select` elements
|
844
838
|
inside of Modal and moves focus onto the first non-disabled one. If none is
|
845
839
|
found and the `primaryButtonRef` prop on Modal is set, then the primary button
|
846
|
-
is focused.
|
840
|
+
is focused. If there are neither, it tries to focus any other focusable elements.
|
841
|
+
In case there are none or `autoFocus` is disabled, Modal itself is focused.
|
847
842
|
|
848
|
-
|
849
|
-
|
843
|
+
<Playground>
|
844
|
+
{() => {
|
845
|
+
const [modalOpen, setModalOpen] = React.useState(null);
|
846
|
+
const modalPrimaryButtonRef = React.useRef();
|
847
|
+
const modalCloseButtonRef = React.useRef();
|
848
|
+
return (
|
849
|
+
<>
|
850
|
+
<Button
|
851
|
+
label="Launch modal with autofocus and form"
|
852
|
+
onClick={() => setModalOpen(1)}
|
853
|
+
/>
|
854
|
+
<Button
|
855
|
+
label="Launch modal with autofocus"
|
856
|
+
onClick={() => setModalOpen(2)}
|
857
|
+
/>
|
858
|
+
<Button
|
859
|
+
label="Launch modal with autofocus disabled"
|
860
|
+
onClick={() => setModalOpen(3)}
|
861
|
+
/>
|
862
|
+
<div>
|
863
|
+
{modalOpen === 1 && (
|
864
|
+
<Modal
|
865
|
+
closeButtonRef={modalCloseButtonRef}
|
866
|
+
primaryButtonRef={modalPrimaryButtonRef}
|
867
|
+
>
|
868
|
+
<ModalHeader>
|
869
|
+
<ModalTitle>Modal with autoFocus and form</ModalTitle>
|
870
|
+
<ModalCloseButton onClick={() => setModalOpen(null)} />
|
871
|
+
</ModalHeader>
|
872
|
+
<ModalBody>
|
873
|
+
<ModalContent>
|
874
|
+
<FormLayout autoWidth fieldLayout="horizontal">
|
875
|
+
<TextField
|
876
|
+
disabled
|
877
|
+
label="A form element"
|
878
|
+
/>
|
879
|
+
<TextField label="Another form element" />
|
880
|
+
<TextArea label="Yet another one" />
|
881
|
+
</FormLayout>
|
882
|
+
</ModalContent>
|
883
|
+
</ModalBody>
|
884
|
+
<ModalFooter>
|
885
|
+
<Button
|
886
|
+
label="Submit"
|
887
|
+
onClick={() => setModalOpen(null)}
|
888
|
+
ref={modalPrimaryButtonRef}
|
889
|
+
/>
|
890
|
+
<Button
|
891
|
+
color="secondary"
|
892
|
+
label="Close"
|
893
|
+
onClick={() => setModalOpen(null)}
|
894
|
+
priority="outline"
|
895
|
+
ref={modalCloseButtonRef}
|
896
|
+
/>
|
897
|
+
</ModalFooter>
|
898
|
+
</Modal>
|
899
|
+
)}
|
900
|
+
{modalOpen === 2 && (
|
901
|
+
<Modal
|
902
|
+
closeButtonRef={modalCloseButtonRef}
|
903
|
+
primaryButtonRef={modalPrimaryButtonRef}
|
904
|
+
>
|
905
|
+
<ModalHeader>
|
906
|
+
<ModalTitle>Modal with autoFocus enabled with no form</ModalTitle>
|
907
|
+
<ModalCloseButton onClick={() => setModalOpen(null)} />
|
908
|
+
</ModalHeader>
|
909
|
+
<ModalBody>
|
910
|
+
<ModalContent>
|
911
|
+
<p>
|
912
|
+
This Modal autofocuses the primary button or any other
|
913
|
+
focusable element.
|
914
|
+
</p>
|
915
|
+
</ModalContent>
|
916
|
+
</ModalBody>
|
917
|
+
<ModalFooter>
|
918
|
+
<Button
|
919
|
+
label="Acknowledge"
|
920
|
+
onClick={() => setModalOpen(null)}
|
921
|
+
ref={modalPrimaryButtonRef}
|
922
|
+
/>
|
923
|
+
<Button
|
924
|
+
color="secondary"
|
925
|
+
label="Close"
|
926
|
+
onClick={() => setModalOpen(null)}
|
927
|
+
priority="outline"
|
928
|
+
ref={modalCloseButtonRef}
|
929
|
+
/>
|
930
|
+
</ModalFooter>
|
931
|
+
</Modal>
|
932
|
+
)}
|
933
|
+
{modalOpen === 3 && (
|
934
|
+
<Modal
|
935
|
+
autoFocus={false}
|
936
|
+
closeButtonRef={modalCloseButtonRef}
|
937
|
+
primaryButtonRef={modalPrimaryButtonRef}
|
938
|
+
>
|
939
|
+
<ModalHeader>
|
940
|
+
<ModalTitle>Modal with autoFocus disabled</ModalTitle>
|
941
|
+
</ModalHeader>
|
942
|
+
<ModalBody>
|
943
|
+
<ModalContent>
|
944
|
+
<p>
|
945
|
+
This Modal focuses the Modal element itself.
|
946
|
+
</p>
|
947
|
+
</ModalContent>
|
948
|
+
</ModalBody>
|
949
|
+
<ModalFooter>
|
950
|
+
<Button
|
951
|
+
label="Acknowledge"
|
952
|
+
onClick={() => setModalOpen(null)}
|
953
|
+
ref={modalPrimaryButtonRef}
|
954
|
+
/>
|
955
|
+
<Button
|
956
|
+
color="secondary"
|
957
|
+
label="Close"
|
958
|
+
onClick={() => setModalOpen(null)}
|
959
|
+
priority="outline"
|
960
|
+
ref={modalCloseButtonRef}
|
961
|
+
/>
|
962
|
+
</ModalFooter>
|
963
|
+
</Modal>
|
964
|
+
)}
|
965
|
+
</div>
|
966
|
+
</>
|
967
|
+
);
|
968
|
+
}}
|
969
|
+
</Playground>
|
850
970
|
|
851
971
|
## Scrolling Long Content
|
852
972
|
|
@@ -948,7 +1068,6 @@ independent of the page itself. This can be done in three ways using the
|
|
948
1068
|
setModalScrolling('auto');
|
949
1069
|
setModalOpen(true);
|
950
1070
|
}}
|
951
|
-
priority="outline"
|
952
1071
|
/>
|
953
1072
|
<Button
|
954
1073
|
label="Launch modal with ScrollView"
|
@@ -956,7 +1075,6 @@ independent of the page itself. This can be done in three ways using the
|
|
956
1075
|
setModalScrolling('custom');
|
957
1076
|
setModalOpen(true);
|
958
1077
|
}}
|
959
|
-
priority="outline"
|
960
1078
|
/>
|
961
1079
|
<Button
|
962
1080
|
label="Launch modal with non-scrolling body"
|
@@ -964,12 +1082,11 @@ independent of the page itself. This can be done in three ways using the
|
|
964
1082
|
setModalScrolling('none');
|
965
1083
|
setModalOpen(true);
|
966
1084
|
}}
|
967
|
-
priority="outline"
|
968
1085
|
/>
|
969
1086
|
<div>
|
970
1087
|
{modalOpen && (
|
971
1088
|
<Modal
|
972
|
-
autoFocus={
|
1089
|
+
autoFocus={modalScrolling !== 'none'}
|
973
1090
|
closeButtonRef={modalCloseButtonRef}
|
974
1091
|
primaryButtonRef={modalPrimaryButtonRef}
|
975
1092
|
size="small"
|
@@ -996,6 +1113,7 @@ independent of the page itself. This can be done in three ways using the
|
|
996
1113
|
ref={modalPrimaryButtonRef}
|
997
1114
|
/>
|
998
1115
|
<Button
|
1116
|
+
color="secondary"
|
999
1117
|
label="Close"
|
1000
1118
|
onClick={() => setModalOpen(false)}
|
1001
1119
|
priority="outline"
|
@@ -1012,9 +1130,154 @@ independent of the page itself. This can be done in three ways using the
|
|
1012
1130
|
|
1013
1131
|
### Long Content and Autofocus
|
1014
1132
|
|
1015
|
-
👉 If you
|
1016
|
-
|
1017
|
-
opened.
|
1133
|
+
👉 If you have a Modal with `scrolling` set to `none`, you may want to disable
|
1134
|
+
`autoFocus` to prevent the modal from scrolling to the end immediately after
|
1135
|
+
being opened.
|
1136
|
+
|
1137
|
+
## Prevent Scrolling Underneath the Modal
|
1138
|
+
|
1139
|
+
You can choose the mode in which Modal prevents the scroll of the page underneath.
|
1140
|
+
Default mode prevents scrolling on `<body>` element and accounts for the scrollbar
|
1141
|
+
width. If you choose `off`, there will be no scroll prevention. If you need more
|
1142
|
+
flexibility, define your methods `start` (called on Modal's mount) and `reset`
|
1143
|
+
(called on Modal unmount) wrapped by an object and handle scroll prevention
|
1144
|
+
yourself.
|
1145
|
+
|
1146
|
+
<Playground>
|
1147
|
+
{() => {
|
1148
|
+
const [modalOpen, setModalOpen] = React.useState(null);
|
1149
|
+
const modalPrimaryButtonRef = React.useRef();
|
1150
|
+
const modalCloseButtonRef = React.useRef();
|
1151
|
+
const customScrollPreventionObject = {
|
1152
|
+
start: () => {
|
1153
|
+
// YOUR CUSTOM SCROLL PREVENTING LOGIC GOES HERE
|
1154
|
+
window.document.body.style.overflowY = 'hidden'
|
1155
|
+
},
|
1156
|
+
reset: () => {
|
1157
|
+
// YOUR CUSTOM SCROLL RE-ENABLING LOGIC GOES HERE
|
1158
|
+
window.document.body.style.overflowY = 'auto'
|
1159
|
+
},
|
1160
|
+
};
|
1161
|
+
return (
|
1162
|
+
<>
|
1163
|
+
<Button
|
1164
|
+
label="Launch modal with default scroll prevention"
|
1165
|
+
onClick={() => setModalOpen(1)}
|
1166
|
+
/>
|
1167
|
+
<Button
|
1168
|
+
label="Launch modal with no scroll prevention"
|
1169
|
+
onClick={() => setModalOpen(2)}
|
1170
|
+
/>
|
1171
|
+
<Button
|
1172
|
+
label="Launch modal with custom scroll prevention"
|
1173
|
+
onClick={() => setModalOpen(3)}
|
1174
|
+
/>
|
1175
|
+
<div>
|
1176
|
+
{modalOpen === 1 && (
|
1177
|
+
<Modal
|
1178
|
+
closeButtonRef={modalCloseButtonRef}
|
1179
|
+
primaryButtonRef={modalPrimaryButtonRef}
|
1180
|
+
>
|
1181
|
+
<ModalHeader>
|
1182
|
+
<ModalTitle>Modal with default scroll prevention</ModalTitle>
|
1183
|
+
<ModalCloseButton onClick={() => setModalOpen(null)} />
|
1184
|
+
</ModalHeader>
|
1185
|
+
<ModalBody>
|
1186
|
+
<ModalContent>
|
1187
|
+
<p>
|
1188
|
+
This Modal uses default scroll prevention on the document's
|
1189
|
+
<code>body</code> element.
|
1190
|
+
</p>
|
1191
|
+
</ModalContent>
|
1192
|
+
</ModalBody>
|
1193
|
+
<ModalFooter>
|
1194
|
+
<Button
|
1195
|
+
label="Acknowledge"
|
1196
|
+
onClick={() => setModalOpen(null)}
|
1197
|
+
ref={modalPrimaryButtonRef}
|
1198
|
+
/>
|
1199
|
+
<Button
|
1200
|
+
color="secondary"
|
1201
|
+
label="Close"
|
1202
|
+
onClick={() => setModalOpen(null)}
|
1203
|
+
priority="outline"
|
1204
|
+
ref={modalCloseButtonRef}
|
1205
|
+
/>
|
1206
|
+
</ModalFooter>
|
1207
|
+
</Modal>
|
1208
|
+
)}
|
1209
|
+
{modalOpen === 2 && (
|
1210
|
+
<Modal
|
1211
|
+
closeButtonRef={modalCloseButtonRef}
|
1212
|
+
preventScrollUnderneath="off"
|
1213
|
+
primaryButtonRef={modalPrimaryButtonRef}
|
1214
|
+
>
|
1215
|
+
<ModalHeader>
|
1216
|
+
<ModalTitle>Modal with no scroll prevention</ModalTitle>
|
1217
|
+
<ModalCloseButton onClick={() => setModalOpen(null)} />
|
1218
|
+
</ModalHeader>
|
1219
|
+
<ModalBody>
|
1220
|
+
<ModalContent>
|
1221
|
+
<p>
|
1222
|
+
This Modal does not prevent scrolling.
|
1223
|
+
</p>
|
1224
|
+
</ModalContent>
|
1225
|
+
</ModalBody>
|
1226
|
+
<ModalFooter>
|
1227
|
+
<Button
|
1228
|
+
label="Acknowledge"
|
1229
|
+
onClick={() => setModalOpen(null)}
|
1230
|
+
ref={modalPrimaryButtonRef}
|
1231
|
+
/>
|
1232
|
+
<Button
|
1233
|
+
color="secondary"
|
1234
|
+
label="Close"
|
1235
|
+
onClick={() => setModalOpen(null)}
|
1236
|
+
priority="outline"
|
1237
|
+
ref={modalCloseButtonRef}
|
1238
|
+
/>
|
1239
|
+
</ModalFooter>
|
1240
|
+
</Modal>
|
1241
|
+
)}
|
1242
|
+
{modalOpen === 3 && (
|
1243
|
+
<Modal
|
1244
|
+
closeButtonRef={modalCloseButtonRef}
|
1245
|
+
preventScrollUnderneath={customScrollPreventionObject}
|
1246
|
+
primaryButtonRef={modalPrimaryButtonRef}
|
1247
|
+
>
|
1248
|
+
<ModalHeader>
|
1249
|
+
<ModalTitle>Modal with custom scroll prevention</ModalTitle>
|
1250
|
+
<ModalCloseButton onClick={() => setModalOpen(null)} />
|
1251
|
+
</ModalHeader>
|
1252
|
+
<ModalBody>
|
1253
|
+
<ModalContent>
|
1254
|
+
<p>
|
1255
|
+
This Modal uses provided custom functions to prevent scrolling
|
1256
|
+
and reset it on unmount.
|
1257
|
+
</p>
|
1258
|
+
</ModalContent>
|
1259
|
+
</ModalBody>
|
1260
|
+
<ModalFooter>
|
1261
|
+
<Button
|
1262
|
+
label="Acknowledge"
|
1263
|
+
onClick={() => setModalOpen(null)}
|
1264
|
+
ref={modalPrimaryButtonRef}
|
1265
|
+
/>
|
1266
|
+
<Button
|
1267
|
+
color="secondary"
|
1268
|
+
label="Close"
|
1269
|
+
onClick={() => setModalOpen(null)}
|
1270
|
+
priority="outline"
|
1271
|
+
ref={modalCloseButtonRef}
|
1272
|
+
/>
|
1273
|
+
</ModalFooter>
|
1274
|
+
</Modal>
|
1275
|
+
)}
|
1276
|
+
</div>
|
1277
|
+
</>
|
1278
|
+
);
|
1279
|
+
}}
|
1280
|
+
</Playground>
|
1018
1281
|
|
1019
1282
|
<!-- markdownlint-disable MD024 -->
|
1020
1283
|
|
@@ -1090,6 +1353,7 @@ accessibility.
|
|
1090
1353
|
| `--rui-Modal--fullscreen__width` | Width of fullscreen modal |
|
1091
1354
|
| `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
|
1092
1355
|
|
1356
|
+
[nng-modal]: https://www.nngroup.com/articles/modal-nonmodal-dialog/
|
1093
1357
|
[React synthetic events]: https://reactjs.org/docs/events.html
|
1094
1358
|
[div]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#attributes
|
1095
1359
|
[heading]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#attributes
|