@react-ui-org/react-ui 0.58.0 → 0.59.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 (92) hide show
  1. package/README.md +2 -11
  2. package/dist/react-ui.css +17 -17
  3. package/dist/react-ui.development.css +1228 -1051
  4. package/dist/react-ui.development.js +126 -66
  5. package/dist/react-ui.js +1 -1
  6. package/package.json +5 -5
  7. package/src/components/Alert/Alert.jsx +4 -4
  8. package/src/components/Alert/README.md +0 -26
  9. package/src/components/Alert/_settings.scss +1 -2
  10. package/src/components/Badge/Badge.jsx +2 -2
  11. package/src/components/Button/Button.jsx +2 -2
  12. package/src/components/ButtonGroup/ButtonGroup.jsx +2 -2
  13. package/src/components/Card/Card.jsx +6 -6
  14. package/src/components/Card/Card.module.scss +2 -2
  15. package/src/components/Card/CardBody.jsx +1 -1
  16. package/src/components/Card/CardFooter.jsx +1 -1
  17. package/src/components/Card/README.md +2 -21
  18. package/src/components/Card/_settings.scss +1 -2
  19. package/src/components/Card/_theme.scss +1 -0
  20. package/src/components/CheckboxField/CheckboxField.jsx +2 -2
  21. package/src/components/FileInputField/FileInputField.jsx +147 -21
  22. package/src/components/FileInputField/FileInputField.module.scss +87 -1
  23. package/src/components/FileInputField/README.md +83 -2
  24. package/src/components/FileInputField/_settings.scss +15 -0
  25. package/src/components/FormLayout/FormLayout.jsx +2 -2
  26. package/src/components/FormLayout/FormLayoutCustomField.jsx +2 -2
  27. package/src/components/FormLayout/README.md +1 -0
  28. package/src/components/Grid/Grid.jsx +1 -1
  29. package/src/components/Grid/Grid.module.scss +2 -2
  30. package/src/components/Grid/GridSpan.jsx +1 -1
  31. package/src/components/InputGroup/InputGroup.jsx +2 -2
  32. package/src/components/InputGroup/InputGroup.module.scss +3 -3
  33. package/src/components/InputGroup/README.md +1 -1
  34. package/src/components/Modal/Modal.jsx +117 -45
  35. package/src/components/Modal/Modal.module.scss +34 -18
  36. package/src/components/Modal/ModalBody.jsx +2 -2
  37. package/src/components/Modal/ModalBody.module.scss +18 -0
  38. package/src/components/Modal/ModalCloseButton.jsx +1 -1
  39. package/src/components/Modal/ModalContent.jsx +1 -1
  40. package/src/components/Modal/ModalFooter.jsx +2 -2
  41. package/src/components/Modal/ModalFooter.module.scss +6 -2
  42. package/src/components/Modal/ModalHeader.jsx +2 -2
  43. package/src/components/Modal/ModalHeader.module.scss +8 -1
  44. package/src/components/Modal/ModalTitle.jsx +1 -1
  45. package/src/components/Modal/README.md +391 -171
  46. package/src/components/Modal/_animations.scss +9 -0
  47. package/src/components/Modal/_helpers/dialogOnCancelHandler.js +28 -0
  48. package/src/components/Modal/_helpers/dialogOnClickHandler.js +46 -0
  49. package/src/components/Modal/_helpers/dialogOnCloseHandler.js +28 -0
  50. package/src/components/Modal/_helpers/dialogOnKeyDownHandler.js +62 -0
  51. package/src/components/Modal/_helpers/getPositionClassName.js +1 -1
  52. package/src/components/Modal/_hooks/useModalFocus.js +24 -91
  53. package/src/components/Modal/_settings.scss +4 -3
  54. package/src/components/Modal/_theme.scss +1 -0
  55. package/src/components/Paper/Paper.jsx +2 -2
  56. package/src/components/Popover/Popover.jsx +2 -2
  57. package/src/components/Popover/PopoverWrapper.jsx +1 -1
  58. package/src/components/Radio/Radio.jsx +2 -2
  59. package/src/components/ScrollView/ScrollView.jsx +2 -2
  60. package/src/components/SelectField/SelectField.jsx +2 -2
  61. package/src/components/Table/Table.jsx +1 -1
  62. package/src/components/Tabs/Tabs.jsx +1 -1
  63. package/src/components/Tabs/TabsItem.jsx +2 -2
  64. package/src/components/Text/Text.jsx +2 -2
  65. package/src/components/TextArea/TextArea.jsx +2 -2
  66. package/src/components/TextField/TextField.jsx +2 -2
  67. package/src/components/TextLink/TextLink.jsx +1 -1
  68. package/src/components/Toggle/Toggle.jsx +2 -2
  69. package/src/components/Toolbar/Toolbar.jsx +2 -2
  70. package/src/components/Toolbar/ToolbarGroup.jsx +2 -2
  71. package/src/components/Toolbar/ToolbarItem.jsx +2 -2
  72. package/src/helpers/classNames/README.md +65 -0
  73. package/src/helpers/classNames/classNames.js +11 -0
  74. package/src/helpers/classNames/index.js +1 -0
  75. package/src/helpers/transferProps/README.md +46 -0
  76. package/src/helpers/transferProps/index.js +1 -0
  77. package/src/index.js +3 -3
  78. package/src/styles/elements/_links.scss +2 -14
  79. package/src/styles/generic/_focus.scss +1 -1
  80. package/src/styles/theme/_form-fields.scss +5 -5
  81. package/src/styles/tools/_accessibility.scss +3 -5
  82. package/src/styles/tools/_collections.scss +3 -20
  83. package/src/styles/tools/_links.scss +17 -0
  84. package/src/styles/tools/form-fields/_box-field-elements.scss +21 -9
  85. package/src/styles/tools/form-fields/_box-field-layout.scss +2 -2
  86. package/src/styles/tools/form-fields/_box-field-sizes.scss +6 -10
  87. package/src/styles/tools/form-fields/_variants.scss +10 -10
  88. package/src/theme.scss +51 -1
  89. package/src/translations/en.js +5 -0
  90. package/src/styles/settings/_z-indexes.scss +0 -2
  91. package/src/utils/classNames.js +0 -8
  92. /package/src/{utils → helpers/transferProps}/transferProps.js +0 -0
@@ -13,7 +13,7 @@ import { FileInputField } from '@react-ui-org/react-ui';
13
13
  And use it:
14
14
 
15
15
  ```docoff-react-preview
16
- <FileInputField label="Attachment" />
16
+ <FileInputField id="my-file" label="Attachment" onFilesChanged={() => {}} />
17
17
  ```
18
18
 
19
19
  See [API](#api) for all available options.
@@ -48,12 +48,37 @@ layout perspective, FileInputFields work just like any other form fields.
48
48
 
49
49
  ## Sizes
50
50
 
51
+ Aside from the default (medium) size, two additional sizes are available: small
52
+ and large.
53
+
54
+ ```docoff-react-preview
55
+ <FileInputField
56
+ id="my-file-small"
57
+ label="Attachment"
58
+ onFilesChanged={() => {}}
59
+ size="small"
60
+ />
61
+ <FileInputField
62
+ id="my-file-medium"
63
+ label="Attachment"
64
+ onFilesChanged={() => {}}
65
+ />
66
+ <FileInputField
67
+ id="my-file-large"
68
+ label="Attachment"
69
+ onFilesChanged={() => {}}
70
+ size="large"
71
+ />
72
+ ```
73
+
51
74
  Full-width fields span the full width of a parent:
52
75
 
53
76
  ```docoff-react-preview
54
77
  <FileInputField
55
78
  fullWidth
79
+ id="my-file"
56
80
  label="First name"
81
+ onFilesChanged={() => {}}
57
82
  />
58
83
  ```
59
84
 
@@ -68,8 +93,10 @@ dangerous to hide labels from users in most cases. Keep in mind you should
68
93
 
69
94
  ```docoff-react-preview
70
95
  <FileInputField
96
+ id="my-file"
71
97
  isLabelVisible={false}
72
98
  label="Attachment"
99
+ onFilesChanged={() => {}}
73
100
  />
74
101
  ```
75
102
 
@@ -81,14 +108,18 @@ supports this kind of layout as well.
81
108
 
82
109
  ```docoff-react-preview
83
110
  <FileInputField
111
+ id="my-file-horizontal"
84
112
  label="Attachment"
85
113
  layout="horizontal"
114
+ onFilesChanged={() => {}}
86
115
  />
87
116
  <FileInputField
88
117
  fullWidth
118
+ id="my-file-horizontal-full-width"
89
119
  isLabelVisible={false}
90
120
  label="Attachment"
91
121
  layout="horizontal"
122
+ onFilesChanged={() => {}}
92
123
  />
93
124
  ```
94
125
 
@@ -100,18 +131,24 @@ filled.
100
131
  ```docoff-react-preview
101
132
  <FileInputField
102
133
  helpText="Choose one or more files to upload."
134
+ id="my-file-help-text"
103
135
  label="Attachment"
136
+ onFilesChanged={() => {}}
104
137
  />
105
138
  <FileInputField
106
139
  helpText="Choose one or more files to upload."
140
+ id="my-file-help-text-horizontal"
107
141
  label="Attachment"
108
142
  layout="horizontal"
143
+ onFilesChanged={() => {}}
109
144
  />
110
145
  <FileInputField
111
146
  fullWidth
112
147
  helpText="Choose one or more files to upload."
148
+ id="my-file-help-text-horizontal-full-width"
113
149
  label="Attachment"
114
150
  layout="horizontal"
151
+ onFilesChanged={() => {}}
115
152
  />
116
153
  ```
117
154
 
@@ -126,17 +163,23 @@ have.
126
163
 
127
164
  ```docoff-react-preview
128
165
  <FileInputField
166
+ id="my-file-valid"
129
167
  label="Attachment"
168
+ onFilesChanged={() => {}}
130
169
  validationState="valid"
131
170
  validationText="Looks good!"
132
171
  />
133
172
  <FileInputField
173
+ id="my-file-invalid"
134
174
  label="Attachment"
175
+ onFilesChanged={() => {}}
135
176
  validationState="invalid"
136
177
  validationText="Your file is too big. Please select something smaller."
137
178
  />
138
179
  <FileInputField
180
+ id="my-file-warning"
139
181
  label="Attachment"
182
+ onFilesChanged={() => {}}
140
183
  validationState="warning"
141
184
  validationText={`
142
185
  You selected more than 10 files.
@@ -152,7 +195,44 @@ It's possible to disable the whole input.
152
195
  ```docoff-react-preview
153
196
  <FileInputField
154
197
  disabled
198
+ id="my-file"
155
199
  label="Attachment"
200
+ onFilesChanged={() => {}}
201
+ />
202
+ ```
203
+
204
+ ## Handling Files
205
+
206
+ Files selected by the user are handled by providing a custom function to the
207
+ `onFilesChanged` prop. The `onFilesChanged` function is then called on the
208
+ `change` event of the `input` element and on the `drop` event of the root
209
+ `div` element.
210
+
211
+ ```docoff-react-preview
212
+ <FileInputField
213
+ id="my-file"
214
+ label="Attachment"
215
+ onFilesChanged={(files, event) => {
216
+ // Do something with the files…
217
+ console.log('Files selected:', files);
218
+ }}
219
+ />
220
+ ```
221
+
222
+ ### Multiple Files
223
+
224
+ By default, users can select only one file. To allow selecting multiple files,
225
+ set the `multiple` prop to `true`.
226
+
227
+ ```docoff-react-preview
228
+ <FileInputField
229
+ id="my-files"
230
+ label="Attachment"
231
+ multiple
232
+ onFilesChanged={(files, event) => {
233
+ // Do something with the files…
234
+ console.log('Files selected:', files);
235
+ }}
156
236
  />
157
237
  ```
158
238
 
@@ -172,8 +252,9 @@ to improve its accessibility.
172
252
  Choose up to 10 files. Allowed extensions are .pdf, .jpg, .jpeg, or .png.
173
253
  Size limit is 10 MB.
174
254
  `}
255
+ id="my-file"
175
256
  label="Attachment"
176
- multiple
257
+ onFilesChanged={() => {}}
177
258
  />
178
259
  ```
179
260
 
@@ -0,0 +1,15 @@
1
+ @use "../../styles/theme/typography";
2
+
3
+ $drop-zone-color: var(--rui-color-text-primary);
4
+ $drop-zone-disabled-color: var(--rui-color-text-primary-disabled);
5
+ $drop-zone-border-color: var(--rui-color-border-primary);
6
+ $drop-zone-hover-border-color: var(--rui-color-border-primary-hover);
7
+ $drop-zone-active-border-color: var(--rui-color-border-primary-active);
8
+ $drop-zone-dragging-border-color: var(--rui-color-border-primary-active);
9
+ $drop-zone-disabled-border-color: var(--rui-color-border-primary);
10
+ $drop-zone-background-color: var(--rui-color-background-basic);
11
+ $drop-zone-disabled-background-color: var(--rui-color-background-disabled);
12
+ $drop-zone-disabled-cursor: var(--rui-cursor-not-allowed);
13
+ $drop-zone-font-weight: typography.$font-weight-base;
14
+ $drop-zone-line-height: typography.$line-height-base;
15
+ $drop-zone-font-family: typography.$font-family-base;
@@ -1,8 +1,8 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useMemo } from 'react';
3
3
  import { withGlobalProps } from '../../providers/globalProps';
4
- import { classNames } from '../../utils/classNames';
5
- import { transferProps } from '../../utils/transferProps';
4
+ import { classNames } from '../../helpers/classNames/classNames';
5
+ import { transferProps } from '../../helpers/transferProps';
6
6
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
7
7
  import { FormLayoutContext } from './FormLayoutContext';
8
8
  import styles from './FormLayout.module.scss';
@@ -1,8 +1,8 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
3
  import { withGlobalProps } from '../../providers/globalProps';
4
- import { classNames } from '../../utils/classNames';
5
- import { transferProps } from '../../utils/transferProps';
4
+ import { classNames } from '../../helpers/classNames/classNames';
5
+ import { transferProps } from '../../helpers/transferProps';
6
6
  import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
7
7
  import { getRootValidationStateClassName } from '../_helpers/getRootValidationStateClassName';
8
8
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
@@ -389,6 +389,7 @@ React.createElement(() => {
389
389
  />
390
390
  <FileInputField
391
391
  label="Attachment"
392
+ onFilesChanged={() => {}}
392
393
  />
393
394
  <Toggle
394
395
  checked={receiveNewsletter}
@@ -1,7 +1,7 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
  import { withGlobalProps } from '../../providers/globalProps';
4
- import { transferProps } from '../../utils/transferProps';
4
+ import { transferProps } from '../../helpers/transferProps';
5
5
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
6
6
  import { generateResponsiveCustomProperties } from './_helpers/generateResponsiveCustomProperties';
7
7
  import styles from './Grid.module.scss';
@@ -20,8 +20,8 @@
20
20
  //
21
21
  // 2. Apply custom property value that is defined within current breakpoint, see 1.
22
22
  //
23
- // 3. Intentionally use longhand properties because the custom property fallback mechanism evaluates `initial` values as
24
- // empty. That makes the other value of the shorthand property unexpectedly used for both axes.
23
+ // 3. Intentionally use longhand properties because the custom property fallback mechanism evaluates `initial` values
24
+ // as empty. That makes the other value of the shorthand property unexpectedly used for both axes.
25
25
 
26
26
  @use "sass:map";
27
27
  @use "../../styles/tools/spacing";
@@ -1,7 +1,7 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
  import { withGlobalProps } from '../../providers/globalProps';
4
- import { transferProps } from '../../utils/transferProps';
4
+ import { transferProps } from '../../helpers/transferProps';
5
5
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
6
6
  import { generateResponsiveCustomProperties } from './_helpers/generateResponsiveCustomProperties';
7
7
  import styles from './Grid.module.scss';
@@ -4,8 +4,8 @@ import React, {
4
4
  useMemo,
5
5
  } from 'react';
6
6
  import { withGlobalProps } from '../../providers/globalProps';
7
- import { classNames } from '../../utils/classNames';
8
- import { transferProps } from '../../utils/transferProps';
7
+ import { classNames } from '../../helpers/classNames/classNames';
8
+ import { transferProps } from '../../helpers/transferProps';
9
9
  import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
10
10
  import { getRootValidationStateClassName } from '../_helpers/getRootValidationStateClassName';
11
11
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
@@ -84,14 +84,14 @@
84
84
 
85
85
  // Sizes
86
86
  .isRootSizeSmall {
87
- @include box-field-sizes.size(small, $has-input: false);
87
+ @include box-field-sizes.size(small);
88
88
  }
89
89
 
90
90
  .isRootSizeMedium {
91
- @include box-field-sizes.size(medium, $has-input: false);
91
+ @include box-field-sizes.size(medium);
92
92
  }
93
93
 
94
94
  .isRootSizeLarge {
95
- @include box-field-sizes.size(large, $has-input: false);
95
+ @include box-field-sizes.size(large);
96
96
  }
97
97
  }
@@ -145,7 +145,7 @@ supports this kind of layout as well.
145
145
  label="Horizontal layout"
146
146
  layout="horizontal"
147
147
  >
148
- <TextField label="Label" />
148
+ <FileInputField label="Attachment" onFilesChanged={() => {}} />
149
149
  <Button label="Submit" />
150
150
  </InputGroup>
151
151
  ```
@@ -1,9 +1,19 @@
1
1
  import PropTypes from 'prop-types';
2
- import React, { useRef } from 'react';
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useImperativeHandle,
6
+ useRef,
7
+ } from 'react';
3
8
  import { createPortal } from 'react-dom';
9
+ import { classNames } from '../../helpers/classNames';
10
+ import { transferProps } from '../../helpers/transferProps';
4
11
  import { withGlobalProps } from '../../providers/globalProps';
5
- import { classNames } from '../../utils/classNames';
6
- import { transferProps } from '../../utils/transferProps';
12
+ import { getRootColorClassName } from '../_helpers/getRootColorClassName';
13
+ import { dialogOnCancelHandler } from './_helpers/dialogOnCancelHandler';
14
+ import { dialogOnClickHandler } from './_helpers/dialogOnClickHandler';
15
+ import { dialogOnCloseHandler } from './_helpers/dialogOnCloseHandler';
16
+ import { dialogOnKeyDownHandler } from './_helpers/dialogOnKeyDownHandler';
7
17
  import { getPositionClassName } from './_helpers/getPositionClassName';
8
18
  import { getSizeClassName } from './_helpers/getSizeClassName';
9
19
  import { useModalFocus } from './_hooks/useModalFocus';
@@ -12,44 +22,37 @@ import styles from './Modal.module.scss';
12
22
 
13
23
  const preRender = (
14
24
  children,
15
- childrenWrapperRef,
16
- closeButtonRef,
25
+ color,
26
+ dialogRef,
17
27
  position,
18
- restProps,
19
28
  size,
29
+ events,
30
+ restProps,
20
31
  ) => (
21
- <div
22
- className={styles.backdrop}
23
- onClick={(e) => {
24
- e.preventDefault();
25
- if (closeButtonRef?.current != null) {
26
- closeButtonRef.current.click();
27
- }
28
- }}
29
- role="presentation"
32
+ <dialog
33
+ {...transferProps(restProps)}
34
+ {...transferProps(events)}
35
+ className={classNames(
36
+ styles.root,
37
+ color && getRootColorClassName(color, styles),
38
+ getSizeClassName(size, styles),
39
+ getPositionClassName(position, styles),
40
+ )}
41
+ ref={dialogRef}
30
42
  >
31
- <div
32
- {...transferProps(restProps)}
33
- className={classNames(
34
- styles.root,
35
- getSizeClassName(size, styles),
36
- getPositionClassName(position, styles),
37
- )}
38
- onClick={(e) => {
39
- e.stopPropagation();
40
- }}
41
- ref={childrenWrapperRef}
42
- role="presentation"
43
- >
44
- {children}
45
- </div>
46
- </div>
43
+ {children}
44
+ </dialog>
47
45
  );
48
46
 
49
47
  export const Modal = ({
48
+ allowCloseOnBackdropClick,
49
+ allowCloseOnEscapeKey,
50
+ allowPrimaryActionOnEnterKey,
50
51
  autoFocus,
51
52
  children,
52
53
  closeButtonRef,
54
+ color,
55
+ dialogRef,
53
56
  portalId,
54
57
  position,
55
58
  preventScrollUnderneath,
@@ -57,45 +60,89 @@ export const Modal = ({
57
60
  size,
58
61
  ...restProps
59
62
  }) => {
60
- const childrenWrapperRef = useRef();
63
+ const internalDialogRef = useRef();
61
64
 
62
- useModalFocus(
63
- autoFocus,
64
- childrenWrapperRef,
65
- primaryButtonRef,
66
- closeButtonRef,
67
- );
65
+ useEffect(() => {
66
+ internalDialogRef.current.showModal();
67
+ }, []);
68
68
 
69
+ // We need to have a reference to the dialog element to be able to call its methods,
70
+ // but at the same time we want to expose this reference to the parent component for
71
+ // case someone wants to call dialog methods from outside the component.
72
+ useImperativeHandle(dialogRef, () => internalDialogRef.current);
73
+
74
+ useModalFocus(autoFocus, internalDialogRef, primaryButtonRef);
69
75
  useModalScrollPrevention(preventScrollUnderneath);
70
76
 
77
+ const onCancel = useCallback(
78
+ (e) => dialogOnCancelHandler(e, closeButtonRef, restProps.onCancel),
79
+ [closeButtonRef, restProps.onCancel],
80
+ );
81
+ const onClick = useCallback(
82
+ (e) => dialogOnClickHandler(e, closeButtonRef, internalDialogRef, allowCloseOnBackdropClick),
83
+ [allowCloseOnBackdropClick, closeButtonRef, internalDialogRef],
84
+ );
85
+ const onClose = useCallback(
86
+ (e) => dialogOnCloseHandler(e, closeButtonRef, restProps.onClose),
87
+ [closeButtonRef, restProps.onClose],
88
+ );
89
+ const onKeyDown = useCallback(
90
+ (e) => dialogOnKeyDownHandler(
91
+ e,
92
+ closeButtonRef,
93
+ primaryButtonRef,
94
+ allowCloseOnEscapeKey,
95
+ allowPrimaryActionOnEnterKey,
96
+ ),
97
+ [
98
+ allowCloseOnEscapeKey,
99
+ allowPrimaryActionOnEnterKey,
100
+ closeButtonRef,
101
+ primaryButtonRef,
102
+ ],
103
+ );
104
+ const events = {
105
+ onCancel,
106
+ onClick,
107
+ onClose,
108
+ onKeyDown,
109
+ };
110
+
71
111
  if (portalId === null) {
72
112
  return preRender(
73
113
  children,
74
- childrenWrapperRef,
75
- closeButtonRef,
114
+ color,
115
+ internalDialogRef,
76
116
  position,
77
- restProps,
78
117
  size,
118
+ events,
119
+ restProps,
79
120
  );
80
121
  }
81
122
 
82
123
  return createPortal(
83
124
  preRender(
84
125
  children,
85
- childrenWrapperRef,
86
- closeButtonRef,
126
+ color,
127
+ internalDialogRef,
87
128
  position,
88
- restProps,
89
129
  size,
130
+ events,
131
+ restProps,
90
132
  ),
91
133
  document.getElementById(portalId),
92
134
  );
93
135
  };
94
136
 
95
137
  Modal.defaultProps = {
138
+ allowCloseOnBackdropClick: true,
139
+ allowCloseOnEscapeKey: true,
140
+ allowPrimaryActionOnEnterKey: true,
96
141
  autoFocus: true,
97
142
  children: null,
98
143
  closeButtonRef: null,
144
+ color: undefined,
145
+ dialogRef: null,
99
146
  portalId: null,
100
147
  position: 'center',
101
148
  preventScrollUnderneath: window.document.body,
@@ -104,6 +151,18 @@ Modal.defaultProps = {
104
151
  };
105
152
 
106
153
  Modal.propTypes = {
154
+ /**
155
+ * If `true`, the `Modal` can be closed by clicking on the backdrop.
156
+ */
157
+ allowCloseOnBackdropClick: PropTypes.bool,
158
+ /**
159
+ * If `true`, the `Modal` can be closed by pressing the Escape key.
160
+ */
161
+ allowCloseOnEscapeKey: PropTypes.bool,
162
+ /**
163
+ * If `true`, the `Modal` can be submitted by pressing the Enter key.
164
+ */
165
+ allowPrimaryActionOnEnterKey: PropTypes.bool,
107
166
  /**
108
167
  * If `true`, focus the first input element in the `Modal`, or primary button (referenced by the `primaryButtonRef`
109
168
  * prop), or other focusable element when the `Modal` is opened. If there are none or `autoFocus` is set to `false`,
@@ -121,12 +180,25 @@ Modal.propTypes = {
121
180
  */
122
181
  children: PropTypes.node,
123
182
  /**
124
- * Reference to close button element. It is used to close modal when Escape key is pressed or the backdrop is clicked.
183
+ * Reference to close button element. It is used to close modal when Escape key is pressed
184
+ * or the backdrop is clicked.
125
185
  */
126
186
  closeButtonRef: PropTypes.shape({
127
187
  // eslint-disable-next-line react/forbid-prop-types
128
188
  current: PropTypes.any,
129
189
  }),
190
+ /**
191
+ * Color to clarify importance and meaning of the modal. Implements
192
+ * [Feedback color collection](/docs/foundation/collections#colors).
193
+ */
194
+ color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note']),
195
+ /**
196
+ * Reference to dialog element
197
+ */
198
+ dialogRef: PropTypes.shape({
199
+ // eslint-disable-next-line react/forbid-prop-types
200
+ current: PropTypes.any,
201
+ }),
130
202
  /**
131
203
  * If set, modal is rendered in the React Portal with that ID.
132
204
  */
@@ -1,9 +1,16 @@
1
+ // 1. Modal uses <dialog> element that uses the browser's built-in dialog functionality, so that:
2
+ // * visibility of the .root element and its backdrop is managed by the browser
3
+ // * positioning of the .root element and its backdrop is managed by the browser
4
+ // * z-index of the .root element and its backdrop is not needed as dialog is rendered in browser's Top layer
5
+
1
6
  @use "sass:map";
2
7
  @use "../../styles/theme/typography";
3
8
  @use "../../styles/tools/accessibility";
4
9
  @use "../../styles/tools/breakpoint";
10
+ @use "../../styles/tools/collections";
5
11
  @use "../../styles/tools/reset";
6
12
  @use "../../styles/tools/spacing";
13
+ @use "animations";
7
14
  @use "settings";
8
15
  @use "theme";
9
16
 
@@ -13,18 +20,16 @@
13
20
  --rui-local-max-width: calc(100% - (2 * var(--rui-local-outer-spacing)));
14
21
  --rui-local-max-height: calc(100% - (2 * var(--rui-local-outer-spacing)));
15
22
 
16
- position: fixed;
17
- left: 50%;
18
- z-index: settings.$z-index;
19
- display: flex;
20
23
  flex-direction: column;
21
24
  max-width: var(--rui-local-max-width);
22
25
  max-height: var(--rui-local-max-height);
26
+ padding: 0;
23
27
  overflow-y: auto;
28
+ color: inherit;
29
+ border-width: 0;
24
30
  border-radius: settings.$border-radius;
25
31
  background: theme.$background;
26
32
  box-shadow: theme.$box-shadow;
27
- transform: translateX(-50%);
28
33
  overscroll-behavior: contain;
29
34
 
30
35
  @include breakpoint.up(sm) {
@@ -32,14 +37,20 @@
32
37
  }
33
38
  }
34
39
 
35
- .backdrop {
36
- position: fixed;
37
- top: 0;
38
- left: 0;
39
- z-index: settings.$backdrop-z-index;
40
- width: 100vw;
41
- height: 100vh;
40
+ .root[open] {
41
+ display: flex;
42
+
43
+ @media (prefers-reduced-motion: no-preference) {
44
+ animation: fade-in theme.$animation-duration ease-out;
45
+ }
46
+ }
47
+
48
+ .root[open]::backdrop {
42
49
  background: theme.$backdrop-background;
50
+
51
+ @media (prefers-reduced-motion: no-preference) {
52
+ animation: inherit;
53
+ }
43
54
  }
44
55
 
45
56
  .isRootSizeSmall {
@@ -64,17 +75,22 @@
64
75
  }
65
76
 
66
77
  .isRootSizeAuto {
67
- width: auto;
68
78
  min-width: min(var(--rui-local-max-width), #{map.get(theme.$sizes, auto, min-width)});
69
79
  max-width: min(var(--rui-local-max-width), #{map.get(theme.$sizes, auto, max-width)});
70
80
  }
71
81
 
72
- .isRootPositionCenter {
73
- top: 50%;
74
- transform: translate(-50%, -50%);
75
- }
76
-
77
82
  .isRootPositionTop {
78
83
  top: var(--rui-local-outer-spacing);
84
+ bottom: auto;
85
+ }
86
+
87
+ @each $color in settings.$colors {
88
+ @include collections.generate-class(
89
+ $prefix: "rui-",
90
+ $component-name: "Modal",
91
+ $variant-name: "color",
92
+ $variant-value: $color,
93
+ $properties: settings.$themeable-properties,
94
+ );
79
95
  }
80
96
  }
@@ -1,8 +1,8 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
  import { withGlobalProps } from '../../providers/globalProps';
4
- import { classNames } from '../../utils/classNames';
5
- import { transferProps } from '../../utils/transferProps';
4
+ import { classNames } from '../../helpers/classNames/classNames';
5
+ import { transferProps } from '../../helpers/transferProps';
6
6
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
7
7
  import { getScrollingClassName } from './_helpers/getScrollingClassName';
8
8
  import styles from './ModalBody.module.scss';
@@ -1,6 +1,24 @@
1
+ // 1. Intentionally do not provide a fallback value for the border color. Setting a fallback value (e.g. `transparent`)
2
+ // will result in the border being skewed at both ends.
3
+
4
+ @use "settings";
5
+
1
6
  @layer components.modal {
2
7
  .root {
3
8
  flex: 1 1 auto;
9
+ border-inline: settings.$border-width solid var(--rui-local-border-color); // 1.
10
+
11
+ &:first-child {
12
+ border-top: settings.$border-width solid var(--rui-local-border-color); // 1.
13
+ border-top-left-radius: settings.$border-radius;
14
+ border-top-right-radius: settings.$border-radius;
15
+ }
16
+
17
+ &:last-child {
18
+ border-bottom: settings.$border-width solid var(--rui-local-border-color); // 1.
19
+ border-bottom-right-radius: settings.$border-radius;
20
+ border-bottom-left-radius: settings.$border-radius;
21
+ }
4
22
  }
5
23
 
6
24
  .isRootScrollingAuto,
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
3
  import { TranslationsContext } from '../../providers/translations';
4
4
  import { withGlobalProps } from '../../providers/globalProps';
5
- import { transferProps } from '../../utils/transferProps';
5
+ import { transferProps } from '../../helpers/transferProps';
6
6
  import styles from './ModalCloseButton.module.scss';
7
7
 
8
8
  export const ModalCloseButton = React.forwardRef((props, ref) => {