@transferwise/components 0.0.0-experimental-438bbba → 0.0.0-experimental-9d119e3

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 (61) hide show
  1. package/build/card/Card.js.map +1 -1
  2. package/build/card/Card.mjs.map +1 -1
  3. package/build/circularButton/CircularButton.js.map +1 -1
  4. package/build/circularButton/CircularButton.mjs.map +1 -1
  5. package/build/common/locale/index.js.map +1 -1
  6. package/build/common/locale/index.mjs.map +1 -1
  7. package/build/dateLookup/tableLink/TableLink.js.map +1 -1
  8. package/build/dateLookup/tableLink/TableLink.mjs.map +1 -1
  9. package/build/instructionsList/InstructionsList.js.map +1 -1
  10. package/build/instructionsList/InstructionsList.mjs.map +1 -1
  11. package/build/main.css +207 -210
  12. package/build/styles/main.css +207 -210
  13. package/build/styles/uploadInput/UploadInput.css +14 -81
  14. package/build/styles/uploadInput/uploadButton/UploadButton.css +78 -31
  15. package/build/styles/uploadInput/uploadItem/UploadItem.css +126 -109
  16. package/build/types/card/Card.d.ts.map +1 -1
  17. package/build/types/circularButton/CircularButton.d.ts.map +1 -1
  18. package/build/types/instructionsList/InstructionsList.d.ts.map +1 -1
  19. package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
  20. package/build/types/uploadInput/uploadButton/UploadButton.d.ts +6 -1
  21. package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
  22. package/build/types/uploadInput/uploadItem/UploadItem.d.ts +1 -5
  23. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  24. package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts +5 -5
  25. package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts.map +1 -1
  26. package/build/uploadInput/UploadInput.js +28 -38
  27. package/build/uploadInput/UploadInput.js.map +1 -1
  28. package/build/uploadInput/UploadInput.mjs +29 -39
  29. package/build/uploadInput/UploadInput.mjs.map +1 -1
  30. package/build/uploadInput/uploadButton/UploadButton.js +31 -38
  31. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  32. package/build/uploadInput/uploadButton/UploadButton.mjs +32 -39
  33. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  34. package/build/uploadInput/uploadItem/UploadItem.js +33 -56
  35. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  36. package/build/uploadInput/uploadItem/UploadItem.mjs +34 -57
  37. package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
  38. package/build/uploadInput/uploadItem/UploadItemLink.js +5 -7
  39. package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
  40. package/build/uploadInput/uploadItem/UploadItemLink.mjs +5 -7
  41. package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
  42. package/package.json +3 -3
  43. package/src/card/Card.spec.tsx +5 -4
  44. package/src/card/Card.story.tsx +6 -4
  45. package/src/card/Card.tsx +2 -3
  46. package/src/circularButton/CircularButton.tsx +1 -1
  47. package/src/common/locale/index.ts +1 -1
  48. package/src/dateLookup/tableLink/TableLink.tsx +15 -15
  49. package/src/instructionsList/InstructionsList.tsx +4 -1
  50. package/src/main.css +207 -210
  51. package/src/uploadInput/UploadInput.css +14 -81
  52. package/src/uploadInput/UploadInput.less +20 -81
  53. package/src/uploadInput/UploadInput.tests.story.tsx +8 -3
  54. package/src/uploadInput/UploadInput.tsx +41 -68
  55. package/src/uploadInput/uploadButton/UploadButton.css +78 -31
  56. package/src/uploadInput/uploadButton/UploadButton.less +78 -35
  57. package/src/uploadInput/uploadButton/UploadButton.tsx +153 -147
  58. package/src/uploadInput/uploadItem/UploadItem.css +126 -109
  59. package/src/uploadInput/uploadItem/UploadItem.less +123 -118
  60. package/src/uploadInput/uploadItem/UploadItem.tsx +123 -146
  61. package/src/uploadInput/uploadItem/UploadItemLink.tsx +25 -23
@@ -1,160 +1,165 @@
1
1
  @import (reference) "../../../node_modules/@transferwise/neptune-css/src/less/ring.less";
2
2
 
3
- // Be aware of touching styles related to :before/:after pseudo-elements in this file, delimiters between items build by using them
4
- .np-upload-item {
5
- border: 1px solid var(--color-interactive-secondary);
3
+ .np-upload-input__item {
6
4
  position: relative;
5
+ padding: var(--size-16);
6
+ display: flex;
7
+ align-items: flex-start;
8
+ border-left: var(--outerBorder);
9
+ border-right: var(--outerBorder);
7
10
 
8
- &:first-child ~ div:not(.np-upload-item--link):before,
9
- &:not(:first-child).np-upload-item--link .np-upload-item__link:before,
10
- &.np-upload-item--link:hover .np-upload-item__link:after {
11
- display: block;
12
- position: absolute;
13
- height: 1px;
14
- background-color: var(--color-border-neutral);
15
- content: " ";
16
- left: var(--size-16);
17
- width: calc(100% - 2 * var(--size-16));
11
+ &:first-child {
12
+ border-top: var(--outerBorder);
13
+ border-top-left-radius: var(--radius-small);
14
+ border-top-right-radius: var(--radius-small);
18
15
  }
19
16
 
20
- &:first-child ~ div:not(.np-upload-item--link):before,
21
- &:not(:first-child).np-upload-item--link .np-upload-item__link:before {
22
- top: 0;
17
+ & + .np-upload-input__item {
18
+ &:before {
19
+ content: " ";
20
+ display: block;
21
+ position: absolute;
22
+ height: 1px;
23
+ left: var(--size-16);
24
+ width: calc(100% - 2 * var(--size-16));
25
+ top: 0;
26
+ background: var(--color-border-neutral);
27
+ }
23
28
  }
24
29
 
25
- &.np-upload-item--link:hover .np-upload-item__link:after {
26
- bottom: -1px;
27
- }
30
+ .np-upload-input__item-content {
31
+ padding-right: var(--size-32);
32
+ flex: 1;
28
33
 
29
- &:first-child {
30
- & ~ div {
31
- border-top: 1px;
34
+ @media (max-width: 320px) {
35
+ padding-right: var(--size-64);
32
36
  }
33
37
  }
34
38
 
35
- &:not(:first-child) {
36
- .np-upload-item__link:hover {
37
- border-top-color: var(--color-border-neutral);
38
- }
39
+ .np-upload-input__title,
40
+ .np-upload-input__text {
41
+ margin: 0;
42
+ text-align-last: left;
43
+ color: var(--color-content-secondary);
39
44
  }
40
45
 
41
- &:not(:last-child) {
42
- border-bottom: 0;
46
+ .np-upload-input__title + .np-upload-input__text {
47
+ margin-top: var(--size-4);
43
48
  }
44
49
 
45
-
46
- &.np-upload-item--link:hover + .np-upload-item,
47
- &.np-upload-item--link:hover + .np-upload-button-container {
48
- &:before,
49
- .np-upload-item__link:before {
50
- display: none;
51
- }
50
+ .np-upload-input__icon {
51
+ padding-right: var(--size-16);
52
52
  }
53
- }
54
53
 
55
- .np-upload-button-container {
56
- &:hover:before,
57
- &.droppable-dropping:before {
58
- left: 0 !important;
59
- width: 100% !important;
54
+ .np-upload-input__item-link,
55
+ .np-upload-input__item-container {
56
+ align-items: flex-start;
57
+ display: flex;
58
+ width: 100%;
60
59
  }
61
60
 
62
- &:has(:focus-visible) {
63
- .ring();
64
- border-color: transparent;
65
- outline-offset: -3px;
66
- }
67
- }
61
+ .np-upload-input__item-action {
62
+ --iconSize: var(--size-24);
63
+ --clickArea: 44px;
68
64
 
69
- .np-upload-item--single-file:focus-visible,
70
- .np-upload-item__link:focus-visible,
71
- .np-upload-button-container:has(:focus-visible) {
72
- outline-width: 3px;
73
- }
65
+ position: absolute;
66
+ right: var(--size-16);
67
+ top: var(--size-16);
74
68
 
75
- .np-upload-item--link {
76
- a {
77
- flex: 1;
78
- text-decoration: none;
79
- border-top: 1px solid transparent;
80
- border-radius: inherit;
69
+ @media (--screen-400-zoom) {
70
+ --iconSize: var(--size-48);
71
+ }
81
72
 
82
- &:focus-visible {
83
- outline-offset: -2px;
73
+ :before {
74
+ content: '';
75
+ display: block;
76
+ width: var(--clickArea);
77
+ height: var(--clickArea);
78
+ border-radius: 50%;
79
+ position: absolute;
80
+ inset: calc((var(--clickArea) - var(--iconSize)) * -.5)
84
81
  }
85
82
 
86
- &:hover {
87
- &:before {
88
- display: none !important;
83
+ .np-upload-input__item-button {
84
+ appearance: none;
85
+ height: var(--iconSize);
86
+ width: var(--iconSize);
87
+ padding: 0 var(--size-4);
88
+ border-radius: 50%;
89
+ border: 0;
90
+ background-color: var(--color-background-neutral);
91
+ color: var(--color-interactive-primary);
92
+ transition: color, background-color 0.15s ease-in-out;
93
+ outline-offset: 0;
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+
98
+ &:hover {
99
+ background-color: var(--color-sentiment-negative);
100
+ color: var(--color-contrast-overlay) !important;
89
101
  }
90
102
 
91
- &:after {
92
- left: 0 !important;
93
- width: 100% !important;
103
+ &:active {
104
+ background-color: var(--color-background-neutral-active);
94
105
  }
95
106
  }
107
+ }
96
108
 
97
- &:hover,
98
- &:active {
109
+ &.is-interactive {
110
+ padding: 0;
111
+
112
+ &:hover:not(:has(.np-upload-input__item-button:hover)) {
113
+ &:before,
114
+ & + .np-upload-input__item:before {
115
+ width: 100%;
116
+ left: 0;
117
+ }
118
+ }
119
+
120
+ .np-upload-input__item-link {
121
+ padding: var(--size-16);
99
122
  text-decoration: none;
100
- .np-upload-button {
123
+ border-radius: inherit;
124
+ border-top: 1px solid transparent;
125
+ background-clip: padding-box;
126
+
127
+ &:focus-visible {
128
+ outline-offset: -2px;
129
+ outline-width: 3px;
130
+ }
131
+
132
+ &:hover,
133
+ &:active {
101
134
  background-color: var(--color-background-neutral);
102
- border-radius: inherit;
103
135
  }
104
136
  }
105
- }
106
137
 
107
- &:first-of-type a {
108
- border-top: 0;
138
+ &:first-child {
139
+ .np-upload-input__item-link {
140
+ border-top-width: 0;
141
+ }
142
+ }
109
143
  }
110
- }
111
144
 
112
- .np-upload-item__body {
113
- display: flex;
114
- align-items: center;
115
- justify-content: space-between;
116
- // We should have the `relative` position for the body, because we have `absolute` position for `np-upload-item__remove-button` to achieve appropriate `hover` effects
117
- position: relative;
118
- border-radius: inherit;
119
- }
145
+ .np-upload-input-errors {
146
+ padding-left: 0;
147
+ list-style-type: "";
120
148
 
121
- .np-upload-item__remove-button {
122
- display: flex;
123
- align-items: center;
124
- justify-content: center;
125
- align-self: flex-start;
126
- position: absolute;
127
- height: var(--size-24);
128
- min-height: 0;
129
- width: var(--size-24);
130
- padding: 0;
131
- border-radius: 50% !important;
132
- outline-offset: 0 !important;
133
- background-color: var(--color-background-neutral);
134
- border: none;
135
- color: var(--color-interactive-primary);
136
- right: var(--size-16);
137
- top: var(--size-16);
138
- transition: color, background-color 0.15s ease-in-out;
139
-
140
- @media (--screen-400-zoom) {
141
- top: var(--size-16);
142
- right: var(--size-16);
143
- height: var(--size-48);
144
- width: var(--size-48);
145
- }
149
+ & > li {
150
+ position: relative;
151
+ padding-left: var(--size-16);
146
152
 
147
- &:hover {
148
- background-color: var(--color-sentiment-negative);
149
- color: var(--color-contrast-overlay) !important;
150
- }
153
+ &::before {
154
+ content: '•';
155
+ position: absolute;
156
+ display: block;
157
+ left: 0;
158
+ }
151
159
 
152
- &:before {
153
- display: block;
154
- width: 44px;
155
- height: 44px;
156
- content: '';
157
- border-radius: 50%;
158
- position: absolute;
160
+ @media (max-width: 320px) {
161
+ padding-left: var(--size-32);
162
+ }
163
+ }
159
164
  }
160
165
  }
@@ -1,6 +1,5 @@
1
1
  import { Bin, CheckCircleFill, CrossCircleFill } from '@transferwise/icons';
2
2
  import { clsx } from 'clsx';
3
- import { forwardRef, useImperativeHandle, useRef } from 'react';
4
3
  import { useIntl } from 'react-intl';
5
4
 
6
5
  import Body from '../../body';
@@ -28,167 +27,145 @@ export type UploadItemProps = React.JSX.IntrinsicAttributes & {
28
27
  * @param file
29
28
  */
30
29
  onDownload?: (file: UploadedFile) => void;
31
- ref?: React.Ref<UploadItemRef>;
32
30
  };
33
31
 
34
- interface UploadItemRef {
35
- focus: () => void;
36
- }
37
-
38
32
  export enum TEST_IDS {
39
33
  uploadItem = 'uploadItem',
40
34
  mediaBody = 'mediaBody',
41
35
  }
42
36
 
43
- const UploadItem = forwardRef<UploadItemRef, UploadItemProps>(
44
- ({ file, canDelete, onDelete, onDownload, singleFileUpload }, ref) => {
45
- const { formatMessage } = useIntl();
46
- const { status, filename, error, errors, url } = file;
47
- const linkRef = useRef<HTMLAnchorElement>(null);
48
- const buttonRef = useRef<HTMLButtonElement>(null);
49
-
50
- useImperativeHandle<UploadItemRef, UploadItemRef>(ref, () => ({
51
- focus: () => {
52
- if (linkRef.current) {
53
- linkRef.current.focus();
54
- } else if (buttonRef.current) {
55
- buttonRef.current.focus();
56
- }
57
- },
58
- }));
59
-
60
- const isSucceeded = [Status.SUCCEEDED, undefined].includes(status) && !!url;
61
-
62
- /**
63
- * We're temporarily reverting to the regular icon components,
64
- * until the StatusIcon receives 24px sizing. Some misalignment
65
- * to be expected.
66
- */
67
- const getIcon = () => {
68
- if (error || errors?.length || status === Status.FAILED) {
69
- return <CrossCircleFill size={24} className="emphasis--negative" />;
70
- }
71
-
72
- let processIndicator: React.ReactNode;
73
-
74
- switch (status) {
75
- case Status.PROCESSING:
76
- case Status.PENDING:
77
- processIndicator = (
78
- <ProcessIndicator size={Size.EXTRA_SMALL} status={Status.PROCESSING} />
79
- );
80
- break;
81
- case Status.SUCCEEDED:
82
- case Status.DONE:
83
- default:
84
- processIndicator = <CheckCircleFill size={24} className="emphasis--positive" />;
85
- }
86
-
87
- return processIndicator;
88
- };
89
-
90
- const getErrorMessage = (error?: UploadError) =>
91
- typeof error === 'object' ? error.message : error || formatMessage(MESSAGES.uploadingFailed);
92
-
93
- const getMultipleErrors = (errors?: UploadError[]) => {
94
- if (!errors?.length) {
95
- return null;
96
- }
97
-
98
- if (errors?.length === 1) {
99
- return getErrorMessage(errors[0]);
100
- }
37
+ const UploadItem = ({
38
+ file,
39
+ canDelete,
40
+ onDelete,
41
+ onDownload,
42
+ singleFileUpload,
43
+ }: UploadItemProps) => {
44
+ const { formatMessage } = useIntl();
45
+ const { status, filename, error, errors, url } = file;
46
+
47
+ const isSucceeded = [Status.SUCCEEDED, undefined].includes(status) && !!url;
48
+
49
+ /**
50
+ * We're temporarily reverting to the regular icon components,
51
+ * until the StatusIcon receives 24px sizing. Some misalignment
52
+ * to be expected.
53
+ */
54
+ const getIcon = () => {
55
+ if (error || errors?.length || status === Status.FAILED) {
56
+ return <CrossCircleFill size={24} className="emphasis--negative" />;
57
+ }
58
+
59
+ let processIndicator: React.ReactNode;
60
+
61
+ switch (status) {
62
+ case Status.PROCESSING:
63
+ case Status.PENDING:
64
+ processIndicator = <ProcessIndicator size={Size.EXTRA_SMALL} status={Status.PROCESSING} />;
65
+ break;
66
+ case Status.SUCCEEDED:
67
+ case Status.DONE:
68
+ default:
69
+ processIndicator = <CheckCircleFill size={24} className="emphasis--positive" />;
70
+ }
71
+
72
+ return processIndicator;
73
+ };
74
+
75
+ const getErrorMessage = (error?: UploadError) =>
76
+ typeof error === 'object' ? error.message : error || formatMessage(MESSAGES.uploadingFailed);
77
+
78
+ const getMultipleErrors = (errors?: UploadError[]) => {
79
+ if (!errors?.length) {
80
+ return null;
81
+ }
82
+
83
+ if (errors?.length === 1) {
84
+ return getErrorMessage(errors[0]);
85
+ }
86
+
87
+ return (
88
+ <ul className="np-upload-input-errors m-b-0">
89
+ {errors.map((error, index) => {
90
+ // eslint-disable-next-line react/no-array-index-key
91
+ return <li key={index}>{getErrorMessage(error)}</li>;
92
+ })}
93
+ </ul>
94
+ );
95
+ };
101
96
 
97
+ const getDescription = () => {
98
+ if (error || errors?.length || status === Status.FAILED) {
102
99
  return (
103
- <ul className="np-upload-input-errors m-b-0">
104
- {errors.map((error, index) => {
105
- // eslint-disable-next-line react/no-array-index-key
106
- return <li key={index}>{getErrorMessage(error)}</li>;
107
- })}
108
- </ul>
100
+ <Body type={Typography.BODY_DEFAULT_BOLD} className="np-upload-input__text text-negative">
101
+ {errors?.length ? getMultipleErrors(errors) : getErrorMessage(error)}
102
+ </Body>
109
103
  );
110
- };
104
+ }
111
105
 
112
- const getDescription = () => {
113
- if (error || errors?.length || status === Status.FAILED) {
106
+ switch (status) {
107
+ case Status.PENDING:
114
108
  return (
115
- <Body type={Typography.BODY_DEFAULT_BOLD} className="text-negative">
116
- {errors?.length ? getMultipleErrors(errors) : getErrorMessage(error)}
109
+ <Body type={Typography.BODY_DEFAULT} className="np-upload-input__text">
110
+ {formatMessage(MESSAGES.uploading)}
117
111
  </Body>
118
112
  );
119
- }
120
-
121
- switch (status) {
122
- case Status.PENDING:
123
- return (
124
- <Body type={Typography.BODY_DEFAULT_BOLD}>{formatMessage(MESSAGES.uploading)}</Body>
125
- );
126
- case Status.PROCESSING:
127
- return <Body>{formatMessage(MESSAGES.deleting)}</Body>;
128
- case Status.SUCCEEDED:
129
- case Status.DONE:
130
- default:
131
- return (
132
- <Body type={Typography.BODY_DEFAULT_BOLD} className="text-positive">
133
- {formatMessage(MESSAGES.uploaded)}
134
- </Body>
135
- );
136
- }
137
- };
138
-
139
- const getTitle = () => {
140
- return filename || formatMessage(MESSAGES.uploadedFile);
141
- };
142
-
143
- const onDownloadFile = (event: React.MouseEvent): void => {
144
- if (onDownload) {
145
- event.preventDefault();
146
- onDownload(file);
147
- }
148
- };
149
-
150
- return (
151
- <div
152
- className={clsx('np-upload-item', { 'np-upload-item--link': isSucceeded })}
153
- data-testid={TEST_IDS.uploadItem}
113
+ case Status.PROCESSING:
114
+ return <Body className="np-upload-input__text">{formatMessage(MESSAGES.deleting)}</Body>;
115
+ case Status.SUCCEEDED:
116
+ case Status.DONE:
117
+ default:
118
+ return (
119
+ <Body type={Typography.BODY_DEFAULT_BOLD} className="np-upload-input__text">
120
+ {formatMessage(MESSAGES.uploaded)}
121
+ </Body>
122
+ );
123
+ }
124
+ };
125
+
126
+ const getTitle = () => {
127
+ return filename || formatMessage(MESSAGES.uploadedFile);
128
+ };
129
+
130
+ const onDownloadFile = (event: React.MouseEvent): void => {
131
+ if (onDownload) {
132
+ event.preventDefault();
133
+ onDownload(file);
134
+ }
135
+ };
136
+
137
+ return (
138
+ <div
139
+ className={clsx('np-upload-input__item', { 'is-interactive': isSucceeded && url })}
140
+ data-testid={TEST_IDS.uploadItem}
141
+ >
142
+ <UploadItemLink
143
+ url={isSucceeded ? url : undefined}
144
+ singleFileUpload={singleFileUpload}
145
+ onDownload={onDownloadFile}
154
146
  >
155
- <div className="np-upload-item__body">
156
- <UploadItemLink
157
- ref={linkRef}
158
- url={isSucceeded ? url : undefined}
159
- singleFileUpload={singleFileUpload}
160
- onDownload={onDownloadFile}
147
+ <span className="np-upload-input__icon">{getIcon()}</span>
148
+ <div className="np-upload-input__item-content">
149
+ <Body type={Typography.BODY_LARGE} className="np-upload-input__title text-word-break">
150
+ {getTitle()}
151
+ </Body>
152
+ {getDescription()}
153
+ </div>
154
+ </UploadItemLink>
155
+ {canDelete && (
156
+ <div className="np-upload-input__item-action">
157
+ <button
158
+ aria-label={formatMessage(MESSAGES.removeFile, { filename })}
159
+ className="np-upload-input__item-button"
160
+ type="button"
161
+ onClick={() => onDelete()}
161
162
  >
162
- <div className="np-upload-button" aria-live="polite">
163
- <div className="media">
164
- <div className="np-upload-icon media-left">{getIcon()}</div>
165
- <div className="media-body text-xs-left" data-testid={TEST_IDS.mediaBody}>
166
- <Body className="text-word-break d-block text-primary">{getTitle()}</Body>
167
- {getDescription()}
168
- </div>
169
- </div>
170
- </div>
171
- </UploadItemLink>
172
- {canDelete && (
173
- <button
174
- ref={buttonRef}
175
- aria-label={formatMessage(MESSAGES.removeFile, { filename })}
176
- className={clsx('btn', 'np-upload-item__remove-button', 'media-left', {
177
- 'np-upload-item--single-file': singleFileUpload,
178
- })}
179
- type="button"
180
- tabIndex={0}
181
- onClick={() => onDelete()}
182
- >
183
- <Bin size={16} />
184
- </button>
185
- )}
163
+ <Bin size={16} />
164
+ </button>
186
165
  </div>
187
- </div>
188
- );
189
- },
190
- );
191
-
192
- UploadItem.displayName = 'UploadItem';
166
+ )}
167
+ </div>
168
+ );
169
+ };
193
170
 
194
171
  export default UploadItem;
@@ -1,4 +1,4 @@
1
- import { PropsWithChildren, MouseEvent, forwardRef } from 'react';
1
+ import { PropsWithChildren, MouseEvent } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
 
4
4
  type UploadItemLinkProps = PropsWithChildren<{
@@ -7,26 +7,28 @@ type UploadItemLinkProps = PropsWithChildren<{
7
7
  singleFileUpload: boolean;
8
8
  }>;
9
9
 
10
- export const UploadItemLink = forwardRef<HTMLAnchorElement | HTMLDivElement, UploadItemLinkProps>(
11
- ({ children, url, onDownload, singleFileUpload }, ref) => {
12
- if (!url) {
13
- return <div ref={ref as React.RefObject<HTMLDivElement>}>{children}</div>;
14
- }
10
+ export const UploadItemLink = ({
11
+ children,
12
+ url,
13
+ onDownload,
14
+ singleFileUpload,
15
+ }: UploadItemLinkProps) => {
16
+ if (!url) {
17
+ return <div className={clsx('np-upload-input__item-container')}>{children}</div>;
18
+ }
15
19
 
16
- return (
17
- <a
18
- ref={ref as React.RefObject<HTMLAnchorElement>}
19
- href={url}
20
- target="_blank"
21
- rel="noopener noreferrer"
22
- className={clsx(
23
- 'np-upload-item__link',
24
- singleFileUpload ? 'np-upload-item--single-file' : '',
25
- )}
26
- onClick={onDownload}
27
- >
28
- {children}
29
- </a>
30
- );
31
- },
32
- );
20
+ return (
21
+ <a
22
+ href={url}
23
+ target="_blank"
24
+ rel="noopener noreferrer"
25
+ className={clsx(
26
+ 'np-upload-input__item-link',
27
+ singleFileUpload ? 'np-upload-input__item-link--single-file' : '',
28
+ )}
29
+ onClick={onDownload}
30
+ >
31
+ {children}
32
+ </a>
33
+ );
34
+ };