@react-ui-org/react-ui 0.47.0 → 0.49.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 (142) hide show
  1. package/dist/lib.development.js +465 -93
  2. package/dist/lib.js +1 -1
  3. package/package.json +1 -1
  4. package/src/lib/components/Alert/Alert.jsx +3 -0
  5. package/src/lib/components/Alert/Alert.scss +10 -10
  6. package/src/lib/components/Alert/README.mdx +18 -2
  7. package/src/lib/components/Alert/index.js +1 -1
  8. package/src/lib/components/Badge/Badge.jsx +4 -8
  9. package/src/lib/components/Badge/Badge.scss +21 -21
  10. package/src/lib/components/Badge/README.mdx +15 -1
  11. package/src/lib/components/Badge/index.js +1 -1
  12. package/src/lib/components/Button/Button.jsx +23 -34
  13. package/src/lib/components/Button/README.mdx +21 -7
  14. package/src/lib/components/Button/_base.scss +20 -20
  15. package/src/lib/components/Button/_priorities.scss +35 -35
  16. package/src/lib/components/Button/helpers/getRootLabelVisibilityClassName.js +7 -7
  17. package/src/lib/components/Button/helpers/getRootPriorityClassName.js +3 -3
  18. package/src/lib/components/Button/index.js +1 -1
  19. package/src/lib/components/ButtonGroup/ButtonGroup.jsx +2 -8
  20. package/src/lib/components/ButtonGroup/README.mdx +18 -2
  21. package/src/lib/components/Card/Card.jsx +6 -10
  22. package/src/lib/components/Card/Card.scss +13 -13
  23. package/src/lib/components/Card/CardBody.jsx +6 -10
  24. package/src/lib/components/Card/CardFooter.jsx +6 -7
  25. package/src/lib/components/Card/README.mdx +21 -5
  26. package/src/lib/components/CheckboxField/CheckboxField.jsx +17 -44
  27. package/src/lib/components/CheckboxField/README.mdx +18 -6
  28. package/src/lib/components/CheckboxField/index.js +1 -1
  29. package/src/lib/components/FileInputField/FileInputField.jsx +20 -29
  30. package/src/lib/components/FileInputField/FileInputField.scss +3 -3
  31. package/src/lib/components/FileInputField/README.mdx +30 -28
  32. package/src/lib/components/FileInputField/index.js +1 -1
  33. package/src/lib/components/FormLayout/FormLayout.jsx +5 -9
  34. package/src/lib/components/FormLayout/FormLayout.scss +3 -3
  35. package/src/lib/components/FormLayout/FormLayoutCustomField.jsx +4 -1
  36. package/src/lib/components/FormLayout/FormLayoutCustomField.scss +8 -8
  37. package/src/lib/components/FormLayout/README.mdx +28 -13
  38. package/src/lib/components/Grid/Grid.jsx +31 -35
  39. package/src/lib/components/Grid/Grid.scss +10 -15
  40. package/src/lib/components/Grid/GridSpan.jsx +5 -11
  41. package/src/lib/components/Grid/README.mdx +48 -36
  42. package/src/lib/components/Grid/_helpers/generateResponsiveCustomProperties.js +11 -3
  43. package/src/lib/components/Grid/_settings.scss +18 -0
  44. package/src/lib/components/Grid/_tools.scss +5 -5
  45. package/src/lib/components/Modal/Modal.jsx +147 -254
  46. package/src/lib/components/Modal/Modal.scss +7 -55
  47. package/src/lib/components/Modal/ModalBody.jsx +60 -0
  48. package/src/lib/components/Modal/ModalBody.scss +18 -0
  49. package/src/lib/components/Modal/ModalCloseButton.jsx +45 -0
  50. package/src/lib/components/Modal/ModalCloseButton.scss +18 -0
  51. package/src/lib/components/Modal/ModalContent.jsx +39 -0
  52. package/src/lib/components/Modal/ModalContent.scss +5 -0
  53. package/src/lib/components/Modal/ModalFooter.jsx +42 -0
  54. package/src/lib/components/Modal/ModalFooter.scss +35 -0
  55. package/src/lib/components/Modal/ModalHeader.jsx +44 -0
  56. package/src/lib/components/Modal/ModalHeader.scss +30 -0
  57. package/src/lib/components/Modal/ModalTitle.jsx +44 -0
  58. package/src/lib/components/Modal/ModalTitle.scss +10 -0
  59. package/src/lib/components/Modal/README.mdx +865 -195
  60. package/src/lib/components/Modal/_helpers/getJustifyClassName.js +19 -0
  61. package/src/lib/components/Modal/_helpers/getScrollingClassName.js +11 -0
  62. package/src/lib/components/Modal/_settings.scss +1 -5
  63. package/src/lib/components/Modal/_theme.scss +6 -0
  64. package/src/lib/components/Modal/index.js +7 -1
  65. package/src/lib/components/Paper/Paper.jsx +5 -9
  66. package/src/lib/components/Paper/Paper.scss +2 -2
  67. package/src/lib/components/Paper/README.mdx +15 -1
  68. package/src/lib/components/Paper/index.js +1 -1
  69. package/src/lib/components/Popover/Popover.jsx +14 -30
  70. package/src/lib/components/Popover/Popover.scss +7 -6
  71. package/src/lib/components/Popover/PopoverWrapper.jsx +5 -12
  72. package/src/lib/components/Popover/PopoverWrapper.scss +3 -0
  73. package/src/lib/components/Popover/README.mdx +32 -11
  74. package/src/lib/components/Popover/_theme.scss +1 -1
  75. package/src/lib/components/Radio/README.mdx +13 -6
  76. package/src/lib/components/Radio/Radio.jsx +39 -29
  77. package/src/lib/components/Radio/Radio.scss +3 -3
  78. package/src/lib/components/Radio/index.js +1 -1
  79. package/src/lib/components/ScrollView/README.mdx +165 -84
  80. package/src/lib/components/ScrollView/ScrollView.jsx +115 -117
  81. package/src/lib/components/ScrollView/ScrollView.scss +18 -16
  82. package/src/lib/components/ScrollView/index.js +1 -1
  83. package/src/lib/components/SelectField/README.mdx +83 -7
  84. package/src/lib/components/SelectField/SelectField.jsx +86 -61
  85. package/src/lib/components/SelectField/SelectField.scss +8 -8
  86. package/src/lib/components/SelectField/_components/Option/Option.jsx +46 -0
  87. package/src/lib/components/SelectField/_components/Option/index.js +1 -0
  88. package/src/lib/components/SelectField/index.js +1 -1
  89. package/src/lib/components/Table/README.mdx +25 -9
  90. package/src/lib/components/Table/Table.jsx +43 -101
  91. package/src/lib/components/Table/Table.scss +0 -24
  92. package/src/lib/components/Table/_components/TableBodyCell/TableBodyCell.jsx +46 -0
  93. package/src/lib/components/Table/_components/TableBodyCell/index.js +1 -0
  94. package/src/lib/components/Table/_components/TableCell.scss +25 -0
  95. package/src/lib/components/Table/_components/TableHeaderCell/TableHeaderCell.jsx +71 -0
  96. package/src/lib/components/Table/_components/TableHeaderCell/index.js +1 -0
  97. package/src/lib/components/Table/index.js +1 -1
  98. package/src/lib/components/Tabs/README.mdx +21 -3
  99. package/src/lib/components/Tabs/Tabs.jsx +6 -1
  100. package/src/lib/components/Tabs/TabsItem.jsx +3 -0
  101. package/src/lib/components/Tabs/TabsItem.scss +1 -2
  102. package/src/lib/components/Text/README.mdx +25 -7
  103. package/src/lib/components/Text/Text.jsx +3 -7
  104. package/src/lib/components/Text/Text.scss +6 -6
  105. package/src/lib/components/Text/_helpers/getRootClampClassName.js +2 -2
  106. package/src/lib/components/Text/_helpers/getRootHyphensClassName.js +2 -2
  107. package/src/lib/components/Text/_helpers/getRootWordWrappingClassName.js +2 -2
  108. package/src/lib/components/Text/index.js +1 -1
  109. package/src/lib/components/TextArea/README.mdx +34 -31
  110. package/src/lib/components/TextArea/TextArea.jsx +23 -63
  111. package/src/lib/components/TextArea/TextArea.scss +8 -8
  112. package/src/lib/components/TextArea/index.js +1 -1
  113. package/src/lib/components/TextField/README.mdx +56 -54
  114. package/src/lib/components/TextField/TextField.jsx +25 -52
  115. package/src/lib/components/TextField/TextField.scss +9 -9
  116. package/src/lib/components/TextField/index.js +1 -1
  117. package/src/lib/components/TextLink/README.mdx +13 -6
  118. package/src/lib/components/TextLink/TextLink.jsx +0 -10
  119. package/src/lib/components/TextLink/index.js +1 -1
  120. package/src/lib/components/Toggle/README.mdx +18 -6
  121. package/src/lib/components/Toggle/Toggle.jsx +18 -44
  122. package/src/lib/components/Toggle/index.js +1 -1
  123. package/src/lib/components/Toolbar/README.mdx +21 -6
  124. package/src/lib/components/Toolbar/Toolbar.jsx +9 -43
  125. package/src/lib/components/Toolbar/Toolbar.scss +24 -12
  126. package/src/lib/components/Toolbar/ToolbarGroup.jsx +7 -26
  127. package/src/lib/components/Toolbar/ToolbarItem.jsx +3 -7
  128. package/src/lib/components/Toolbar/_helpers/getAlignClassName.js +19 -0
  129. package/src/lib/components/Toolbar/_helpers/getJustifyClassName.js +16 -0
  130. package/src/lib/components/_helpers/getRootColorClassName.js +10 -10
  131. package/src/lib/components/_helpers/getRootSizeClassName.js +3 -3
  132. package/src/lib/components/_helpers/transferProps.js +1 -1
  133. package/src/lib/index.js +24 -16
  134. package/src/lib/provider/withGlobalProps.jsx +20 -3
  135. package/src/lib/styles/tools/form-fields/_box-field-layout.scss +15 -15
  136. package/src/lib/styles/tools/form-fields/_inline-field-elements.scss +1 -1
  137. package/src/lib/styles/tools/form-fields/_inline-field-layout.scss +9 -9
  138. package/src/lib/theme.scss +18 -26
  139. package/src/lib/translations/en.js +1 -1
  140. package/src/lib/components/Grid/_theme.scss +0 -11
  141. package/src/lib/components/ScrollView/_theme.scss +0 -2
  142. package/src/lib/components/withForwardedRef.jsx +0 -11
@@ -1,294 +1,189 @@
1
1
  import PropTypes from 'prop-types';
2
- import React from 'react';
2
+ import React, {
3
+ useEffect,
4
+ useRef,
5
+ } from 'react';
3
6
  import { createPortal } from 'react-dom';
4
- import {
5
- RUIContext,
6
- withGlobalProps,
7
- } from '../../provider';
8
- import { classNames } from '../../utils/classNames';
7
+ import { withGlobalProps } from '../../provider';
9
8
  import { transferProps } from '../_helpers/transferProps';
10
- import {
11
- Toolbar,
12
- ToolbarItem,
13
- } from '../Toolbar';
14
- import Button from '../Button';
15
- import ScrollView from '../ScrollView';
9
+ import { classNames } from '../../utils/classNames';
16
10
  import styles from './Modal.scss';
17
11
 
18
- export class Modal extends React.Component {
19
- constructor(props) {
20
- super(props);
21
-
22
- this.childrenWrapperRef = React.createRef();
23
- this.submitButtonRef = React.createRef();
24
-
25
- this.keyPressHandler = this.keyPressHandler.bind(this);
26
- }
27
-
28
- componentDidMount() {
29
- const { autoFocus } = this.props;
30
-
31
- window.document.addEventListener('keydown', this.keyPressHandler, false);
32
-
33
- // If `autoFocus` is set to `true`, following code finds first form field element
34
- // (input, textarea or select) or submit button and auto focuses it. This is necessary
35
- // to have focus on one of those elements to be able to submit form by pressing Enter key.
36
- if (autoFocus && this.childrenWrapperRef && this.childrenWrapperRef.current) {
37
- const childrenWrapperElement = this.childrenWrapperRef.current;
38
- const childrenElements = childrenWrapperElement.querySelectorAll('*');
39
- const formFieldEl = Array.from(childrenElements).find(
40
- (element) => ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName),
41
- );
42
-
43
- if (formFieldEl) {
44
- formFieldEl.focus();
45
- return;
46
- }
47
-
48
- if (this.submitButtonRef && this.submitButtonRef.current) {
49
- this.submitButtonRef.current.focus();
50
- }
12
+ const preRender = (
13
+ children,
14
+ childrenWrapperRef,
15
+ closeButtonRef,
16
+ position,
17
+ restProps,
18
+ size,
19
+ ) => {
20
+ const sizeClass = (modalSize) => {
21
+ if (modalSize === 'small') {
22
+ return styles.isRootSizeSmall;
51
23
  }
52
- }
53
-
54
- componentWillUnmount() {
55
- window.document.removeEventListener('keydown', this.keyPressHandler, false);
56
- }
57
-
58
- keyPressHandler(e) {
59
- const {
60
- actions,
61
- onClose,
62
- } = this.props;
63
24
 
64
- if (e.keyCode === 27 && onClose) {
65
- onClose();
25
+ if (modalSize === 'medium') {
26
+ return styles.isRootSizeMedium;
66
27
  }
67
28
 
68
- if (e.keyCode === 13 && e.target.nodeName !== 'BUTTON') {
69
- const submitAction = actions.find((action) => action.type === 'submit');
29
+ if (modalSize === 'large') {
30
+ return styles.isRootSizeLarge;
31
+ }
70
32
 
71
- if (submitAction && !submitAction.disabled) {
72
- submitAction.onClick(e);
73
- }
33
+ if (modalSize === 'fullscreen') {
34
+ return styles.isRootSizeFullscreen;
74
35
  }
75
- }
76
36
 
77
- preRender() {
78
- const {
79
- actions,
80
- children,
81
- id,
82
- onClose,
83
- position,
84
- scrollView,
85
- size,
86
- title,
87
- } = this.props;
37
+ return styles.isRootSizeAuto;
38
+ };
88
39
 
89
- const sizeClass = (modalSize) => {
90
- if (modalSize === 'small') {
91
- return styles.isRootSizeSmall;
92
- }
40
+ const positionClass = (modalPosition) => {
41
+ if (modalPosition === 'top') {
42
+ return styles.isRootPositionTop;
43
+ }
93
44
 
94
- if (modalSize === 'medium') {
95
- return styles.isRootSizeMedium;
96
- }
45
+ return styles.isRootPositionCenter;
46
+ };
47
+
48
+ return (
49
+ <div
50
+ className={styles.backdrop}
51
+ onClick={() => {
52
+ if (closeButtonRef?.current != null) {
53
+ closeButtonRef.current.click();
54
+ }
55
+ }}
56
+ role="presentation"
57
+ >
58
+ <div
59
+ {...transferProps(restProps)}
60
+ className={classNames(
61
+ styles.root,
62
+ sizeClass(size),
63
+ positionClass(position),
64
+ )}
65
+ onClick={(e) => {
66
+ e.stopPropagation();
67
+ }}
68
+ role="presentation"
69
+ ref={childrenWrapperRef}
70
+ >
71
+ {children}
72
+ </div>
73
+ </div>
74
+ );
75
+ };
97
76
 
98
- if (modalSize === 'large') {
99
- return styles.isRootSizeLarge;
100
- }
77
+ export const Modal = ({
78
+ autoFocus,
79
+ children,
80
+ closeButtonRef,
81
+ portalId,
82
+ position,
83
+ primaryButtonRef,
84
+ size,
85
+ ...restProps
86
+ }) => {
87
+ const childrenWrapperRef = useRef();
88
+
89
+ const keyPressHandler = (e) => {
90
+ if (e.key === 'Escape' && closeButtonRef?.current != null) {
91
+ closeButtonRef.current.click();
92
+ }
101
93
 
102
- if (modalSize === 'fullscreen') {
103
- return styles.isRootSizeFullscreen;
104
- }
94
+ if (e.key === 'Enter' && e.target.nodeName !== 'BUTTON' && primaryButtonRef?.current != null) {
95
+ primaryButtonRef.current.click();
96
+ }
97
+ };
105
98
 
106
- return styles.isRootSizeAuto;
99
+ useEffect(() => {
100
+ window.document.addEventListener('keydown', keyPressHandler, false);
101
+ const removeKeyPressHandler = () => {
102
+ window.document.removeEventListener('keydown', keyPressHandler, false);
107
103
  };
108
104
 
109
- const positionClass = (modalPosition) => {
110
- if (modalPosition === 'top') {
111
- return styles.isRootPositionTop;
112
- }
113
-
114
- return styles.isRootPositionCenter;
115
- };
105
+ // If `autoFocus` is set to `true`, following code finds first form field element
106
+ // (input, textarea or select) or primary button and auto focuses it. This is necessary
107
+ // to have focus on one of those elements to be able to submit form by pressing Enter key.
108
+ if (autoFocus) {
109
+ if (childrenWrapperRef?.current != null) {
110
+ const childrenWrapperElement = childrenWrapperRef.current;
111
+ const childrenElements = childrenWrapperElement.querySelectorAll('*');
112
+ const formFieldEl = Array.from(childrenElements).find(
113
+ (element) => ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName),
114
+ );
116
115
 
117
- const modalBody = () => {
118
- const content = (
119
- <div
120
- ref={this.childrenWrapperRef}
121
- className={styles.content}
122
- {...(id && { id: `${id}__content` })}
123
- >
124
- {children}
125
- </div>
126
- );
116
+ if (formFieldEl) {
117
+ formFieldEl.focus();
118
+ return removeKeyPressHandler;
119
+ }
120
+ }
127
121
 
128
- if (scrollView) {
129
- return (
130
- <div
131
- className={classNames(
132
- styles.body,
133
- styles.isBodyScrollable,
134
- )}
135
- >
136
- {React.cloneElement(scrollView, scrollView.props, content)}
137
- </div>
138
- );
122
+ if (primaryButtonRef?.current != null) {
123
+ primaryButtonRef.current.focus();
139
124
  }
125
+ }
140
126
 
141
- return (
142
- <div className={styles.body}>
143
- {content}
144
- </div>
145
- );
146
- };
127
+ return removeKeyPressHandler;
128
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
147
129
 
148
- return (
149
- <div
150
- className={styles.backdrop}
151
- id={id}
152
- onClick={(e) => {
153
- if (onClose) {
154
- onClose(e);
155
- }
156
- }}
157
- role="presentation"
158
- >
159
- <div
160
- className={classNames(
161
- styles.root,
162
- sizeClass(size),
163
- positionClass(position),
164
- )}
165
- onClick={(e) => {
166
- e.stopPropagation();
167
- }}
168
- role="presentation"
169
- >
170
- <div className={styles.head}>
171
- <h3
172
- className={styles.headTitle}
173
- {...(id && { id: `${id}__title` })}
174
- >
175
- {title}
176
- </h3>
177
- {onClose && (
178
- <RUIContext.Consumer>
179
- {({ translations }) => (
180
- <button
181
- type="button"
182
- className={styles.close}
183
- onClick={onClose}
184
- title={translations.Modal.close}
185
- {...(id && { id: `${id}__closeModalHeaderButton` })}
186
- >
187
- ×
188
- </button>
189
- )}
190
- </RUIContext.Consumer>
191
- )}
192
- </div>
193
- {modalBody()}
194
- {(actions.length || onClose) && (
195
- <div className={styles.footer}>
196
- <Toolbar justify="center" dense>
197
- {actions.map((action) => (
198
- <ToolbarItem key={action.label}>
199
- <Button
200
- {...transferProps(action)}
201
- color={action.color}
202
- disabled={action.disabled}
203
- feedbackIcon={action.feedbackIcon}
204
- forwardedRef={this.submitButtonRef}
205
- id={action.id || undefined}
206
- label={action.label}
207
- onClick={action.onClick}
208
- type="button"
209
- />
210
- </ToolbarItem>
211
- ))}
212
- {onClose && (
213
- <ToolbarItem>
214
- <RUIContext.Consumer>
215
- {({ translations }) => (
216
- <Button
217
- label={translations.Modal.close}
218
- onClick={onClose}
219
- priority="flat"
220
- {...(id && { id: `${id}__closeModalFooterButton` })}
221
- />
222
- )}
223
- </RUIContext.Consumer>
224
- </ToolbarItem>
225
- )}
226
- </Toolbar>
227
- </div>
228
- )}
229
- </div>
230
- </div>
130
+ if (portalId === null) {
131
+ return preRender(
132
+ children,
133
+ childrenWrapperRef,
134
+ closeButtonRef,
135
+ position,
136
+ restProps,
137
+ size,
231
138
  );
232
139
  }
233
140
 
234
- render() {
235
- const { portalId } = this.props;
236
-
237
- if (portalId === null) {
238
- return this.preRender();
239
- }
240
-
241
- return createPortal(this.preRender(), document.getElementById(portalId));
242
- }
243
- }
141
+ return createPortal(
142
+ preRender(
143
+ children,
144
+ childrenWrapperRef,
145
+ closeButtonRef,
146
+ position,
147
+ restProps,
148
+ size,
149
+ ),
150
+ document.getElementById(portalId),
151
+ );
152
+ };
244
153
 
245
154
  Modal.defaultProps = {
246
- actions: [],
247
155
  autoFocus: true,
248
156
  children: null,
249
- id: undefined,
250
- onClose: null,
157
+ closeButtonRef: null,
251
158
  portalId: null,
252
159
  position: 'center',
253
- scrollView: (<ScrollView
254
- customEndShadowStyle={{ background: 'radial-gradient(farthest-side at center bottom, rgba(0, 0, 0, 0.16) 0%, rgba(0, 0, 0, 0.06) 40%, rgba(0, 0, 0, 0.02) 85%, rgba(0, 0, 0, 0) 100%)' }}
255
- customStartShadowStyle={{ background: 'radial-gradient(farthest-side at center top, rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.05) 60%, rgba(0, 0, 0, 0.02) 85%, rgba(0, 0, 0, 0) 100%)' }}
256
- shadowSize="16px"
257
- />),
160
+ primaryButtonRef: null,
258
161
  size: 'medium',
259
162
  };
260
163
 
261
164
  Modal.propTypes = {
262
165
  /**
263
- * Actions to be rendered in modal footer.
264
- */
265
- actions: PropTypes.arrayOf(PropTypes.shape({
266
- color: PropTypes.oneOf(['primary', 'secondary', 'success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark']),
267
- disabled: PropTypes.bool,
268
- feedbackIcon: PropTypes.node,
269
- id: PropTypes.string,
270
- label: PropTypes.string.isRequired,
271
- onClick: PropTypes.func.isRequired,
272
- })),
273
- /**
274
- * If `true`, focus the first action in the footer when the modal is opened.
166
+ * If `true`, focus the first input element in the modal or primary button (referenced by the `primaryButtonRef` prop)
167
+ * when the modal is opened.
275
168
  */
276
169
  autoFocus: PropTypes.bool,
277
170
  /**
278
- * Content of the modal.
171
+ * Nested elements. Supported types are:
172
+ *
173
+ * * `ModalHeader`
174
+ * * `ModalBody`
175
+ * * `ModalFooter`
176
+ *
177
+ * At least `ModalBody` is required.
279
178
  */
280
179
  children: PropTypes.node,
281
180
  /**
282
- * ID of the root HTML element. It also serves as a base for nested elements:
283
- * * `<ID>__content`
284
- * * `<ID>__closeModalHeaderButton`
285
- * * `<ID>__closeModalFooterButton`
181
+ * Reference to close button element. It is used to close modal when Escape key is pressed or the backdrop is clicked.
286
182
  */
287
- id: PropTypes.string,
288
- /**
289
- * If a function is provided, the close buttons will be displayed.
290
- */
291
- onClose: PropTypes.func,
183
+ closeButtonRef: PropTypes.shape({
184
+ // eslint-disable-next-line react/forbid-prop-types
185
+ current: PropTypes.any,
186
+ }),
292
187
  /**
293
188
  * If set, modal is rendered in the React Portal with that ID.
294
189
  */
@@ -298,19 +193,17 @@ Modal.propTypes = {
298
193
  */
299
194
  position: PropTypes.oneOf(['top', 'center']),
300
195
  /**
301
- * The `ScrollView` component to be used as a wrapper of the content to provide body scrolling functionality.
302
- * If provided only the modal body will be scrollable.
303
- * If set to `null` the entire modal including header and footer will be scrollable.
196
+ * Reference to primary button element. It is used to submit modal when Enter key is pressed and as fallback
197
+ * when `autoFocus` functionality does not find any input element to be focused.
304
198
  */
305
- scrollView: PropTypes.element,
199
+ primaryButtonRef: PropTypes.shape({
200
+ // eslint-disable-next-line react/forbid-prop-types
201
+ current: PropTypes.any,
202
+ }),
306
203
  /**
307
204
  * Size of the modal.
308
205
  */
309
206
  size: PropTypes.oneOf(['small', 'medium', 'large', 'fullscreen', 'auto']),
310
- /**
311
- * Title displayed in modal header.
312
- */
313
- title: PropTypes.string.isRequired,
314
207
  };
315
208
 
316
209
  export const ModalWithGlobalProps = withGlobalProps(Modal, 'Modal');
@@ -9,15 +9,18 @@
9
9
 
10
10
  .root {
11
11
  --rui-local-outer-spacing: #{theme.$outer-spacing-xs};
12
+ --rui-local-max-width: calc(100% - (2 * var(--rui-local-outer-spacing)));
13
+ --rui-local-max-height: calc(100% - (2 * var(--rui-local-outer-spacing)));
12
14
 
13
15
  position: fixed;
14
16
  left: 50%;
15
17
  z-index: settings.$z-index;
16
18
  display: flex;
17
19
  flex-direction: column;
18
- max-width: calc(100% - (2 * var(--rui-local-outer-spacing)));
19
- max-height: calc(100% - (2 * var(--rui-local-outer-spacing)));
20
+ max-width: var(--rui-local-max-width);
21
+ max-height: var(--rui-local-max-height);
20
22
  overflow-y: auto;
23
+ overscroll-behavior: contain;
21
24
  border-radius: settings.$border-radius;
22
25
  background: theme.$background;
23
26
  box-shadow: theme.$box-shadow;
@@ -28,57 +31,6 @@
28
31
  }
29
32
  }
30
33
 
31
- .head {
32
- display: flex;
33
- flex: none;
34
- align-items: baseline;
35
- justify-content: space-between;
36
- padding: settings.$padding-y spacing.of(4) settings.$padding-y settings.$padding-x;
37
- }
38
-
39
- .headTitle {
40
- font-size: settings.$head-title-font-size;
41
-
42
- &:not(:last-child) {
43
- margin-bottom: 0;
44
- }
45
- }
46
-
47
- .close {
48
- @include reset.button();
49
- @include accessibility.min-tap-target();
50
-
51
- font-size: map.get(typography.$size-values, 3);
52
- line-height: 1;
53
- color: inherit;
54
- }
55
-
56
- .body {
57
- flex: 1 1 auto;
58
- }
59
-
60
- .isBodyScrollable {
61
- display: flex;
62
- flex-direction: column;
63
- min-height: 0;
64
- }
65
-
66
- .content {
67
- padding: settings.$padding-y settings.$padding-x settings.$content-padding-bottom;
68
- }
69
-
70
- .footer {
71
- display: flex;
72
- flex: none;
73
- flex-wrap: wrap;
74
- align-items: center;
75
- justify-content: center;
76
- padding: settings.$padding-y settings.$padding-x;
77
- border-bottom-right-radius: settings.$border-radius;
78
- border-bottom-left-radius: settings.$border-radius;
79
- background: theme.$footer-background;
80
- }
81
-
82
34
  .backdrop {
83
35
  position: fixed;
84
36
  top: 0;
@@ -112,8 +64,8 @@
112
64
 
113
65
  .isRootSizeAuto {
114
66
  width: auto;
115
- min-width: map.get(theme.$sizes, auto, min-width);
116
- max-width: map.get(theme.$sizes, auto, max-width);
67
+ min-width: min(var(--rui-local-max-width), #{map.get(theme.$sizes, auto, min-width)});
68
+ max-width: min(var(--rui-local-max-width), #{map.get(theme.$sizes, auto, max-width)});
117
69
  }
118
70
 
119
71
  .isRootPositionCenter {
@@ -0,0 +1,60 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import { withGlobalProps } from '../../provider';
4
+ import { transferProps } from '../_helpers/transferProps';
5
+ import { classNames } from '../../utils/classNames';
6
+ import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
7
+ import { getScrollingClassName } from './_helpers/getScrollingClassName';
8
+ import styles from './ModalBody.scss';
9
+
10
+ export const ModalBody = ({
11
+ children,
12
+ scrolling,
13
+ ...restProps
14
+ }) => {
15
+ if (isChildrenEmpty(children)) {
16
+ return null;
17
+ }
18
+
19
+ return (
20
+ <div
21
+ {...transferProps(restProps)}
22
+ className={classNames(
23
+ styles.root,
24
+ getScrollingClassName(scrolling, styles),
25
+ )}
26
+ >
27
+ {children}
28
+ </div>
29
+ );
30
+ };
31
+
32
+ ModalBody.defaultProps = {
33
+ children: null,
34
+ scrolling: 'auto',
35
+ };
36
+
37
+ ModalBody.propTypes = {
38
+ /**
39
+ * Nested elements. Supported types are:
40
+ *
41
+ * * `ModalContent`
42
+ * * `ScrollView` (`scrolling: 'custom'` must be set)
43
+ *
44
+ * You can also provide a custom component responsible for scrolling and displaying content correctly.
45
+ * At most one nested element is allowed. If none are provided nothing is rendered.
46
+ */
47
+ children: PropTypes.node,
48
+ /**
49
+ * Scrolling mode:
50
+ *
51
+ * - `auto`: scrolling is enabled on ModalBody.
52
+ * - `custom`: use if providing a custom scrolling component, e.g. an instance of `ScrollView`.
53
+ * - `none`: scrolling is disabled on ModalBody and the entire Modal is scrollable instead.
54
+ */
55
+ scrolling: PropTypes.oneOf(['auto', 'custom', 'none']),
56
+ };
57
+
58
+ export const ModalBodyWithGlobalProps = withGlobalProps(ModalBody, 'ModalBody');
59
+
60
+ export default ModalBodyWithGlobalProps;
@@ -0,0 +1,18 @@
1
+ .root {
2
+ flex: 1 1 auto;
3
+ }
4
+
5
+ .isRootScrollingAuto,
6
+ .isRootScrollingCustom {
7
+ min-height: 0;
8
+ }
9
+
10
+ .isRootScrollingAuto {
11
+ overflow-y: auto;
12
+ overscroll-behavior: contain;
13
+ }
14
+
15
+ .isRootScrollingCustom {
16
+ display: flex;
17
+ flex-direction: column;
18
+ }
@@ -0,0 +1,45 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, { useContext } from 'react';
3
+ import {
4
+ RUIContext,
5
+ withGlobalProps,
6
+ } from '../../provider';
7
+ import { transferProps } from '../_helpers/transferProps';
8
+ import styles from './ModalCloseButton.scss';
9
+
10
+ export const ModalCloseButton = React.forwardRef((props, ref) => {
11
+ const {
12
+ disabled,
13
+ ...restProps
14
+ } = props;
15
+
16
+ const { translations } = useContext(RUIContext);
17
+
18
+ return (
19
+ <button
20
+ {...transferProps(restProps)}
21
+ type="button"
22
+ className={styles.root}
23
+ disabled={disabled}
24
+ ref={ref}
25
+ title={translations.ModalCloseButton.close}
26
+ >
27
+ ×
28
+ </button>
29
+ );
30
+ });
31
+
32
+ ModalCloseButton.defaultProps = {
33
+ disabled: false,
34
+ };
35
+
36
+ ModalCloseButton.propTypes = {
37
+ /**
38
+ * If `true`, close button will be disabled.
39
+ */
40
+ disabled: PropTypes.bool,
41
+ };
42
+
43
+ export const ModalCloseButtonWithGlobalProps = withGlobalProps(ModalCloseButton, 'ModalCloseButton');
44
+
45
+ export default ModalCloseButtonWithGlobalProps;
@@ -0,0 +1,18 @@
1
+ @use "sass:map";
2
+ @use "../../styles/theme/typography";
3
+ @use "../../styles/tools/accessibility";
4
+ @use "../../styles/tools/reset";
5
+ @use "../../styles/tools/spacing";
6
+
7
+ .root {
8
+ @include reset.button();
9
+ @include accessibility.min-tap-target();
10
+
11
+ font-size: map.get(typography.$size-values, 3);
12
+ line-height: 1;
13
+ color: inherit;
14
+
15
+ &:disabled {
16
+ cursor: var(--rui-disabled-cursor);
17
+ }
18
+ }