@react-ui-org/react-ui 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib.development.js +330 -54
- package/dist/lib.js +1 -1
- package/package.json +1 -1
- package/src/lib/components/Alert/README.mdx +4 -2
- package/src/lib/components/Alert/index.js +1 -1
- package/src/lib/components/Badge/README.mdx +1 -1
- package/src/lib/components/Badge/index.js +1 -1
- package/src/lib/components/Button/Button.jsx +31 -31
- package/src/lib/components/Button/README.mdx +4 -2
- package/src/lib/components/Button/index.js +1 -1
- package/src/lib/components/ButtonGroup/ButtonGroup.jsx +2 -1
- package/src/lib/components/ButtonGroup/README.mdx +4 -2
- package/src/lib/components/Card/README.mdx +7 -5
- package/src/lib/components/CheckboxField/CheckboxField.jsx +27 -28
- package/src/lib/components/CheckboxField/README.mdx +1 -1
- package/src/lib/components/CheckboxField/index.js +1 -1
- package/src/lib/components/FileInputField/FileInputField.jsx +27 -27
- package/src/lib/components/FileInputField/README.mdx +1 -1
- package/src/lib/components/FileInputField/index.js +1 -1
- package/src/lib/components/FormLayout/README.mdx +15 -13
- package/src/lib/components/Grid/Grid.jsx +31 -28
- package/src/lib/components/Grid/Grid.scss +10 -15
- package/src/lib/components/Grid/GridSpan.jsx +5 -4
- package/src/lib/components/Grid/README.mdx +34 -36
- package/src/lib/components/Grid/_helpers/generateResponsiveCustomProperties.js +11 -3
- package/src/lib/components/Grid/_settings.scss +18 -0
- package/src/lib/components/Grid/_tools.scss +5 -5
- package/src/lib/components/Modal/Modal.jsx +147 -250
- package/src/lib/components/Modal/Modal.scss +7 -55
- package/src/lib/components/Modal/ModalBody.jsx +64 -0
- package/src/lib/components/Modal/ModalBody.scss +18 -0
- package/src/lib/components/Modal/ModalCloseButton.jsx +61 -0
- package/src/lib/components/Modal/ModalCloseButton.scss +18 -0
- package/src/lib/components/Modal/ModalContent.jsx +43 -0
- package/src/lib/components/Modal/ModalContent.scss +5 -0
- package/src/lib/components/Modal/ModalFooter.jsx +46 -0
- package/src/lib/components/Modal/ModalFooter.scss +35 -0
- package/src/lib/components/Modal/ModalHeader.jsx +48 -0
- package/src/lib/components/Modal/ModalHeader.scss +30 -0
- package/src/lib/components/Modal/ModalTitle.jsx +45 -0
- package/src/lib/components/Modal/ModalTitle.scss +10 -0
- package/src/lib/components/Modal/README.mdx +842 -197
- package/src/lib/components/Modal/_helpers/getJustifyClassName.js +19 -0
- package/src/lib/components/Modal/_helpers/getScrollingClassName.js +11 -0
- package/src/lib/components/Modal/_settings.scss +1 -5
- package/src/lib/components/Modal/_theme.scss +6 -0
- package/src/lib/components/Modal/index.js +7 -1
- package/src/lib/components/Paper/README.mdx +1 -1
- package/src/lib/components/Paper/index.js +1 -1
- package/src/lib/components/Popover/Popover.jsx +24 -24
- package/src/lib/components/Popover/Popover.scss +7 -6
- package/src/lib/components/Popover/PopoverWrapper.jsx +5 -5
- package/src/lib/components/Popover/PopoverWrapper.scss +3 -0
- package/src/lib/components/Popover/README.mdx +13 -11
- package/src/lib/components/Popover/_theme.scss +1 -1
- package/src/lib/components/Radio/README.mdx +1 -1
- package/src/lib/components/Radio/Radio.jsx +37 -27
- package/src/lib/components/Radio/index.js +1 -1
- package/src/lib/components/ScrollView/README.mdx +146 -84
- package/src/lib/components/ScrollView/ScrollView.jsx +104 -113
- package/src/lib/components/ScrollView/ScrollView.scss +18 -16
- package/src/lib/components/ScrollView/index.js +1 -1
- package/src/lib/components/SelectField/README.mdx +66 -2
- package/src/lib/components/SelectField/SelectField.jsx +93 -49
- package/src/lib/components/SelectField/_components/Option/Option.jsx +46 -0
- package/src/lib/components/SelectField/_components/Option/index.js +1 -0
- package/src/lib/components/SelectField/index.js +1 -1
- package/src/lib/components/Table/README.mdx +4 -2
- package/src/lib/components/Table/Table.jsx +1 -1
- package/src/lib/components/Table/index.js +1 -1
- package/src/lib/components/Tabs/README.mdx +5 -3
- package/src/lib/components/Tabs/TabsItem.scss +1 -2
- package/src/lib/components/Text/README.mdx +9 -7
- package/src/lib/components/Text/index.js +1 -1
- package/src/lib/components/TextArea/README.mdx +1 -1
- package/src/lib/components/TextArea/TextArea.jsx +33 -33
- package/src/lib/components/TextArea/index.js +1 -1
- package/src/lib/components/TextField/README.mdx +3 -3
- package/src/lib/components/TextField/TextField.jsx +33 -34
- package/src/lib/components/TextField/index.js +1 -1
- package/src/lib/components/TextLink/README.mdx +1 -1
- package/src/lib/components/TextLink/index.js +1 -1
- package/src/lib/components/Toggle/README.mdx +1 -1
- package/src/lib/components/Toggle/Toggle.jsx +28 -28
- package/src/lib/components/Toggle/index.js +1 -1
- package/src/lib/components/Toolbar/README.mdx +8 -6
- package/src/lib/components/_helpers/transferProps.js +1 -1
- package/src/lib/index.js +24 -16
- package/src/lib/provider/withGlobalProps.jsx +20 -3
- package/src/lib/theme.scss +18 -26
- package/src/lib/translations/en.js +1 -1
- package/src/lib/components/Grid/_theme.scss +0 -11
- package/src/lib/components/ScrollView/_theme.scss +0 -2
- package/src/lib/components/withForwardedRef.jsx +0 -11
|
@@ -1,294 +1,193 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
|
-
import 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';
|
|
7
|
+
import { withGlobalProps } from '../../provider';
|
|
8
8
|
import { classNames } from '../../utils/classNames';
|
|
9
|
-
import { transferProps } from '../_helpers/transferProps';
|
|
10
|
-
import {
|
|
11
|
-
Toolbar,
|
|
12
|
-
ToolbarItem,
|
|
13
|
-
} from '../Toolbar';
|
|
14
|
-
import Button from '../Button';
|
|
15
|
-
import ScrollView from '../ScrollView';
|
|
16
9
|
import styles from './Modal.scss';
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
const preRender = (
|
|
12
|
+
children,
|
|
13
|
+
childrenWrapperRef,
|
|
14
|
+
id,
|
|
15
|
+
closeButtonRef,
|
|
16
|
+
position,
|
|
17
|
+
size,
|
|
18
|
+
) => {
|
|
19
|
+
const sizeClass = (modalSize) => {
|
|
20
|
+
if (modalSize === 'small') {
|
|
21
|
+
return styles.isRootSizeSmall;
|
|
22
|
+
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
if (modalSize === 'medium') {
|
|
25
|
+
return styles.isRootSizeMedium;
|
|
26
|
+
}
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
if (modalSize === 'large') {
|
|
29
|
+
return styles.isRootSizeLarge;
|
|
30
|
+
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if (modalSize === 'fullscreen') {
|
|
33
|
+
return styles.isRootSizeFullscreen;
|
|
34
|
+
}
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
return styles.isRootSizeAuto;
|
|
37
|
+
};
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
);
|
|
39
|
+
const positionClass = (modalPosition) => {
|
|
40
|
+
if (modalPosition === 'top') {
|
|
41
|
+
return styles.isRootPositionTop;
|
|
42
|
+
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
return styles.isRootPositionCenter;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
className={styles.backdrop}
|
|
50
|
+
id={id}
|
|
51
|
+
onClick={() => {
|
|
52
|
+
if (closeButtonRef?.current != null) {
|
|
53
|
+
closeButtonRef.current.click();
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
role="presentation"
|
|
57
|
+
>
|
|
58
|
+
<div
|
|
59
|
+
className={classNames(
|
|
60
|
+
styles.root,
|
|
61
|
+
sizeClass(size),
|
|
62
|
+
positionClass(position),
|
|
63
|
+
)}
|
|
64
|
+
onClick={(e) => {
|
|
65
|
+
e.stopPropagation();
|
|
66
|
+
}}
|
|
67
|
+
role="presentation"
|
|
68
|
+
ref={childrenWrapperRef}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
47
75
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
76
|
+
export const Modal = ({
|
|
77
|
+
autoFocus,
|
|
78
|
+
children,
|
|
79
|
+
closeButtonRef,
|
|
80
|
+
id,
|
|
81
|
+
portalId,
|
|
82
|
+
position,
|
|
83
|
+
primaryButtonRef,
|
|
84
|
+
size,
|
|
85
|
+
}) => {
|
|
86
|
+
const childrenWrapperRef = useRef();
|
|
87
|
+
|
|
88
|
+
const keyPressHandler = (e) => {
|
|
89
|
+
if (e.key === 'Escape' && closeButtonRef?.current != null) {
|
|
90
|
+
closeButtonRef.current.click();
|
|
51
91
|
}
|
|
52
|
-
}
|
|
53
92
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
93
|
+
if (e.key === 'Enter' && e.target.nodeName !== 'BUTTON' && primaryButtonRef?.current != null) {
|
|
94
|
+
primaryButtonRef.current.click();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
57
97
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
window.document.addEventListener('keydown', keyPressHandler, false);
|
|
100
|
+
const removeKeyPressHandler = () => {
|
|
101
|
+
window.document.removeEventListener('keydown', keyPressHandler, false);
|
|
102
|
+
};
|
|
63
103
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
104
|
+
// If `autoFocus` is set to `true`, following code finds first form field element
|
|
105
|
+
// (input, textarea or select) or primary button and auto focuses it. This is necessary
|
|
106
|
+
// to have focus on one of those elements to be able to submit form by pressing Enter key.
|
|
107
|
+
if (autoFocus) {
|
|
108
|
+
if (childrenWrapperRef?.current != null) {
|
|
109
|
+
const childrenWrapperElement = childrenWrapperRef.current;
|
|
110
|
+
const childrenElements = childrenWrapperElement.querySelectorAll('*');
|
|
111
|
+
const formFieldEl = Array.from(childrenElements).find(
|
|
112
|
+
(element) => ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName),
|
|
113
|
+
);
|
|
67
114
|
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
if (formFieldEl) {
|
|
116
|
+
formFieldEl.focus();
|
|
117
|
+
return removeKeyPressHandler;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
70
120
|
|
|
71
|
-
if (
|
|
72
|
-
|
|
121
|
+
if (primaryButtonRef?.current != null) {
|
|
122
|
+
primaryButtonRef.current.focus();
|
|
73
123
|
}
|
|
74
124
|
}
|
|
75
|
-
}
|
|
76
125
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
126
|
+
return removeKeyPressHandler;
|
|
127
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
128
|
+
|
|
129
|
+
if (portalId === null) {
|
|
130
|
+
return preRender(
|
|
80
131
|
children,
|
|
132
|
+
childrenWrapperRef,
|
|
81
133
|
id,
|
|
82
|
-
|
|
134
|
+
closeButtonRef,
|
|
83
135
|
position,
|
|
84
|
-
scrollView,
|
|
85
136
|
size,
|
|
86
|
-
title,
|
|
87
|
-
} = this.props;
|
|
88
|
-
|
|
89
|
-
const sizeClass = (modalSize) => {
|
|
90
|
-
if (modalSize === 'small') {
|
|
91
|
-
return styles.isRootSizeSmall;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (modalSize === 'medium') {
|
|
95
|
-
return styles.isRootSizeMedium;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (modalSize === 'large') {
|
|
99
|
-
return styles.isRootSizeLarge;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (modalSize === 'fullscreen') {
|
|
103
|
-
return styles.isRootSizeFullscreen;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return styles.isRootSizeAuto;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const positionClass = (modalPosition) => {
|
|
110
|
-
if (modalPosition === 'top') {
|
|
111
|
-
return styles.isRootPositionTop;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return styles.isRootPositionCenter;
|
|
115
|
-
};
|
|
116
|
-
|
|
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
|
-
);
|
|
127
|
-
|
|
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
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
<div className={styles.body}>
|
|
143
|
-
{content}
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
};
|
|
147
|
-
|
|
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>
|
|
231
137
|
);
|
|
232
138
|
}
|
|
233
139
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
140
|
+
return createPortal(
|
|
141
|
+
preRender(
|
|
142
|
+
children,
|
|
143
|
+
childrenWrapperRef,
|
|
144
|
+
id,
|
|
145
|
+
closeButtonRef,
|
|
146
|
+
position,
|
|
147
|
+
size,
|
|
148
|
+
),
|
|
149
|
+
document.getElementById(portalId),
|
|
150
|
+
);
|
|
151
|
+
};
|
|
244
152
|
|
|
245
153
|
Modal.defaultProps = {
|
|
246
|
-
actions: [],
|
|
247
154
|
autoFocus: true,
|
|
248
155
|
children: null,
|
|
156
|
+
closeButtonRef: null,
|
|
249
157
|
id: undefined,
|
|
250
|
-
onClose: null,
|
|
251
158
|
portalId: null,
|
|
252
159
|
position: 'center',
|
|
253
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
183
|
+
closeButtonRef: PropTypes.shape({
|
|
184
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
185
|
+
current: PropTypes.any,
|
|
186
|
+
}),
|
|
288
187
|
/**
|
|
289
|
-
*
|
|
188
|
+
* ID of the root HTML element.
|
|
290
189
|
*/
|
|
291
|
-
|
|
190
|
+
id: PropTypes.string,
|
|
292
191
|
/**
|
|
293
192
|
* If set, modal is rendered in the React Portal with that ID.
|
|
294
193
|
*/
|
|
@@ -298,19 +197,17 @@ Modal.propTypes = {
|
|
|
298
197
|
*/
|
|
299
198
|
position: PropTypes.oneOf(['top', 'center']),
|
|
300
199
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* If set to `null` the entire modal including header and footer will be scrollable.
|
|
200
|
+
* Reference to primary button element. It is used to submit modal when Enter key is pressed and as fallback
|
|
201
|
+
* when `autoFocus` functionality does not find any input element to be focused.
|
|
304
202
|
*/
|
|
305
|
-
|
|
203
|
+
primaryButtonRef: PropTypes.shape({
|
|
204
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
205
|
+
current: PropTypes.any,
|
|
206
|
+
}),
|
|
306
207
|
/**
|
|
307
208
|
* Size of the modal.
|
|
308
209
|
*/
|
|
309
210
|
size: PropTypes.oneOf(['small', 'medium', 'large', 'fullscreen', 'auto']),
|
|
310
|
-
/**
|
|
311
|
-
* Title displayed in modal header.
|
|
312
|
-
*/
|
|
313
|
-
title: PropTypes.string.isRequired,
|
|
314
211
|
};
|
|
315
212
|
|
|
316
213
|
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:
|
|
19
|
-
max-height:
|
|
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,64 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { withGlobalProps } from '../../provider';
|
|
4
|
+
import { classNames } from '../../utils/classNames';
|
|
5
|
+
import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
|
|
6
|
+
import { getScrollingClassName } from './_helpers/getScrollingClassName';
|
|
7
|
+
import styles from './ModalBody.scss';
|
|
8
|
+
|
|
9
|
+
export const ModalBody = ({
|
|
10
|
+
children,
|
|
11
|
+
id,
|
|
12
|
+
scrolling,
|
|
13
|
+
}) => {
|
|
14
|
+
if (isChildrenEmpty(children)) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={classNames(
|
|
21
|
+
styles.root,
|
|
22
|
+
getScrollingClassName(scrolling, styles),
|
|
23
|
+
)}
|
|
24
|
+
id={id}
|
|
25
|
+
>
|
|
26
|
+
{children}
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
ModalBody.defaultProps = {
|
|
32
|
+
children: null,
|
|
33
|
+
id: undefined,
|
|
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
|
+
* ID of the root HTML element.
|
|
50
|
+
*/
|
|
51
|
+
id: PropTypes.string,
|
|
52
|
+
/**
|
|
53
|
+
* Scrolling mode:
|
|
54
|
+
*
|
|
55
|
+
* - `auto`: scrolling is enabled on ModalBody.
|
|
56
|
+
* - `custom`: use if providing a custom scrolling component, e.g. an instance of `ScrollView`.
|
|
57
|
+
* - `none`: scrolling is disabled on ModalBody and the entire Modal is scrollable instead.
|
|
58
|
+
*/
|
|
59
|
+
scrolling: PropTypes.oneOf(['auto', 'custom', 'none']),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const ModalBodyWithGlobalProps = withGlobalProps(ModalBody, 'ModalBody');
|
|
63
|
+
|
|
64
|
+
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,61 @@
|
|
|
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
|
+
id,
|
|
14
|
+
...restProps
|
|
15
|
+
} = props;
|
|
16
|
+
|
|
17
|
+
const { translations } = useContext(RUIContext);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<button
|
|
21
|
+
{...transferProps(restProps)}
|
|
22
|
+
type="button"
|
|
23
|
+
className={styles.root}
|
|
24
|
+
disabled={disabled}
|
|
25
|
+
id={id}
|
|
26
|
+
ref={ref}
|
|
27
|
+
title={translations.ModalCloseButton.close}
|
|
28
|
+
>
|
|
29
|
+
×
|
|
30
|
+
</button>
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
ModalCloseButton.defaultProps = {
|
|
35
|
+
disabled: false,
|
|
36
|
+
id: undefined,
|
|
37
|
+
ref: undefined,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
ModalCloseButton.propTypes = {
|
|
41
|
+
/**
|
|
42
|
+
* If `true`, close button will be disabled.
|
|
43
|
+
*/
|
|
44
|
+
disabled: PropTypes.bool,
|
|
45
|
+
/**
|
|
46
|
+
* ID of the root HTML element.
|
|
47
|
+
*/
|
|
48
|
+
id: PropTypes.string,
|
|
49
|
+
/**
|
|
50
|
+
* Reference forwarded to the `button` element.
|
|
51
|
+
*/
|
|
52
|
+
ref: PropTypes.oneOfType([
|
|
53
|
+
PropTypes.func,
|
|
54
|
+
// eslint-disable-next-line react/forbid-prop-types
|
|
55
|
+
PropTypes.shape({ current: PropTypes.any }),
|
|
56
|
+
]),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const ModalCloseButtonWithGlobalProps = withGlobalProps(ModalCloseButton, 'ModalCloseButton');
|
|
60
|
+
|
|
61
|
+
export default ModalCloseButtonWithGlobalProps;
|