@react-ui-org/react-ui 0.52.1 → 0.53.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.
Files changed (260) hide show
  1. package/CODEOWNERS +23 -0
  2. package/README.md +10 -7
  3. package/dist/react-ui.css +5 -3
  4. package/dist/react-ui.js +1 -1
  5. package/package.json +64 -77
  6. package/src/{lib/components → components}/Alert/Alert.jsx +1 -1
  7. package/src/{lib/components/Alert/README.mdx → components/Alert/README.md} +84 -100
  8. package/src/{lib/components → components}/Badge/Badge.jsx +1 -1
  9. package/src/{lib/components → components}/Badge/Badge.scss +1 -1
  10. package/src/components/Badge/README.md +103 -0
  11. package/src/{lib/components → components}/Button/Button.jsx +1 -1
  12. package/src/components/Button/README.md +580 -0
  13. package/src/{lib/components → components}/ButtonGroup/ButtonGroup.jsx +11 -9
  14. package/src/{lib/components/ButtonGroup/README.mdx → components/ButtonGroup/README.md} +128 -134
  15. package/src/{lib/components → components}/Card/Card.jsx +1 -1
  16. package/src/components/Card/README.md +314 -0
  17. package/src/{lib/components/CheckboxField/README.mdx → components/CheckboxField/README.md} +96 -108
  18. package/src/{lib/components/FileInputField/README.mdx → components/FileInputField/README.md} +83 -95
  19. package/src/{lib/components → components}/FormLayout/FormLayout.jsx +4 -4
  20. package/src/components/FormLayout/README.md +462 -0
  21. package/src/{lib/components → components}/Grid/Grid.jsx +2 -2
  22. package/src/components/Grid/README.md +281 -0
  23. package/src/{lib/components → components}/InputGroup/InputGroup.jsx +20 -19
  24. package/src/{lib/components → components}/InputGroup/InputGroup.scss +0 -5
  25. package/src/{lib/components/InputGroup/README.mdx → components/InputGroup/README.md} +145 -163
  26. package/src/{lib/components → components}/Modal/Modal.jsx +6 -6
  27. package/src/components/Modal/README.md +1090 -0
  28. package/src/components/Modal/_hooks/useModalScrollPrevention.js +37 -0
  29. package/src/{lib/components/Paper/README.mdx → components/Paper/README.md} +18 -30
  30. package/src/{lib/components/Popover/README.mdx → components/Popover/README.md} +102 -132
  31. package/src/{lib/components/Radio/README.mdx → components/Radio/README.md} +122 -134
  32. package/src/{lib/components → components}/Radio/Radio.jsx +11 -12
  33. package/src/{lib/components → components}/Radio/Radio.scss +0 -5
  34. package/src/components/ScrollView/README.md +503 -0
  35. package/src/{lib/components → components}/ScrollView/ScrollView.jsx +12 -3
  36. package/src/components/SelectField/README.md +681 -0
  37. package/src/components/Table/README.md +259 -0
  38. package/src/{lib/components → components}/Table/Table.jsx +4 -1
  39. package/src/{lib/components → components}/Table/_components/TableHeaderCell/TableHeaderCell.jsx +1 -1
  40. package/src/{lib/components/Tabs/README.mdx → components/Tabs/README.md} +117 -134
  41. package/src/{lib/components → components}/Tabs/TabsItem.jsx +3 -3
  42. package/src/components/Text/README.md +220 -0
  43. package/src/components/TextArea/README.md +359 -0
  44. package/src/{lib/components/TextField/README.mdx → components/TextField/README.md} +336 -342
  45. package/src/{lib/components/TextLink/README.mdx → components/TextLink/README.md} +19 -31
  46. package/src/{lib/components/Toggle/README.mdx → components/Toggle/README.md} +98 -110
  47. package/src/components/Toolbar/README.md +359 -0
  48. package/src/{lib/components → components}/Toolbar/_helpers/getAlignClassName.js +12 -4
  49. package/src/components/_helpers/getRootPriorityClassName.js +15 -0
  50. package/src/{lib/index.js → index.js} +6 -0
  51. package/src/{lib/provider → provider}/RUIProvider.jsx +17 -11
  52. package/src/{lib/styles → styles}/tools/_caret.scss +1 -1
  53. package/src/{lib/styles → styles}/tools/form-fields/_box-field-elements.scss +1 -1
  54. package/src/{lib/styles → styles}/tools/form-fields/_inline-field-elements.scss +2 -2
  55. package/src/{lib/theme.scss → theme.scss} +4 -3
  56. package/CONTRIBUTING.md +0 -137
  57. package/dist/lib.development.js +0 -3179
  58. package/dist/lib.js +0 -1
  59. package/public/racom.svg +0 -11
  60. package/src/lib/components/Badge/README.mdx +0 -126
  61. package/src/lib/components/Button/README.mdx +0 -581
  62. package/src/lib/components/Card/README.mdx +0 -326
  63. package/src/lib/components/FormLayout/README.mdx +0 -501
  64. package/src/lib/components/Grid/README.mdx +0 -299
  65. package/src/lib/components/Modal/README.mdx +0 -1360
  66. package/src/lib/components/Modal/_hooks/useModalScrollPrevention.js +0 -35
  67. package/src/lib/components/ScrollView/README.mdx +0 -521
  68. package/src/lib/components/SelectField/README.mdx +0 -693
  69. package/src/lib/components/Table/README.mdx +0 -275
  70. package/src/lib/components/Text/README.mdx +0 -241
  71. package/src/lib/components/TextArea/README.mdx +0 -366
  72. package/src/lib/components/Toolbar/README.mdx +0 -386
  73. package/src/{lib/components → components}/Alert/Alert.scss +0 -0
  74. package/src/{lib/components → components}/Alert/_settings.scss +0 -0
  75. package/src/{lib/components → components}/Alert/_theme.scss +0 -0
  76. package/src/{lib/components → components}/Alert/_tools.scss +0 -0
  77. package/src/{lib/components → components}/Alert/index.js +0 -0
  78. package/src/{lib/components → components}/Badge/index.js +0 -0
  79. package/src/{lib/components → components}/Button/Button.scss +0 -0
  80. package/src/{lib/components → components}/Button/_base.scss +0 -0
  81. package/src/{lib/components → components}/Button/_priorities.scss +0 -0
  82. package/src/{lib/components → components}/Button/_settings.scss +0 -0
  83. package/src/{lib/components → components}/Button/_theme.scss +0 -0
  84. package/src/{lib/components → components}/Button/_tools.scss +0 -0
  85. package/src/{lib/components → components}/Button/helpers/getRootLabelVisibilityClassName.js +0 -0
  86. package/src/{lib/components/_helpers → components/Button/helpers}/getRootPriorityClassName.js +0 -0
  87. package/src/{lib/components → components}/Button/index.js +0 -0
  88. package/src/{lib/components → components}/ButtonGroup/ButtonGroup.scss +0 -0
  89. package/src/{lib/components → components}/ButtonGroup/ButtonGroupContext.js +0 -0
  90. package/src/{lib/components → components}/ButtonGroup/_theme.scss +0 -0
  91. package/src/{lib/components → components}/ButtonGroup/index.js +0 -0
  92. package/src/{lib/components → components}/Card/Card.scss +0 -0
  93. package/src/{lib/components → components}/Card/CardBody.jsx +0 -0
  94. package/src/{lib/components → components}/Card/CardFooter.jsx +0 -0
  95. package/src/{lib/components → components}/Card/_theme.scss +0 -0
  96. package/src/{lib/components → components}/Card/_tools.scss +0 -0
  97. package/src/{lib/components → components}/Card/index.js +0 -0
  98. package/src/{lib/components → components}/CheckboxField/CheckboxField.jsx +0 -0
  99. package/src/{lib/components → components}/CheckboxField/CheckboxField.scss +0 -0
  100. package/src/{lib/components → components}/CheckboxField/index.js +0 -0
  101. package/src/{lib/components → components}/FileInputField/FileInputField.jsx +0 -0
  102. package/src/{lib/components → components}/FileInputField/FileInputField.scss +0 -0
  103. package/src/{lib/components → components}/FileInputField/index.js +0 -0
  104. package/src/{lib/components → components}/FormLayout/FormLayout.scss +0 -0
  105. package/src/{lib/components → components}/FormLayout/FormLayoutContext.js +0 -0
  106. package/src/{lib/components → components}/FormLayout/FormLayoutCustomField.jsx +4 -4
  107. package/src/{lib/components → components}/FormLayout/FormLayoutCustomField.scss +0 -0
  108. package/src/{lib/components → components}/FormLayout/_theme.scss +0 -0
  109. package/src/{lib/components → components}/FormLayout/index.js +0 -0
  110. package/src/{lib/components → components}/Grid/Grid.scss +0 -0
  111. package/src/{lib/components → components}/Grid/GridSpan.jsx +0 -0
  112. package/src/{lib/components → components}/Grid/_helpers/generateResponsiveCustomProperties.js +0 -0
  113. package/src/{lib/components → components}/Grid/_settings.scss +0 -0
  114. package/src/{lib/components → components}/Grid/_tools.scss +0 -0
  115. package/src/{lib/components → components}/Grid/index.js +0 -0
  116. package/src/{lib/components → components}/InputGroup/InputGroupContext.js +0 -0
  117. package/src/{lib/components → components}/InputGroup/_theme.scss +0 -0
  118. package/src/{lib/components → components}/InputGroup/index.js +0 -0
  119. package/src/{lib/components → components}/Modal/Modal.scss +0 -0
  120. package/src/{lib/components → components}/Modal/ModalBody.jsx +0 -0
  121. package/src/{lib/components → components}/Modal/ModalBody.scss +0 -0
  122. package/src/{lib/components → components}/Modal/ModalCloseButton.jsx +1 -1
  123. package/src/{lib/components → components}/Modal/ModalCloseButton.scss +0 -0
  124. package/src/{lib/components → components}/Modal/ModalContent.jsx +0 -0
  125. package/src/{lib/components → components}/Modal/ModalContent.scss +0 -0
  126. package/src/{lib/components → components}/Modal/ModalFooter.jsx +0 -0
  127. package/src/{lib/components → components}/Modal/ModalFooter.scss +0 -0
  128. package/src/{lib/components → components}/Modal/ModalHeader.jsx +0 -0
  129. package/src/{lib/components → components}/Modal/ModalHeader.scss +0 -0
  130. package/src/{lib/components → components}/Modal/ModalTitle.jsx +0 -0
  131. package/src/{lib/components → components}/Modal/ModalTitle.scss +0 -0
  132. package/src/{lib/components → components}/Modal/_helpers/getJustifyClassName.js +0 -0
  133. package/src/{lib/components → components}/Modal/_helpers/getPositionClassName.js +0 -0
  134. package/src/{lib/components → components}/Modal/_helpers/getScrollingClassName.js +0 -0
  135. package/src/{lib/components → components}/Modal/_helpers/getSizeClassName.js +0 -0
  136. package/src/{lib/components → components}/Modal/_hooks/useModalFocus.js +0 -0
  137. package/src/{lib/components → components}/Modal/_settings.scss +0 -0
  138. package/src/{lib/components → components}/Modal/_theme.scss +0 -0
  139. package/src/{lib/components → components}/Modal/index.js +0 -0
  140. package/src/{lib/components → components}/Paper/Paper.jsx +0 -0
  141. package/src/{lib/components → components}/Paper/Paper.scss +0 -0
  142. package/src/{lib/components → components}/Paper/_theme.scss +0 -0
  143. package/src/{lib/components → components}/Paper/index.js +0 -0
  144. package/src/{lib/components → components}/Popover/Popover.jsx +0 -0
  145. package/src/{lib/components → components}/Popover/Popover.scss +0 -0
  146. package/src/{lib/components → components}/Popover/PopoverWrapper.jsx +0 -0
  147. package/src/{lib/components → components}/Popover/PopoverWrapper.scss +0 -0
  148. package/src/{lib/components → components}/Popover/_helpers/getRootAlignmentClassName.js +0 -0
  149. package/src/{lib/components → components}/Popover/_helpers/getRootSideClassName.js +0 -0
  150. package/src/{lib/components → components}/Popover/_theme.scss +0 -0
  151. package/src/{lib/components → components}/Popover/index.js +0 -0
  152. package/src/{lib/components → components}/Radio/index.js +0 -0
  153. package/src/{lib/components → components}/ScrollView/ScrollView.scss +0 -0
  154. package/src/{lib/components → components}/ScrollView/_helpers/getElementsPositionDifference.js +0 -0
  155. package/src/{lib/components → components}/ScrollView/_hooks/useLoadResizeHook.js +0 -0
  156. package/src/{lib/components → components}/ScrollView/_hooks/useScrollPositionHook.js +0 -0
  157. package/src/{lib/components → components}/ScrollView/index.js +0 -0
  158. package/src/{lib/components → components}/SelectField/SelectField.jsx +0 -0
  159. package/src/{lib/components → components}/SelectField/SelectField.scss +0 -0
  160. package/src/{lib/components → components}/SelectField/_components/Option/Option.jsx +0 -0
  161. package/src/{lib/components → components}/SelectField/_components/Option/index.js +0 -0
  162. package/src/{lib/components → components}/SelectField/index.js +0 -0
  163. package/src/{lib/components → components}/Table/Table.scss +0 -0
  164. package/src/{lib/components → components}/Table/_components/TableBodyCell/TableBodyCell.jsx +0 -0
  165. package/src/{lib/components → components}/Table/_components/TableBodyCell/index.js +0 -0
  166. package/src/{lib/components → components}/Table/_components/TableCell.scss +0 -0
  167. package/src/{lib/components → components}/Table/_components/TableHeaderCell/index.js +0 -0
  168. package/src/{lib/components → components}/Table/_settings.scss +0 -0
  169. package/src/{lib/components → components}/Table/index.js +0 -0
  170. package/src/{lib/components → components}/Tabs/Tabs.jsx +0 -0
  171. package/src/{lib/components → components}/Tabs/Tabs.scss +0 -0
  172. package/src/{lib/components → components}/Tabs/TabsItem.scss +0 -0
  173. package/src/{lib/components → components}/Tabs/_theme.scss +0 -0
  174. package/src/{lib/components → components}/Tabs/index.js +0 -0
  175. package/src/{lib/components → components}/Text/Text.jsx +0 -0
  176. package/src/{lib/components → components}/Text/Text.scss +0 -0
  177. package/src/{lib/components → components}/Text/_helpers/getRootClampClassName.js +0 -0
  178. package/src/{lib/components → components}/Text/_helpers/getRootHyphensClassName.js +0 -0
  179. package/src/{lib/components → components}/Text/_helpers/getRootWordWrappingClassName.js +0 -0
  180. package/src/{lib/components → components}/Text/index.js +0 -0
  181. package/src/{lib/components → components}/TextArea/TextArea.jsx +0 -0
  182. package/src/{lib/components → components}/TextArea/TextArea.scss +0 -0
  183. package/src/{lib/components → components}/TextArea/index.js +0 -0
  184. package/src/{lib/components → components}/TextField/TextField.jsx +0 -0
  185. package/src/{lib/components → components}/TextField/TextField.scss +0 -0
  186. package/src/{lib/components → components}/TextField/index.js +0 -0
  187. package/src/{lib/components → components}/TextLink/TextLink.jsx +1 -1
  188. /package/src/{lib/components → components}/TextLink/TextLink.scss +0 -0
  189. /package/src/{lib/components → components}/TextLink/_theme.scss +0 -0
  190. /package/src/{lib/components → components}/TextLink/index.js +0 -0
  191. /package/src/{lib/components → components}/Toggle/Toggle.jsx +0 -0
  192. /package/src/{lib/components → components}/Toggle/Toggle.scss +0 -0
  193. /package/src/{lib/components → components}/Toggle/index.js +0 -0
  194. /package/src/{lib/components → components}/Toolbar/Toolbar.jsx +0 -0
  195. /package/src/{lib/components → components}/Toolbar/Toolbar.scss +0 -0
  196. /package/src/{lib/components → components}/Toolbar/ToolbarGroup.jsx +0 -0
  197. /package/src/{lib/components → components}/Toolbar/ToolbarItem.jsx +0 -0
  198. /package/src/{lib/components → components}/Toolbar/_helpers/getJustifyClassName.js +0 -0
  199. /package/src/{lib/components → components}/Toolbar/_theme.scss +0 -0
  200. /package/src/{lib/components → components}/Toolbar/index.js +0 -0
  201. /package/src/{lib/components → components}/_helpers/getRootColorClassName.js +0 -0
  202. /package/src/{lib/components → components}/_helpers/getRootSizeClassName.js +0 -0
  203. /package/src/{lib/components → components}/_helpers/getRootValidationStateClassName.js +0 -0
  204. /package/src/{lib/components → components}/_helpers/isChildrenEmpty.js +0 -0
  205. /package/src/{lib/components → components}/_helpers/resolveContextOrProp.js +0 -0
  206. /package/src/{lib/components → components}/_helpers/transferProps.js +0 -0
  207. /package/src/{lib/foundation.scss → foundation.scss} +0 -0
  208. /package/src/{lib/helpers.scss → helpers.scss} +0 -0
  209. /package/src/{lib/provider → provider}/RUIContext.jsx +0 -0
  210. /package/src/{lib/provider → provider}/index.js +0 -0
  211. /package/src/{lib/provider → provider}/withGlobalProps.jsx +0 -0
  212. /package/src/{lib/styles → styles}/_utilities.scss +0 -0
  213. /package/src/{lib/styles → styles}/elements/_code.scss +0 -0
  214. /package/src/{lib/styles → styles}/elements/_links.scss +0 -0
  215. /package/src/{lib/styles → styles}/elements/_lists.scss +0 -0
  216. /package/src/{lib/styles → styles}/elements/_page.scss +0 -0
  217. /package/src/{lib/styles → styles}/elements/_rulers.scss +0 -0
  218. /package/src/{lib/styles → styles}/elements/_small.scss +0 -0
  219. /package/src/{lib/styles → styles}/generic/_box-sizing.scss +0 -0
  220. /package/src/{lib/styles → styles}/generic/_focus.scss +0 -0
  221. /package/src/{lib/styles → styles}/generic/_forms.scss +0 -0
  222. /package/src/{lib/styles → styles}/generic/_reset.scss +0 -0
  223. /package/src/{lib/styles → styles}/generic/_shared.scss +0 -0
  224. /package/src/{lib/styles → styles}/helpers/_animation.scss +0 -0
  225. /package/src/{lib/styles → styles}/settings/_animations.scss +0 -0
  226. /package/src/{lib/styles → styles}/settings/_breakpoints.scss +0 -0
  227. /package/src/{lib/styles → styles}/settings/_escaped-characters.scss +0 -0
  228. /package/src/{lib/styles → styles}/settings/_form-fields.scss +0 -0
  229. /package/src/{lib/styles → styles}/settings/_forms.scss +0 -0
  230. /package/src/{lib/styles → styles}/settings/_utilities.scss +0 -0
  231. /package/src/{lib/styles → styles}/settings/_z-indexes.scss +0 -0
  232. /package/src/{lib/styles → styles}/theme/_accessibility.scss +0 -0
  233. /package/src/{lib/styles → styles}/theme/_borders.scss +0 -0
  234. /package/src/{lib/styles → styles}/theme/_code.scss +0 -0
  235. /package/src/{lib/styles → styles}/theme/_form-fields.scss +0 -0
  236. /package/src/{lib/styles → styles}/theme/_links.scss +0 -0
  237. /package/src/{lib/styles → styles}/theme/_lists.scss +0 -0
  238. /package/src/{lib/styles → styles}/theme/_page.scss +0 -0
  239. /package/src/{lib/styles → styles}/theme/_spacing.scss +0 -0
  240. /package/src/{lib/styles → styles}/theme/_typography.scss +0 -0
  241. /package/src/{lib/styles → styles}/theme-constants/_breakpoints.scss +0 -0
  242. /package/src/{lib/styles → styles}/theme-constants/_colors.scss +0 -0
  243. /package/src/{lib/styles → styles}/theme-constants/_svg.scss +0 -0
  244. /package/src/{lib/styles → styles}/tools/_accessibility.scss +0 -0
  245. /package/src/{lib/styles → styles}/tools/_breakpoint.scss +0 -0
  246. /package/src/{lib/styles → styles}/tools/_colors.scss +0 -0
  247. /package/src/{lib/styles → styles}/tools/_reset.scss +0 -0
  248. /package/src/{lib/styles → styles}/tools/_scrollbar.scss +0 -0
  249. /package/src/{lib/styles → styles}/tools/_spacing.scss +0 -0
  250. /package/src/{lib/styles → styles}/tools/_string.scss +0 -0
  251. /package/src/{lib/styles → styles}/tools/_svg.scss +0 -0
  252. /package/src/{lib/styles → styles}/tools/_transition.scss +0 -0
  253. /package/src/{lib/styles → styles}/tools/_utilities.scss +0 -0
  254. /package/src/{lib/styles → styles}/tools/form-fields/_box-field-layout.scss +0 -0
  255. /package/src/{lib/styles → styles}/tools/form-fields/_box-field-sizes.scss +0 -0
  256. /package/src/{lib/styles → styles}/tools/form-fields/_foundation.scss +0 -0
  257. /package/src/{lib/styles → styles}/tools/form-fields/_inline-field-layout.scss +0 -0
  258. /package/src/{lib/styles → styles}/tools/form-fields/_variants.scss +0 -0
  259. /package/src/{lib/translations → translations}/en.js +0 -0
  260. /package/src/{lib/utils → utils}/classNames.js +0 -0
@@ -1,1360 +0,0 @@
1
- ---
2
- name: Modal
3
- menu: 'Miscellaneous'
4
- route: /components/modal
5
- ---
6
-
7
- # Modal
8
-
9
- Modal allows prompting users to take or complete an action.
10
-
11
- import {
12
- Playground,
13
- Props,
14
- } from 'docz'
15
- import {
16
- Button,
17
- CheckboxField,
18
- FormLayout,
19
- Modal,
20
- ModalBody,
21
- ModalCloseButton,
22
- ModalContent,
23
- ModalFooter,
24
- ModalHeader,
25
- ModalTitle,
26
- Radio,
27
- ScrollView,
28
- TextArea,
29
- TextField,
30
- Toolbar,
31
- ToolbarGroup,
32
- ToolbarItem,
33
- } from '../..'
34
- import Icon from '../../../docs/_components/Icon'
35
-
36
- ## Basic Usage
37
-
38
- To implement the Modal component, you need to import it first:
39
-
40
- ```js
41
- import {
42
- Modal,
43
- ModalHeader,
44
- ModalBody,
45
- ModalCloseButton,
46
- ModalContent,
47
- ModalFooter,
48
- ModalTitle,
49
- } from '@react-ui-org/react-ui';
50
- ```
51
-
52
- And use it:
53
-
54
- <Playground>
55
- {() => {
56
- const [modalOpen, setModalOpen] = React.useState(false);
57
- const modalPrimaryButtonRef = React.useRef();
58
- const modalCloseButtonRef = React.useRef();
59
- return (
60
- <>
61
- <Button label="Launch modal" onClick={() => setModalOpen(true)} />
62
- <div>
63
- {modalOpen && (
64
- <Modal
65
- closeButtonRef={modalCloseButtonRef}
66
- primaryButtonRef={modalPrimaryButtonRef}
67
- >
68
- <ModalHeader>
69
- <ModalTitle>Delete the user?</ModalTitle>
70
- <ModalCloseButton onClick={() => setModalOpen(false)} />
71
- </ModalHeader>
72
- <ModalBody>
73
- <ModalContent>
74
- <p>
75
- Do you really want to delete the user <code>admin</code>?
76
- This cannot be undone.
77
- </p>
78
- </ModalContent>
79
- </ModalBody>
80
- <ModalFooter>
81
- <Button
82
- color="danger"
83
- label="Delete"
84
- onClick={() => setModalOpen(false)}
85
- ref={modalPrimaryButtonRef}
86
- />
87
- <Button
88
- color="secondary"
89
- label="Close"
90
- onClick={() => setModalOpen(false)}
91
- priority="outline"
92
- ref={modalCloseButtonRef}
93
- />
94
- </ModalFooter>
95
- </Modal>
96
- )}
97
- </div>
98
- </>
99
- );
100
- }}
101
- </Playground>
102
-
103
- See [API](#api) for all available options.
104
-
105
- ## General Guidelines
106
-
107
- - Use modals to **confirm an action,** display a **blocking alert**, or reveal
108
- **contextual options or settings** that cannot be displayed in line with the
109
- parent content.
110
-
111
- - **The title** should communicate the **purpose of the modal** rather than a
112
- generic text. Eg. “Delete the user?” tells more than “Are you sure?” or
113
- “Warning”.
114
-
115
- - **Modal actions** should correspond to the modal purpose, too. E.g. “Delete”
116
- tells better what happens rather than “OK”.
117
-
118
- - Modal **automatically focuses the first non-disabled form field** by default
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. 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.
123
-
124
- - **Avoid stacking** of modals. While it may technically work, the modal is just
125
- not designed for that.
126
-
127
- 📖 [Read more about modals at Nielsen Norman Group.][nng-modal]
128
-
129
- ## Composition
130
-
131
- Modal is decomposed into the following components:
132
-
133
- - [Modal](#api)
134
- - [ModalHeader](#modalheader-1)
135
- - [ModalTitle](#modaltitle)
136
- - [ModalCloseButton](#modalclosebutton)
137
- - [ModalBody](#modalbody-1)
138
- - [ModalContent](#modalcontent)
139
- (may be wrapped with [ScrollView](/components/scroll-view))
140
- - [ModalFooter](#modalfooter-1)
141
-
142
- Using different combinations, you can compose different kinds of modals,
143
- e.g. dialog modal, blocking modal, scrollable modal, etc.
144
-
145
- <Playground>
146
- {() => {
147
- const [modalOpen, setModalOpen] = React.useState(null);
148
- const modalPrimaryButtonRef = React.useRef();
149
- const modalCloseButtonRef = React.useRef();
150
- return (
151
- <>
152
- <Button
153
- label="Launch blocking modal without title"
154
- onClick={() => {
155
- setModalOpen(1);
156
- setTimeout(() => setModalOpen(null), 2500);
157
- }}
158
- />
159
- <Button
160
- label="Launch blocking modal with title"
161
- onClick={() => {
162
- setModalOpen(2);
163
- setTimeout(() => setModalOpen(null), 3500);
164
- }}
165
- />
166
- <Button
167
- label="Launch modal as dialog"
168
- onClick={() => setModalOpen(3)}
169
- />
170
- <Button
171
- label="Launch modal as form"
172
- onClick={() => setModalOpen(4)}
173
- />
174
- <div>
175
- {modalOpen === 1 && (
176
- <Modal>
177
- <ModalBody>
178
- <ModalContent>
179
- <p className="text-center">
180
- Application is being loaded.
181
- <span className="d-inline-flex align-items-center animation-spin-counterclockwise">
182
- <Icon icon="loading" />
183
- </span>
184
- </p>
185
- </ModalContent>
186
- </ModalBody>
187
- </Modal>
188
- )}
189
- {modalOpen === 2 && (
190
- <Modal>
191
- <ModalHeader>
192
- <ModalTitle>Action finished</ModalTitle>
193
- </ModalHeader>
194
- <ModalBody>
195
- <ModalContent>
196
- <p>
197
- Action has been successfully finished.
198
- You will be redirected within a few seconds.
199
- </p>
200
- </ModalContent>
201
- </ModalBody>
202
- </Modal>
203
- )}
204
- {modalOpen === 3 && (
205
- <Modal
206
- closeButtonRef={modalCloseButtonRef}
207
- primaryButtonRef={modalPrimaryButtonRef}
208
- >
209
- <ModalHeader>
210
- <ModalTitle>Delete the user?</ModalTitle>
211
- <ModalCloseButton onClick={() => setModalOpen(false)} />
212
- </ModalHeader>
213
- <ModalBody>
214
- <ModalContent>
215
- <p>
216
- Do you really want to delete the user <code>admin</code>?
217
- This cannot be undone.
218
- </p>
219
- </ModalContent>
220
- </ModalBody>
221
- <ModalFooter>
222
- <Button
223
- color="danger"
224
- label="Delete"
225
- onClick={() => setModalOpen(false)}
226
- ref={modalPrimaryButtonRef}
227
- />
228
- <Button
229
- color="secondary"
230
- label="Close"
231
- onClick={() => setModalOpen(false)}
232
- priority="outline"
233
- ref={modalCloseButtonRef}
234
- />
235
- </ModalFooter>
236
- </Modal>
237
- )}
238
- {modalOpen === 4 && (
239
- <Modal
240
- closeButtonRef={modalCloseButtonRef}
241
- primaryButtonRef={modalPrimaryButtonRef}
242
- >
243
- <ModalHeader>
244
- <ModalTitle>Add new user</ModalTitle>
245
- <ModalCloseButton onClick={() => setModalOpen(false)} />
246
- </ModalHeader>
247
- <ModalBody>
248
- <ModalContent>
249
- <FormLayout fieldLayout="horizontal">
250
- <TextField label="Username" />
251
- <TextField label="Password" type="password" />
252
- </FormLayout>
253
- </ModalContent>
254
- </ModalBody>
255
- <ModalFooter>
256
- <Button
257
- label="Save"
258
- onClick={() => setModalOpen(false)}
259
- ref={modalPrimaryButtonRef}
260
- />
261
- <Button
262
- color="secondary"
263
- label="Close"
264
- onClick={() => setModalOpen(false)}
265
- priority="outline"
266
- ref={modalCloseButtonRef}
267
- />
268
- </ModalFooter>
269
- </Modal>
270
- )}
271
- </div>
272
- </>
273
- );
274
- }}
275
- </Playground>
276
-
277
- ### ModalHeader
278
-
279
- ModalHeader is an optional part of the Modal which allows you to display the title
280
- of the modal and its close button.
281
-
282
- It is recommended to compose ModalHeader from the following elements. For title,
283
- use ModalTitle. For the close button, use ModalCloseButton, however it can
284
- be omitted if a close button is part of ModalFooter.
285
-
286
- There are two ways how to position elements within the ModalHeader:
287
-
288
- 1. You can use provided positioning. Place previously mentioned elements into
289
- the ModalHeader and use `justify` prop to set up the positioning of those
290
- elements.
291
- 2. You can customize positioning using another component (e.g.
292
- [Toolbar](/components/toolbar)). In that case, set `justify` to `stretch` and
293
- position elements on your own.
294
-
295
- <Playground>
296
- {() => {
297
- const [modalOpen, setModalOpen] = React.useState(false);
298
- const [variant, setVariant] = React.useState(null);
299
- const modalPrimaryButtonRef = React.useRef();
300
- const modalCloseButtonRef = React.useRef();
301
- return (
302
- <>
303
- <Button
304
- label="Launch with close button"
305
- onClick={() => {
306
- setModalOpen(true);
307
- setVariant(1);
308
- }}
309
- />
310
- <Button
311
- label="Launch without close button"
312
- onClick={() => {
313
- setModalOpen(true);
314
- setVariant(2);
315
- }}
316
- />
317
- <Button
318
- label="Launch without close button and with centered title"
319
- onClick={() => {
320
- setModalOpen(true);
321
- setVariant(3);
322
- }}
323
- />
324
- <Button
325
- label="Launch with custom layout"
326
- onClick={() => {
327
- setModalOpen(true);
328
- setVariant(4);
329
- }}
330
- />
331
- <div>
332
- {modalOpen && (
333
- <Modal
334
- closeButtonRef={modalCloseButtonRef}
335
- primaryButtonRef={modalPrimaryButtonRef}
336
- >
337
- {variant === 1 && (
338
- <ModalHeader>
339
- <ModalTitle>Delete the user?</ModalTitle>
340
- <ModalCloseButton onClick={() => setModalOpen(false)} />
341
- </ModalHeader>
342
- )}
343
- {variant === 2 && (
344
- <ModalHeader>
345
- <ModalTitle>Delete the user?</ModalTitle>
346
- </ModalHeader>
347
- )}
348
- {variant === 3 && (
349
- <ModalHeader justify="center">
350
- <ModalTitle>Delete the user?</ModalTitle>
351
- </ModalHeader>
352
- )}
353
- {variant === 4 && (
354
- <ModalHeader justify="stretch">
355
- <Toolbar justify="space-between">
356
- <ToolbarItem>
357
- {''}
358
- </ToolbarItem>
359
- <ToolbarItem>
360
- <ModalTitle>Delete the user?</ModalTitle>
361
- </ToolbarItem>
362
- <ToolbarItem>
363
- <ModalCloseButton onClick={() => setModalOpen(false)} />
364
- </ToolbarItem>
365
- </Toolbar>
366
- </ModalHeader>
367
- )}
368
- <ModalBody>
369
- <ModalContent>
370
- <p>
371
- Do you really want to delete the user <code>admin</code>?
372
- This cannot be undone.
373
- </p>
374
- </ModalContent>
375
- </ModalBody>
376
- <ModalFooter>
377
- <Button
378
- color="danger"
379
- label="Delete"
380
- onClick={() => setModalOpen(false)}
381
- ref={modalPrimaryButtonRef}
382
- />
383
- <Button
384
- color="secondary"
385
- label="Close"
386
- onClick={() => setModalOpen(false)}
387
- priority="outline"
388
- ref={modalCloseButtonRef}
389
- />
390
- </ModalFooter>
391
- </Modal>
392
- )}
393
- </div>
394
- </>
395
- );
396
- }}
397
- </Playground>
398
-
399
- ### ModalBody
400
-
401
- ModalBody is a mandatory part of the Modal which allows you to display the
402
- content of the Modal.
403
-
404
- Although the ModalBody allows you to display arbitrary content, you should not
405
- place content directly into the ModalBody, but wrap it with ModalContent first.
406
-
407
- In case your content is expected to be long, consider wrapping ModalContent
408
- with ScrollView. Check [Scrolling Long Content](#scrolling-long-content) section
409
- below.
410
-
411
- ### ModalFooter
412
-
413
- ModalFooter is an optional part of the Modal which allows you to display
414
- user actions.
415
-
416
- There are two ways to position buttons within the ModalFooter:
417
-
418
- 1. You can use provided positioning. Place Button component (or any arbitrary
419
- element) and use `justify` prop to set up the positioning of those elements.
420
- 2. You can customize positioning using another component (e.g.
421
- [Toolbar](/components/toolbar)). In that case, set `justify` to `stretch`
422
- and position elements on your own.
423
-
424
- <Playground>
425
- {() => {
426
- const [modalOpen, setModalOpen] = React.useState(false);
427
- const [modalJustify, setModalJustify] = React.useState('center');
428
- const modalPrimaryButtonRef = React.useRef();
429
- const modalCloseButtonRef = React.useRef();
430
- return (
431
- <>
432
- <Button
433
- label="Launch modal with footer variants"
434
- onClick={() => setModalOpen(true)}
435
- />
436
- <div>
437
- {modalOpen && (
438
- <Modal
439
- closeButtonRef={modalCloseButtonRef}
440
- primaryButtonRef={modalPrimaryButtonRef}
441
- >
442
- <ModalHeader>
443
- <ModalTitle>Footer justification</ModalTitle>
444
- <ModalCloseButton onClick={() => setModalOpen(false)} />
445
- </ModalHeader>
446
- <ModalBody>
447
- <ModalContent>
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
- />
475
- </ModalContent>
476
- </ModalBody>
477
- <ModalFooter justify={modalJustify}>
478
- {
479
- modalJustify === 'stretch'
480
- ? (
481
- <Toolbar justify="space-between">
482
- <ToolbarGroup>
483
- <ToolbarItem>
484
- <Button
485
- color="danger"
486
- label="Delete"
487
- onClick={() => setModalOpen(false)}
488
- ref={modalPrimaryButtonRef}
489
- />
490
- </ToolbarItem>
491
- <ToolbarItem>
492
- <Button
493
- color="warning"
494
- label="Archive"
495
- onClick={() => setModalOpen(false)}
496
- ref={modalPrimaryButtonRef}
497
- />
498
- </ToolbarItem>
499
- </ToolbarGroup>
500
- <ToolbarItem>
501
- <Button
502
- color="secondary"
503
- label="Close"
504
- onClick={() => setModalOpen(false)}
505
- priority="outline"
506
- ref={modalCloseButtonRef}
507
- />
508
- </ToolbarItem>
509
- </Toolbar>
510
- ) : (
511
- <>
512
- <Button
513
- label="OK"
514
- onClick={() => setModalOpen(false)}
515
- ref={modalPrimaryButtonRef}
516
- />
517
- <Button
518
- color="secondary"
519
- label="Close"
520
- onClick={() => setModalOpen(false)}
521
- priority="outline"
522
- ref={modalCloseButtonRef}
523
- />
524
- </>
525
- )
526
- }
527
- </ModalFooter>
528
- </Modal>
529
- )}
530
- </div>
531
- </>
532
- );
533
- }}
534
- </Playground>
535
-
536
- ## Sizes
537
-
538
- Modal is available in three fixed-width sizes: small, medium, large and fullscreen.
539
- Modals of any size automatically shrink when they cannot fit the screen width.
540
-
541
- <Playground>
542
- {() => {
543
- const [modalOpen, setModalOpen] = React.useState(false);
544
- const [modalSize, setModalSize] = React.useState('small');
545
- const modalPrimaryButtonRef = React.useRef();
546
- const modalCloseButtonRef = React.useRef();
547
- return (
548
- <>
549
- <Button
550
- label="Launch small modal"
551
- onClick={() => {
552
- setModalSize('small');
553
- setModalOpen(true);
554
- }}
555
- />
556
- <Button
557
- label="Launch medium modal"
558
- onClick={() => {
559
- setModalSize('medium');
560
- setModalOpen(true);
561
- }}
562
- />
563
- <Button
564
- label="Launch large modal"
565
- onClick={() => {
566
- setModalSize('large');
567
- setModalOpen(true);
568
- }}
569
- />
570
- <Button
571
- label="Launch fullscreen modal"
572
- onClick={() => {
573
- setModalSize('fullscreen');
574
- setModalOpen(true);
575
- }}
576
- />
577
- <div>
578
- {modalOpen && (
579
- <Modal
580
- closeButtonRef={modalCloseButtonRef}
581
- primaryButtonRef={modalPrimaryButtonRef}
582
- size={modalSize}
583
- >
584
- <ModalHeader>
585
- <ModalTitle>Delete the user?</ModalTitle>
586
- <ModalCloseButton onClick={() => setModalOpen(false)} />
587
- </ModalHeader>
588
- <ModalBody>
589
- <ModalContent>
590
- <p>
591
- Do you really want to delete the user <code>admin</code>?
592
- This cannot be undone.
593
- </p>
594
- </ModalContent>
595
- </ModalBody>
596
- <ModalFooter>
597
- <Button
598
- color="danger"
599
- label="Delete"
600
- onClick={() => setModalOpen(false)}
601
- ref={modalPrimaryButtonRef}
602
- />
603
- <Button
604
- color="secondary"
605
- label="Close"
606
- onClick={() => setModalOpen(false)}
607
- priority="outline"
608
- ref={modalCloseButtonRef}
609
- />
610
- </ModalFooter>
611
- </Modal>
612
- )}
613
- </div>
614
- </>
615
- );
616
- }}
617
- </Playground>
618
-
619
- On top of that, the modal can adjust to the width of its content.
620
-
621
- <Playground>
622
- {() => {
623
- const [modalOpen, setModalOpen] = React.useState(false);
624
- const modalPrimaryButtonRef = React.useRef();
625
- const modalCloseButtonRef = React.useRef();
626
- return (
627
- <>
628
- <Button
629
- label="Launch auto-width modal"
630
- onClick={() => setModalOpen(true)}
631
- />
632
- <div>
633
- {modalOpen && (
634
- <Modal
635
- closeButtonRef={modalCloseButtonRef}
636
- primaryButtonRef={modalPrimaryButtonRef}
637
- size="auto"
638
- >
639
- <ModalHeader>
640
- <ModalTitle>Delete the user?</ModalTitle>
641
- <ModalCloseButton onClick={() => setModalOpen(false)} />
642
- </ModalHeader>
643
- <ModalBody>
644
- <ModalContent>
645
- <p>
646
- Do you really want to delete the user <code>admin</code>?
647
- This cannot be undone.
648
- </p>
649
- </ModalContent>
650
- </ModalBody>
651
- <ModalFooter>
652
- <Button
653
- color="danger"
654
- label="Delete"
655
- onClick={() => setModalOpen(false)}
656
- ref={modalPrimaryButtonRef}
657
- />
658
- <Button
659
- color="secondary"
660
- label="Close"
661
- onClick={() => setModalOpen(false)}
662
- priority="outline"
663
- ref={modalCloseButtonRef}
664
- />
665
- </ModalFooter>
666
- </Modal>
667
- )}
668
- </div>
669
- </>
670
- );
671
- }}
672
- </Playground>
673
-
674
- ## Position
675
-
676
- Modal can be aligned either to the top or center of the screen.
677
-
678
- <Playground>
679
- {() => {
680
- const [modalOpen, setModalOpen] = React.useState(false);
681
- const [modalPosition, setModalPosition] = React.useState('center');
682
- const modalPrimaryButtonRef = React.useRef();
683
- const modalCloseButtonRef = React.useRef();
684
- return (
685
- <>
686
- <Button
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
- }}
699
- />
700
- <div>
701
- {modalOpen && (
702
- <Modal
703
- closeButtonRef={modalCloseButtonRef}
704
- position={modalPosition}
705
- primaryButtonRef={modalPrimaryButtonRef}
706
- >
707
- <ModalHeader>
708
- <ModalTitle>Delete the user?</ModalTitle>
709
- <ModalCloseButton onClick={() => setModalOpen(false)} />
710
- </ModalHeader>
711
- <ModalBody>
712
- <ModalContent>
713
- <p>
714
- Do you really want to delete the user <code>admin</code>?
715
- This cannot be undone.
716
- </p>
717
- </ModalContent>
718
- </ModalBody>
719
- <ModalFooter>
720
- <Button
721
- color="danger"
722
- label="Delete"
723
- onClick={() => setModalOpen(false)}
724
- ref={modalPrimaryButtonRef}
725
- />
726
- <Button
727
- color="secondary"
728
- label="Close"
729
- onClick={() => setModalOpen(false)}
730
- priority="outline"
731
- ref={modalCloseButtonRef}
732
- />
733
- </ModalFooter>
734
- </Modal>
735
- )}
736
- </div>
737
- </>
738
- );
739
- }}
740
- </Playground>
741
-
742
- ## Forms in Modals
743
-
744
- You can safely place a FormLayout into a Modal of any size, including the
745
- auto-width Modal.
746
-
747
- <Playground>
748
- {() => {
749
- const [modalOpen, setModalOpen] = React.useState(false);
750
- const [agree, setAgree] = React.useState(true);
751
- const modalPrimaryButtonRef = React.useRef();
752
- const modalCloseButtonRef = React.useRef();
753
- return (
754
- <>
755
- <Button
756
- label="Launch auto-width modal with auto-width form"
757
- onClick={() => setModalOpen(true)}
758
- />
759
- <div>
760
- {modalOpen && (
761
- <Modal
762
- closeButtonRef={modalCloseButtonRef}
763
- primaryButtonRef={modalPrimaryButtonRef}
764
- size="auto"
765
- >
766
- <ModalHeader>
767
- <ModalTitle>Auto-width form inside auto-width modal</ModalTitle>
768
- <ModalCloseButton onClick={() => setModalOpen(false)} />
769
- </ModalHeader>
770
- <ModalBody>
771
- <ModalContent>
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>
788
- </ModalContent>
789
- </ModalBody>
790
- <ModalFooter>
791
- <Button
792
- color="primary"
793
- label="Save"
794
- onClick={() => setModalOpen(false)}
795
- ref={modalPrimaryButtonRef}
796
- />
797
- <Button
798
- color="secondary"
799
- label="Cancel"
800
- onClick={() => setModalOpen(false)}
801
- priority="outline"
802
- ref={modalCloseButtonRef}
803
- />
804
- </ModalFooter>
805
- </Modal>
806
- )}
807
- </div>
808
- </>
809
- );
810
- }}
811
- </Playground>
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
-
821
- ## Keyboard Control
822
-
823
- Modal can be controlled either by mouse or keyboard. To enhance user
824
- experience, primary action can be fired by pressing `Enter` key and the modal
825
- can be closed by pressing the `Escape` key.
826
-
827
- To enable it, you just need to pass a reference to the buttons using
828
- `primaryButtonRef` and `closeButtonRef` props on Modal. The advantage of passing
829
- the reference to the button is that if the button is disabled, the key press
830
- will not fire the event.
831
-
832
- ## Autofocus
833
-
834
- Autofocus is implemented to enhance the user experience by automatically
835
- focusing an element within the Modal.
836
-
837
- How does it work? It tries to find `input`, `textarea`, and `select` elements
838
- inside of Modal and moves focus onto the first non-disabled one. If none is
839
- found and the `primaryButtonRef` prop on Modal is set, then the primary button
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.
842
-
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>
970
-
971
- ## Scrolling Long Content
972
-
973
- When modals become too long for the user's viewport or device, they scroll
974
- independent of the page itself. This can be done in three ways using the
975
- `scrolling` option of the ModalBody component:
976
-
977
- - `auto` (default) — ModalBody is responsible for scrolling,
978
- - `custom` — you must provide a custom component to handle scrolling,
979
- typically an instance of [ScrollView](/components/scroll-view) wrapping
980
- ModalContent,
981
- - `none` — entire Modal is responsible for scrolling.
982
-
983
- <Playground>
984
- {() => {
985
- const [modalOpen, setModalOpen] = React.useState(false);
986
- const [modalScrolling, setModalScrolling] = React.useState('auto');
987
- const modalCloseButtonRef = React.useRef();
988
- const modalPrimaryButtonRef = React.useRef();
989
- const modalContent = (
990
- <ModalContent>
991
- <p>
992
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
993
- commodo ligula eget dolor. Aenean massa.
994
- </p>
995
- <p>
996
- Cum sociis natoque penatibus et magnis dis parturient montes,
997
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
998
- pellentesque eu, pretium quis, sem.
999
- </p>
1000
- <p>
1001
- Nulla consequat massa quis enim. Donec pede justo, fringilla
1002
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
1003
- ut, imperdiet a, venenatis vitae, justo.
1004
- </p>
1005
- <p>
1006
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
1007
- commodo ligula eget dolor. Aenean massa.
1008
- </p>
1009
- <p>
1010
- Cum sociis natoque penatibus et magnis dis parturient montes,
1011
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
1012
- pellentesque eu, pretium quis, sem.
1013
- </p>
1014
- <p>
1015
- Nulla consequat massa quis enim. Donec pede justo, fringilla
1016
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
1017
- ut, imperdiet a, venenatis vitae, justo.
1018
- </p>
1019
- <p>
1020
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
1021
- commodo ligula eget dolor. Aenean massa.
1022
- </p>
1023
- <p>
1024
- Cum sociis natoque penatibus et magnis dis parturient montes,
1025
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
1026
- pellentesque eu, pretium quis, sem.
1027
- </p>
1028
- <p>
1029
- Nulla consequat massa quis enim. Donec pede justo, fringilla
1030
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
1031
- ut, imperdiet a, venenatis vitae, justo.
1032
- </p>
1033
- <p>
1034
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
1035
- commodo ligula eget dolor. Aenean massa.
1036
- </p>
1037
- <p>
1038
- Cum sociis natoque penatibus et magnis dis parturient montes,
1039
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
1040
- pellentesque eu, pretium quis, sem.
1041
- </p>
1042
- <p>
1043
- Nulla consequat massa quis enim. Donec pede justo, fringilla
1044
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
1045
- ut, imperdiet a, venenatis vitae, justo.
1046
- </p>
1047
- <p>
1048
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
1049
- commodo ligula eget dolor. Aenean massa.
1050
- </p>
1051
- <p>
1052
- Cum sociis natoque penatibus et magnis dis parturient montes,
1053
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
1054
- pellentesque eu, pretium quis, sem.
1055
- </p>
1056
- <p>
1057
- Nulla consequat massa quis enim. Donec pede justo, fringilla
1058
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
1059
- ut, imperdiet a, venenatis vitae, justo.
1060
- </p>
1061
- </ModalContent>
1062
- )
1063
- return (
1064
- <>
1065
- <Button
1066
- label="Launch modal with scrolling body"
1067
- onClick={() => {
1068
- setModalScrolling('auto');
1069
- setModalOpen(true);
1070
- }}
1071
- />
1072
- <Button
1073
- label="Launch modal with ScrollView"
1074
- onClick={() => {
1075
- setModalScrolling('custom');
1076
- setModalOpen(true);
1077
- }}
1078
- />
1079
- <Button
1080
- label="Launch modal with non-scrolling body"
1081
- onClick={() => {
1082
- setModalScrolling('none');
1083
- setModalOpen(true);
1084
- }}
1085
- />
1086
- <div>
1087
- {modalOpen && (
1088
- <Modal
1089
- autoFocus={modalScrolling !== 'none'}
1090
- closeButtonRef={modalCloseButtonRef}
1091
- primaryButtonRef={modalPrimaryButtonRef}
1092
- size="small"
1093
- >
1094
- <ModalHeader>
1095
- <ModalTitle>Modal with long content</ModalTitle>
1096
- <ModalCloseButton onClick={() => setModalOpen(false)} />
1097
- </ModalHeader>
1098
- <ModalBody scrolling={modalScrolling}>
1099
- {
1100
- modalScrolling === 'custom'
1101
- ? (
1102
- <ScrollView>
1103
- {modalContent}
1104
- </ScrollView>
1105
- )
1106
- : modalContent
1107
- }
1108
- </ModalBody>
1109
- <ModalFooter>
1110
- <Button
1111
- label="OK"
1112
- onClick={() => setModalOpen(false)}
1113
- ref={modalPrimaryButtonRef}
1114
- />
1115
- <Button
1116
- color="secondary"
1117
- label="Close"
1118
- onClick={() => setModalOpen(false)}
1119
- priority="outline"
1120
- ref={modalCloseButtonRef}
1121
- />
1122
- </ModalFooter>
1123
- </Modal>
1124
- )}
1125
- </div>
1126
- </>
1127
- );
1128
- }}
1129
- </Playground>
1130
-
1131
- ### Long Content and Autofocus
1132
-
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>
1281
-
1282
- <!-- markdownlint-disable MD024 -->
1283
-
1284
- ## Forwarding HTML Attributes
1285
-
1286
- In addition to the options below in the [component's API](#api) section, you
1287
- can specify [React synthetic events] or **any HTML attribute you like.** All
1288
- attributes that don't interfere with the API are forwarded to the:
1289
-
1290
- - `<div>` HTML element in case of the `Modal` component. This `<div>` is not the
1291
- root, but its first child which represents the modal window.
1292
- - root `<div>` HTML element in case of `ModalHeader`, `ModalBody`, `ModalContent`
1293
- and `ModalFooter` components.
1294
- - heading HTML element, which level can be specified through `level` option, in
1295
- case of the `ModalTitle` component.
1296
- - native HTML `<button>` in case of the `ModalCloseButton` component.
1297
-
1298
- This enables making the component interactive and helps to improve its
1299
- accessibility.
1300
-
1301
- 👉 Refer to the MDN reference for the full list of supported attributes of the
1302
- [div], [heading] and [button] element.
1303
-
1304
- ## API
1305
-
1306
- <Props table of={Modal} />
1307
-
1308
- ### ModalHeader
1309
-
1310
- <Props table of={ModalHeader} />
1311
-
1312
- ### ModalTitle
1313
-
1314
- <Props table of={ModalTitle} />
1315
-
1316
- ### ModalCloseButton
1317
-
1318
- <Props table of={ModalCloseButton} />
1319
-
1320
- ### ModalBody
1321
-
1322
- <Props table of={ModalBody} />
1323
-
1324
- ### ModalContent
1325
-
1326
- <Props table of={ModalContent} />
1327
-
1328
- ### ModalFooter
1329
-
1330
- <Props table of={ModalFooter} />
1331
-
1332
- ## Theming
1333
-
1334
- | Custom Property | Description |
1335
- |------------------------------------------------------|---------------------------------------------------------------|
1336
- | `--rui-Modal__padding-x` | Inline padding of individual modal components |
1337
- | `--rui-Modal__padding-y` | Block padding of individual modal components |
1338
- | `--rui-Modal__background` | Modal background (including `url()` or gradient) |
1339
- | `--rui-Modal__box-shadow` | Modal box shadow |
1340
- | `--rui-Modal__separator__width` | Width of separator between modal header, body, and footer |
1341
- | `--rui-Modal__separator__color` | Color of separator between modal header, body, and footer |
1342
- | `--rui-Modal__outer-spacing-xs` | Spacing around modal, `xs` screen size |
1343
- | `--rui-Modal__outer-spacing-sm` | Spacing around modal, `sm` screen size and bigger |
1344
- | `--rui-Modal__header__gap` | Modal header gap between children |
1345
- | `--rui-Modal__footer__background` | Modal footer background (including `url()` or gradient) |
1346
- | `--rui-Modal__footer__gap` | Modal footer gap between children |
1347
- | `--rui-Modal__backdrop__background` | Modal backdrop background (including `url()` or gradient) |
1348
- | `--rui-Modal--auto__min-width` | Min width of auto-sized modal (when enough screen estate) |
1349
- | `--rui-Modal--auto__max-width` | Max width of auto-sized modal (when enough screen estate) |
1350
- | `--rui-Modal--small__width` | Width of small modal |
1351
- | `--rui-Modal--medium__width` | Width of medium modal |
1352
- | `--rui-Modal--large__width` | Width of large modal |
1353
- | `--rui-Modal--fullscreen__width` | Width of fullscreen modal |
1354
- | `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
1355
-
1356
- [nng-modal]: https://www.nngroup.com/articles/modal-nonmodal-dialog/
1357
- [React synthetic events]: https://reactjs.org/docs/events.html
1358
- [div]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#attributes
1359
- [heading]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#attributes
1360
- [button]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes