@react-ui-org/react-ui 0.47.0 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }