@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.
- package/README.md +2 -11
- package/dist/react-ui.css +17 -17
- package/dist/react-ui.development.css +1230 -1053
- package/dist/react-ui.development.js +126 -66
- package/dist/react-ui.js +1 -1
- package/package.json +5 -5
- package/src/components/Alert/Alert.jsx +4 -4
- package/src/components/Alert/README.md +1 -27
- package/src/components/Alert/_settings.scss +1 -2
- package/src/components/Badge/Badge.jsx +2 -2
- package/src/components/Button/Button.jsx +2 -2
- package/src/components/ButtonGroup/ButtonGroup.jsx +2 -2
- package/src/components/Card/Card.jsx +6 -6
- package/src/components/Card/Card.module.scss +2 -2
- package/src/components/Card/CardBody.jsx +1 -1
- package/src/components/Card/CardFooter.jsx +1 -1
- package/src/components/Card/README.md +12 -30
- package/src/components/Card/_settings.scss +1 -2
- package/src/components/Card/_theme.scss +2 -0
- package/src/components/CheckboxField/CheckboxField.jsx +2 -2
- package/src/components/FileInputField/FileInputField.jsx +147 -21
- package/src/components/FileInputField/FileInputField.module.scss +87 -1
- package/src/components/FileInputField/README.md +83 -2
- package/src/components/FileInputField/_settings.scss +15 -0
- package/src/components/FormLayout/FormLayout.jsx +2 -2
- package/src/components/FormLayout/FormLayoutCustomField.jsx +2 -2
- package/src/components/FormLayout/README.md +8 -6
- package/src/components/Grid/Grid.jsx +1 -1
- package/src/components/Grid/Grid.module.scss +2 -2
- package/src/components/Grid/GridSpan.jsx +1 -1
- package/src/components/InputGroup/InputGroup.jsx +2 -2
- package/src/components/InputGroup/InputGroup.module.scss +3 -3
- package/src/components/InputGroup/README.md +1 -1
- package/src/components/Modal/Modal.jsx +117 -45
- package/src/components/Modal/Modal.module.scss +34 -18
- package/src/components/Modal/ModalBody.jsx +2 -2
- package/src/components/Modal/ModalBody.module.scss +18 -0
- package/src/components/Modal/ModalCloseButton.jsx +1 -1
- package/src/components/Modal/ModalContent.jsx +1 -1
- package/src/components/Modal/ModalFooter.jsx +2 -2
- package/src/components/Modal/ModalFooter.module.scss +6 -2
- package/src/components/Modal/ModalHeader.jsx +2 -2
- package/src/components/Modal/ModalHeader.module.scss +8 -1
- package/src/components/Modal/ModalTitle.jsx +1 -1
- package/src/components/Modal/README.md +391 -171
- package/src/components/Modal/_animations.scss +9 -0
- package/src/components/Modal/_helpers/dialogOnCancelHandler.js +28 -0
- package/src/components/Modal/_helpers/dialogOnClickHandler.js +46 -0
- package/src/components/Modal/_helpers/dialogOnCloseHandler.js +28 -0
- package/src/components/Modal/_helpers/dialogOnKeyDownHandler.js +62 -0
- package/src/components/Modal/_helpers/getPositionClassName.js +1 -1
- package/src/components/Modal/_hooks/useModalFocus.js +24 -91
- package/src/components/Modal/_settings.scss +4 -3
- package/src/components/Modal/_theme.scss +1 -0
- package/src/components/Paper/Paper.jsx +2 -2
- package/src/components/Popover/Popover.jsx +2 -2
- package/src/components/Popover/PopoverWrapper.jsx +1 -1
- package/src/components/Radio/Radio.jsx +2 -2
- package/src/components/ScrollView/ScrollView.jsx +2 -2
- package/src/components/SelectField/SelectField.jsx +2 -2
- package/src/components/Table/Table.jsx +1 -1
- package/src/components/Tabs/Tabs.jsx +1 -1
- package/src/components/Tabs/TabsItem.jsx +2 -2
- package/src/components/Text/Text.jsx +2 -2
- package/src/components/TextArea/TextArea.jsx +2 -2
- package/src/components/TextField/TextField.jsx +2 -2
- package/src/components/TextLink/TextLink.jsx +1 -1
- package/src/components/Toggle/Toggle.jsx +2 -2
- package/src/components/Toolbar/Toolbar.jsx +2 -2
- package/src/components/Toolbar/ToolbarGroup.jsx +2 -2
- package/src/components/Toolbar/ToolbarItem.jsx +2 -2
- package/src/helpers/classNames/README.md +65 -0
- package/src/helpers/classNames/classNames.js +11 -0
- package/src/helpers/classNames/index.js +1 -0
- package/src/helpers/transferProps/README.md +46 -0
- package/src/helpers/transferProps/index.js +1 -0
- package/src/index.js +3 -3
- package/src/styles/elements/_links.scss +2 -14
- package/src/styles/generic/_focus.scss +1 -1
- package/src/styles/theme/_form-fields.scss +5 -5
- package/src/styles/tools/_accessibility.scss +3 -5
- package/src/styles/tools/_collections.scss +3 -20
- package/src/styles/tools/_links.scss +17 -0
- package/src/styles/tools/form-fields/_box-field-elements.scss +21 -9
- package/src/styles/tools/form-fields/_box-field-layout.scss +2 -2
- package/src/styles/tools/form-fields/_box-field-sizes.scss +6 -10
- package/src/styles/tools/form-fields/_variants.scss +10 -10
- package/src/theme.scss +53 -3
- package/src/translations/en.js +5 -0
- package/src/styles/settings/_z-indexes.scss +0 -2
- package/src/utils/classNames.js +0 -8
- /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.
|
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.
|
82
|
-
"@testing-library/react": "^16.
|
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 '../../
|
6
|
-
import { transferProps } from '../../
|
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
|
72
|
+
* [Feedback color collection](/docs/foundation/collections#colors).
|
73
73
|
*/
|
74
|
-
color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note'
|
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
|
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:
|
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 '../../
|
5
|
-
import { transferProps } from '../../
|
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 '../../
|
5
|
-
import { transferProps } from '../../
|
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 '../../
|
7
|
-
import { transferProps } from '../../
|
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 '../../
|
5
|
-
import { transferProps } from '../../
|
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:
|
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
|
48
|
+
* [Feedback color collection](/docs/foundation/collections#colors).
|
49
49
|
*/
|
50
|
-
color: PropTypes.oneOf(['success', 'warning', 'danger', 'help', 'info', 'note'
|
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 '../../
|
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 '../../
|
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
|
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'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'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
|
313
|
-
|
314
|
-
| `--rui-Card__padding`
|
315
|
-
| `--rui-Card__border-width`
|
316
|
-
| `--rui-Card__border-
|
317
|
-
| `--rui-
|
318
|
-
| `--rui-
|
319
|
-
| `--rui-Card--
|
320
|
-
| `--rui-Card--
|
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,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 '../../
|
5
|
-
import { transferProps } from '../../
|
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, {
|
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 '../../
|
5
|
-
import { transferProps } from '../../
|
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
|
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
|
-
<
|
103
|
+
<div
|
30
104
|
className={classNames(
|
31
105
|
styles.root,
|
32
106
|
fullWidth && styles.isRootFullWidth,
|
33
|
-
|
34
|
-
resolveContextOrProp(
|
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
|
-
|
42
|
-
|
121
|
+
id={`${id}__root`}
|
122
|
+
onDragLeave={!disabled ? handleDragLeave : undefined}
|
123
|
+
onDragOver={!disabled ? handleDragOver : undefined}
|
124
|
+
onDrop={!disabled ? handleDrop : undefined}
|
43
125
|
>
|
44
|
-
<
|
126
|
+
<label
|
45
127
|
className={classNames(
|
46
128
|
styles.label,
|
47
|
-
!isLabelVisible && styles.isLabelHidden,
|
129
|
+
(!isLabelVisible || inputGroupContext) && styles.isLabelHidden,
|
48
130
|
)}
|
49
|
-
|
131
|
+
htmlFor={id}
|
132
|
+
id={`${id}__labelText`}
|
50
133
|
>
|
51
134
|
{label}
|
52
|
-
</
|
135
|
+
</label>
|
53
136
|
<div className={styles.field}>
|
54
137
|
<div className={styles.inputContainer}>
|
55
138
|
<input
|
56
139
|
{...transferProps(restProps)}
|
57
|
-
|
140
|
+
className={styles.input}
|
141
|
+
disabled={resolveContextOrProp(inputGroupContext && inputGroupContext.disabled, disabled)}
|
58
142
|
id={id}
|
59
|
-
|
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={
|
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={
|
186
|
+
id={`${id}__validationText`}
|
76
187
|
>
|
77
188
|
{validationText}
|
78
189
|
</div>
|
79
190
|
)}
|
80
191
|
</div>
|
81
|
-
</
|
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
|
*/
|