@react-ui-org/react-ui 0.58.0 → 0.59.1

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 +1230 -1053
  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 +1 -27
  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 +12 -30
  18. package/src/components/Card/_settings.scss +1 -2
  19. package/src/components/Card/_theme.scss +2 -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 +8 -6
  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 +53 -3
  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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@react-ui-org/react-ui",
3
3
  "description": "React UI is a themeable UI library for React apps.",
4
- "version": "0.58.0",
4
+ "version": "0.59.1",
5
5
  "keywords": [
6
6
  "react",
7
7
  "ui",
@@ -56,7 +56,7 @@
56
56
  "precopy": "rm -rf dist && mkdir dist",
57
57
  "prepublishOnly": "npm run build",
58
58
  "start": "webpack --watch --mode=development",
59
- "stylelint": "stylelint \"src/**/*.scss\" --config stylelint.config.js",
59
+ "stylelint": "stylelint \"src/**/*.{css,scss}\" \"!src/docs/_assets/generated/**\" --config stylelint.config.js",
60
60
  "test": "npm run jest"
61
61
  },
62
62
  "dependencies": {
@@ -76,10 +76,11 @@
76
76
  "@babel/preset-env": "^7.24.7",
77
77
  "@babel/preset-react": "^7.24.7",
78
78
  "@babel/register": "^7.24.6",
79
+ "@happy-dom/jest-environment": "^16.6.0",
79
80
  "@stylistic/stylelint-config": "^1.0.1",
80
81
  "@svgr/webpack": "^8.1.0",
81
- "@testing-library/jest-dom": "^6.4.6",
82
- "@testing-library/react": "^16.0.0",
82
+ "@testing-library/jest-dom": "^6.6.3",
83
+ "@testing-library/react": "^16.1.0",
83
84
  "@testing-library/user-event": "^14.5.2",
84
85
  "@visionappscz/eslint-config-visionapps": "^1.7.0",
85
86
  "@visionappscz/stylelint-config": "^4.0.0",
@@ -97,7 +98,6 @@
97
98
  "eslint-plugin-react-hooks": "^4.6.2",
98
99
  "identity-obj-proxy": "^3.0.0",
99
100
  "jest": "^29.7.0",
100
- "jest-environment-jsdom": "^29.7.0",
101
101
  "markdownlint-cli2": "^0.13.0",
102
102
  "mini-css-extract-plugin": "^2.9.0",
103
103
  "postcss": "^8.4.39",
@@ -2,8 +2,8 @@ import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
3
  import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { TranslationsContext } from '../../providers/translations';
5
- import { classNames } from '../../utils/classNames';
6
- import { transferProps } from '../../utils/transferProps';
5
+ import { classNames } from '../../helpers/classNames/classNames';
6
+ import { transferProps } from '../../helpers/transferProps';
7
7
  import { getRootColorClassName } from '../_helpers/getRootColorClassName';
8
8
  import styles from './Alert.module.scss';
9
9
 
@@ -69,9 +69,9 @@ Alert.propTypes = {
69
69
  children: PropTypes.node.isRequired,
70
70
  /**
71
71
  * Color variant to clarify importance and meaning of the alert. Implements
72
- * [Feedback and Neutral color collections](/docs/foundation/collections#colors).
72
+ * [Feedback color collection](/docs/foundation/collections#colors).
73
73
  */
74
- color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark']),
74
+ color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note']),
75
75
  /**
76
76
  * Optional element to be displayed next to the alert body.
77
77
  */
@@ -32,7 +32,7 @@ See [API](#api) for all available options.
32
32
  ## Color Variants
33
33
 
34
34
  To cover all possible needs of your project, Alert is available in colors from
35
- [Feedback and Neutral color collections](/docs/foundation/collections#colors).
35
+ [Feedback color collection](/docs/foundation/collections#colors).
36
36
 
37
37
  ### Success
38
38
 
@@ -111,32 +111,6 @@ Neutral informative alert.
111
111
  </Alert>
112
112
  ```
113
113
 
114
- ### Light
115
-
116
- Light alert variant.
117
-
118
- ```docoff-react-preview
119
- <docoff-placeholder dark>
120
- <Alert color="light">
121
- <strong>Light alert:</strong> Stands out on dark backgrounds.
122
- {' '}
123
- <TextLink href="/" label="This is a link" />
124
- </Alert>
125
- </docoff-placeholder>
126
- ```
127
-
128
- ### Dark
129
-
130
- Dark alert variant.
131
-
132
- ```docoff-react-preview
133
- <Alert color="dark">
134
- <strong>Dark alert:</strong> Stands out on light backgrounds.
135
- {' '}
136
- <TextLink href="/" label="This is a link" />
137
- </Alert>
138
- ```
139
-
140
114
  ## Alerts with Icons
141
115
 
142
116
  An icon can (and should) accompany the message.
@@ -1,4 +1,3 @@
1
- @use "sass:list";
2
1
  @use "sass:map";
3
2
  @use "../../styles/settings/collections";
4
3
  @use "../../styles/theme/typography";
@@ -8,5 +7,5 @@ $font-size: map.get(typography.$font-size-values, 1);
8
7
  $line-height: typography.$line-height-base;
9
8
  $min-height: calc(#{$font-size} * #{$line-height} + 2 * #{theme.$padding});
10
9
 
11
- $colors: list.join(collections.$feedback-colors, collections.$neutral-colors);
10
+ $colors: collections.$feedback-colors;
12
11
  $themeable-properties: color, foreground-color, background-color;
@@ -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 { getRootColorClassName } from '../_helpers/getRootColorClassName';
7
7
  import { getRootPriorityClassName } from '../_helpers/getRootPriorityClassName';
8
8
  import styles from './Badge.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 { getRootColorClassName } from '../_helpers/getRootColorClassName';
7
7
  import { getRootPriorityClassName } from '../_helpers/getRootPriorityClassName';
8
8
  import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
@@ -3,8 +3,8 @@ import React, {
3
3
  useMemo,
4
4
  } from 'react';
5
5
  import { withGlobalProps } from '../../providers/globalProps';
6
- import { classNames } from '../../utils/classNames';
7
- import { transferProps } from '../../utils/transferProps';
6
+ import { classNames } from '../../helpers/classNames/classNames';
7
+ import { transferProps } from '../../helpers/transferProps';
8
8
  import { getRootPriorityClassName } from '../_helpers/getRootPriorityClassName';
9
9
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
10
10
  import styles from './ButtonGroup.module.scss';
@@ -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 { getRootColorClassName } from '../_helpers/getRootColorClassName';
7
7
  import styles from './Card.module.scss';
8
8
 
@@ -18,7 +18,7 @@ export const Card = ({
18
18
  {...transferProps(restProps)}
19
19
  className={classNames(
20
20
  styles.root,
21
- getRootColorClassName(color, styles),
21
+ color && getRootColorClassName(color, styles),
22
22
  dense && styles.isRootDense,
23
23
  raised && styles.isRootRaised,
24
24
  disabled && styles.isRootDisabled,
@@ -29,7 +29,7 @@ export const Card = ({
29
29
  );
30
30
 
31
31
  Card.defaultProps = {
32
- color: 'light',
32
+ color: undefined,
33
33
  dense: false,
34
34
  disabled: false,
35
35
  raised: false,
@@ -45,9 +45,9 @@ Card.propTypes = {
45
45
  children: PropTypes.node.isRequired,
46
46
  /**
47
47
  * Color to clarify importance and meaning of the card. Implements
48
- * [Feedback and Neutral color collections](/docs/foundation/collections#colors).
48
+ * [Feedback color collection](/docs/foundation/collections#colors).
49
49
  */
50
- color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note', 'light', 'dark']),
50
+ color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note']),
51
51
  /**
52
52
  * Make the card more compact.
53
53
  */
@@ -12,9 +12,9 @@
12
12
  flex-direction: column;
13
13
  min-width: 0; // 1.
14
14
  color: var(--rui-local-color);
15
- border: theme.$border-width solid var(--rui-local-border-color);
15
+ border: theme.$border-width solid var(--rui-local-border-color, theme.$border-color);
16
16
  border-radius: theme.$border-radius;
17
- background-color: var(--rui-local-background-color);
17
+ background-color: var(--rui-local-background-color, theme.$background-color);
18
18
  }
19
19
 
20
20
  .body {
@@ -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 styles from './Card.module.scss';
6
6
 
7
7
  export const CardBody = ({
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { transferProps } from '../../utils/transferProps';
3
+ import { transferProps } from '../../helpers/transferProps';
4
4
  import { withGlobalProps } from '../../providers/globalProps';
5
5
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
6
6
  import styles from './Card.module.scss';
@@ -148,7 +148,7 @@ for card content.
148
148
  ## Color Variants
149
149
 
150
150
  To cover all possible needs of your project, Card is available in colors from
151
- [Feedback and Neutral color collections](/docs/foundation/collections#colors).
151
+ [Feedback color collection](/docs/foundation/collections#colors).
152
152
 
153
153
  ```docoff-react-preview
154
154
  <Card color="success">
@@ -211,26 +211,6 @@ To cover all possible needs of your project, Card is available in colors from
211
211
  <Button label="Read more" priority="outline" color="note" />
212
212
  </CardFooter>
213
213
  </Card>
214
- <Card>
215
- <CardBody>
216
- Hello! I&apos;m light (default) variant of card.
217
- {' '}
218
- <TextLink href="/" label="This is a link" />
219
- </CardBody>
220
- <CardFooter>
221
- <Button label="Read more" priority="outline" color="dark" />
222
- </CardFooter>
223
- </Card>
224
- <Card color="dark">
225
- <CardBody>
226
- Hello! I&apos;m dark variant of card.
227
- {' '}
228
- <TextLink href="/" label="This is a link" />
229
- </CardBody>
230
- <CardFooter>
231
- <Button label="Read more" priority="outline" color="light" />
232
- </CardFooter>
233
- </Card>
234
214
  ```
235
215
 
236
216
  ## States
@@ -309,15 +289,17 @@ Separate your card actions with CardFooter. See
309
289
 
310
290
  ### Common Theming Options
311
291
 
312
- | Custom Property | Description |
313
- |------------------------------------------------------|--------------------------------------------------------------|
314
- | `--rui-Card__padding` | Padding shared by card header, body and footer |
315
- | `--rui-Card__border-width` | Border width |
316
- | `--rui-Card__border-radius` | Corner radius |
317
- | `--rui-Card--dense__padding` | Padding of dense card |
318
- | `--rui-Card--raised__box-shadow` | Box shadow of raised card |
319
- | `--rui-Card--disabled__background-color` | Card background color in disabled state |
320
- | `--rui-Card--disabled__opacity` | Card opacity in disabled state |
292
+ | Custom Property | Description |
293
+ |------------------------------------------|------------------------------------------------|
294
+ | `--rui-Card__padding` | Padding shared by card header, body and footer |
295
+ | `--rui-Card__border-width` | Border width |
296
+ | `--rui-Card__border-color` | Border color |
297
+ | `--rui-Card__border-radius` | Corner radius |
298
+ | `--rui-Card__background-color` | Card background color |
299
+ | `--rui-Card--dense__padding` | Padding of dense card |
300
+ | `--rui-Card--raised__box-shadow` | Box shadow of raised card |
301
+ | `--rui-Card--disabled__background-color` | Card background color in disabled state |
302
+ | `--rui-Card--disabled__opacity` | Card opacity in disabled state |
321
303
 
322
304
  ### Theming Variants
323
305
 
@@ -1,5 +1,4 @@
1
- @use "sass:list";
2
1
  @use "../../styles/settings/collections";
3
2
 
4
- $colors: list.join(collections.$feedback-colors, collections.$neutral-colors);
3
+ $colors: collections.$feedback-colors;
5
4
  $themeable-properties: color, border-color, background-color;
@@ -1,6 +1,8 @@
1
1
  $padding: var(--rui-Card__padding);
2
2
  $border-width: var(--rui-Card__border-width);
3
+ $border-color: var(--rui-Card__border-color);
3
4
  $border-radius: var(--rui-Card__border-radius);
5
+ $background-color: var(--rui-Card__background-color);
4
6
 
5
7
  $dense-padding: var(--rui-Card--dense__padding);
6
8
  $raised-box-shadow: var(--rui-Card--raised__box-shadow);
@@ -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 { getRootValidationStateClassName } from '../_helpers/getRootValidationStateClassName';
7
7
  import { FormLayoutContext } from '../FormLayout';
8
8
  import styles from './CheckboxField.module.scss';
@@ -1,10 +1,19 @@
1
1
  import PropTypes from 'prop-types';
2
- import React, { useContext } from 'react';
2
+ import React, {
3
+ useContext,
4
+ useImperativeHandle,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
3
8
  import { withGlobalProps } from '../../providers/globalProps';
4
- import { classNames } from '../../utils/classNames';
5
- import { transferProps } from '../../utils/transferProps';
9
+ import { classNames } from '../../helpers/classNames';
10
+ import { transferProps } from '../../helpers/transferProps';
11
+ import { TranslationsContext } from '../../providers/translations';
12
+ import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
6
13
  import { getRootValidationStateClassName } from '../_helpers/getRootValidationStateClassName';
7
14
  import { resolveContextOrProp } from '../_helpers/resolveContextOrProp';
15
+ import { InputGroupContext } from '../InputGroup';
16
+ import { Text } from '../Text';
8
17
  import { FormLayoutContext } from '../FormLayout';
9
18
  import styles from './FileInputField.module.scss';
10
19
 
@@ -17,54 +26,156 @@ export const FileInputField = React.forwardRef((props, ref) => {
17
26
  isLabelVisible,
18
27
  label,
19
28
  layout,
29
+ multiple,
30
+ onFilesChanged,
20
31
  required,
32
+ size,
21
33
  validationState,
22
34
  validationText,
23
35
  ...restProps
24
36
  } = props;
25
37
 
26
- const context = useContext(FormLayoutContext);
38
+ const internalInputRef = useRef();
39
+
40
+ // We need to have a reference to the input element to be able to call its methods,
41
+ // but at the same time we want to expose this reference to the parent component for
42
+ // case someone wants to call input methods from outside the component.
43
+ useImperativeHandle(ref, () => internalInputRef.current);
44
+
45
+ const formLayoutContext = useContext(FormLayoutContext);
46
+ const inputGroupContext = useContext(InputGroupContext);
47
+ const translations = useContext(TranslationsContext);
48
+
49
+ const [selectedFileNames, setSelectedFileNames] = useState([]);
50
+ const [isDragging, setIsDragging] = useState(false);
51
+
52
+ const handleFileChange = (files, event) => {
53
+ if (files.length === 0) {
54
+ setSelectedFileNames([]);
55
+ return;
56
+ }
57
+
58
+ // Mimic the native behavior of the `input` element: if multiple files are selected and the input
59
+ // does not accept multiple files, no files are processed.
60
+ if (files.length > 1 && !multiple) {
61
+ setSelectedFileNames([]);
62
+ return;
63
+ }
64
+
65
+ const fileNames = [];
66
+
67
+ [...files].forEach((file) => {
68
+ fileNames.push(file.name);
69
+ });
70
+
71
+ setSelectedFileNames(fileNames);
72
+ onFilesChanged(files, event);
73
+ };
74
+
75
+ const handleInputChange = (event) => {
76
+ handleFileChange(event.target.files, event);
77
+ };
78
+
79
+ const handleClick = () => {
80
+ internalInputRef?.current.click();
81
+ };
82
+
83
+ const handleDrop = (event) => {
84
+ event.preventDefault();
85
+ handleFileChange(event.dataTransfer.files, event);
86
+ setIsDragging(false);
87
+ };
88
+
89
+ const handleDragOver = (event) => {
90
+ if (!isDragging) {
91
+ setIsDragging(true);
92
+ }
93
+ event.preventDefault();
94
+ };
95
+
96
+ const handleDragLeave = () => {
97
+ if (isDragging) {
98
+ setIsDragging(false);
99
+ }
100
+ };
27
101
 
28
102
  return (
29
- <label
103
+ <div
30
104
  className={classNames(
31
105
  styles.root,
32
106
  fullWidth && styles.isRootFullWidth,
33
- context && styles.isRootInFormLayout,
34
- resolveContextOrProp(context && context.layout, layout) === 'horizontal'
107
+ formLayoutContext && styles.isRootInFormLayout,
108
+ resolveContextOrProp(formLayoutContext && formLayoutContext.layout, layout) === 'horizontal'
35
109
  ? styles.isRootLayoutHorizontal
36
110
  : styles.isRootLayoutVertical,
37
- disabled && styles.isRootDisabled,
111
+ resolveContextOrProp(inputGroupContext && inputGroupContext.disabled, disabled) && styles.isRootDisabled,
112
+ inputGroupContext && styles.isRootGrouped,
113
+ isDragging && styles.isRootDragging,
38
114
  required && styles.isRootRequired,
115
+ getRootSizeClassName(
116
+ resolveContextOrProp(inputGroupContext && inputGroupContext.size, size),
117
+ styles,
118
+ ),
39
119
  getRootValidationStateClassName(validationState, styles),
40
120
  )}
41
- htmlFor={id}
42
- id={id && `${id}__label`}
121
+ id={`${id}__root`}
122
+ onDragLeave={!disabled ? handleDragLeave : undefined}
123
+ onDragOver={!disabled ? handleDragOver : undefined}
124
+ onDrop={!disabled ? handleDrop : undefined}
43
125
  >
44
- <div
126
+ <label
45
127
  className={classNames(
46
128
  styles.label,
47
- !isLabelVisible && styles.isLabelHidden,
129
+ (!isLabelVisible || inputGroupContext) && styles.isLabelHidden,
48
130
  )}
49
- id={id && `${id}__labelText`}
131
+ htmlFor={id}
132
+ id={`${id}__labelText`}
50
133
  >
51
134
  {label}
52
- </div>
135
+ </label>
53
136
  <div className={styles.field}>
54
137
  <div className={styles.inputContainer}>
55
138
  <input
56
139
  {...transferProps(restProps)}
57
- disabled={disabled}
140
+ className={styles.input}
141
+ disabled={resolveContextOrProp(inputGroupContext && inputGroupContext.disabled, disabled)}
58
142
  id={id}
59
- ref={ref}
143
+ multiple={multiple}
144
+ onChange={handleInputChange}
145
+ ref={internalInputRef}
60
146
  required={required}
147
+ tabIndex={-1}
61
148
  type="file"
62
149
  />
150
+ <button
151
+ className={styles.dropZone}
152
+ disabled={resolveContextOrProp(inputGroupContext && inputGroupContext.disabled, disabled)}
153
+ onClick={handleClick}
154
+ type="button"
155
+ >
156
+ <Text lines={1}>
157
+ {!selectedFileNames.length && (
158
+ <>
159
+ <span className={styles.dropZoneLink}>{translations.FileInputField.browse}</span>
160
+ {' '}
161
+ {translations.FileInputField.drop}
162
+ </>
163
+ )}
164
+ {selectedFileNames.length === 1 && selectedFileNames[0]}
165
+ {selectedFileNames.length > 1 && (
166
+ <>
167
+ {selectedFileNames.length}
168
+ {' '}
169
+ {translations.FileInputField.filesSelected}
170
+ </>
171
+ )}
172
+ </Text>
173
+ </button>
63
174
  </div>
64
175
  {helpText && (
65
176
  <div
66
177
  className={styles.helpText}
67
- id={id && `${id}__helpText`}
178
+ id={`${id}__helpText`}
68
179
  >
69
180
  {helpText}
70
181
  </div>
@@ -72,13 +183,13 @@ export const FileInputField = React.forwardRef((props, ref) => {
72
183
  {validationText && (
73
184
  <div
74
185
  className={styles.validationText}
75
- id={id && `${id}__validationText`}
186
+ id={`${id}__validationText`}
76
187
  >
77
188
  {validationText}
78
189
  </div>
79
190
  )}
80
191
  </div>
81
- </label>
192
+ </div>
82
193
  );
83
194
  });
84
195
 
@@ -86,10 +197,11 @@ FileInputField.defaultProps = {
86
197
  disabled: false,
87
198
  fullWidth: false,
88
199
  helpText: null,
89
- id: undefined,
90
200
  isLabelVisible: true,
91
201
  layout: 'vertical',
202
+ multiple: false,
92
203
  required: false,
204
+ size: 'medium',
93
205
  validationState: null,
94
206
  validationText: null,
95
207
  };
@@ -116,7 +228,7 @@ FileInputField.propTypes = {
116
228
  * * `<ID>__helpText`
117
229
  * * `<ID>__validationText`
118
230
  */
119
- id: PropTypes.string,
231
+ id: PropTypes.string.isRequired,
120
232
  /**
121
233
  * If `false`, the label will be visually hidden (but remains accessible by assistive
122
234
  * technologies).
@@ -134,10 +246,24 @@ FileInputField.propTypes = {
134
246
  *
135
247
  */
136
248
  layout: PropTypes.oneOf(['horizontal', 'vertical']),
249
+ /**
250
+ * If `true`, the input will accept multiple files.
251
+ */
252
+ multiple: PropTypes.bool,
253
+ /**
254
+ * Callback fired when the value of the input changes.
255
+ */
256
+ onFilesChanged: PropTypes.func.isRequired,
137
257
  /**
138
258
  * If `true`, the input will be required.
139
259
  */
140
260
  required: PropTypes.bool,
261
+ /**
262
+ * Size of the field.
263
+ *
264
+ * Ignored if the component is rendered within `InputGroup` component as the value is inherited in such case.
265
+ */
266
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
141
267
  /**
142
268
  * Alter the field to provide feedback based on validation result.
143
269
  */