@pareto-engineering/design-system 4.2.3 → 4.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/f/fields/FileUpload/FileUpload.js +32 -29
- package/dist/cjs/f/fields/FileUpload/common/{FilePreview/FilePreview.js → Preview/Preview.js} +64 -25
- package/dist/cjs/f/fields/FileUpload/common/{FilePreview → Preview}/index.js +3 -3
- package/{src/ui/f/fields/FileUpload/common/FilePreview → dist/cjs/f/fields/FileUpload/common/Preview}/styles.scss +9 -6
- package/dist/cjs/f/fields/FileUpload/common/index.js +3 -3
- package/dist/cjs/f/fields/FileUpload/index.js +14 -2
- package/dist/cjs/f/fields/FileUpload/{fileUploadOptions.js → utils.js} +19 -2
- package/dist/cjs/f/fields/index.js +12 -0
- package/dist/cjs/g/FormBuilder/FormBuilder.js +11 -3
- package/dist/cjs/g/FormBuilder/common/Renderer/Renderer.js +6 -2
- package/dist/cjs/g/FormBuilder/common/Renderer/common/Section/Section.js +6 -2
- package/dist/es/f/fields/FileUpload/FileUpload.js +32 -30
- package/dist/es/f/fields/FileUpload/common/Preview/Preview.js +119 -0
- package/dist/es/f/fields/FileUpload/common/{FilePreview → Preview}/index.js +1 -1
- package/dist/{cjs/f/fields/FileUpload/common/FilePreview → es/f/fields/FileUpload/common/Preview}/styles.scss +9 -6
- package/dist/es/f/fields/FileUpload/common/index.js +1 -1
- package/dist/es/f/fields/FileUpload/index.js +1 -1
- package/dist/es/f/fields/FileUpload/{fileUploadOptions.js → utils.js} +16 -1
- package/dist/es/f/fields/index.js +1 -1
- package/dist/es/g/FormBuilder/FormBuilder.js +11 -3
- package/dist/es/g/FormBuilder/common/Renderer/Renderer.js +6 -2
- package/dist/es/g/FormBuilder/common/Renderer/common/Section/Section.js +6 -2
- package/package.json +2 -2
- package/src/stories/f/FileUpload.stories.jsx +13 -0
- package/src/ui/f/fields/FileUpload/FileUpload.jsx +25 -26
- package/src/ui/f/fields/FileUpload/common/Preview/Preview.jsx +185 -0
- package/src/ui/f/fields/FileUpload/common/{FilePreview → Preview}/index.js +1 -1
- package/{dist/es/f/fields/FileUpload/common/FilePreview → src/ui/f/fields/FileUpload/common/Preview}/styles.scss +9 -6
- package/src/ui/f/fields/FileUpload/common/index.js +1 -1
- package/src/ui/f/fields/FileUpload/index.js +1 -1
- package/src/ui/f/fields/FileUpload/{fileUploadOptions.js → utils.js} +20 -0
- package/src/ui/f/fields/index.js +6 -1
- package/src/ui/g/FormBuilder/FormBuilder.jsx +9 -0
- package/src/ui/g/FormBuilder/common/Renderer/Renderer.jsx +4 -0
- package/src/ui/g/FormBuilder/common/Renderer/common/Section/Section.jsx +4 -0
- package/tests/__snapshots__/Storyshots.test.js.snap +65 -0
- package/dist/es/f/fields/FileUpload/common/FilePreview/FilePreview.js +0 -80
- package/src/ui/f/fields/FileUpload/common/FilePreview/FilePreview.jsx +0 -115
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import styleNames from '@pareto-engineering/bem/exports';
|
|
5
|
+
import { LoadingCircle, ProgressBar, Tooltip } from "../../../../../a";
|
|
6
|
+
|
|
7
|
+
// Local Definitions
|
|
8
|
+
|
|
9
|
+
import "./styles.scss";
|
|
10
|
+
import { Button } from "../../../../../b";
|
|
11
|
+
import { getFileType, getFileTypeFromUrl } from "../../utils";
|
|
12
|
+
const baseClassName = styleNames.base;
|
|
13
|
+
const componentClassName = 'file-preview';
|
|
14
|
+
const statusColorMap = {
|
|
15
|
+
pending: 'yellow',
|
|
16
|
+
error: 'red',
|
|
17
|
+
success: 'green'
|
|
18
|
+
};
|
|
19
|
+
const FILE_UPLOAD_COMPLETE_PROGRESS = 100;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This is the component description.
|
|
23
|
+
*/
|
|
24
|
+
const Preview = ({
|
|
25
|
+
id,
|
|
26
|
+
className: userClassName,
|
|
27
|
+
style,
|
|
28
|
+
file,
|
|
29
|
+
handleDelete,
|
|
30
|
+
uploadStatus,
|
|
31
|
+
color,
|
|
32
|
+
uploadProgress,
|
|
33
|
+
writeOnly
|
|
34
|
+
// ...otherProps
|
|
35
|
+
}) => {
|
|
36
|
+
const isFileObject = file instanceof File;
|
|
37
|
+
const type = isFileObject ? getFileType(file) : getFileTypeFromUrl(file?.url);
|
|
38
|
+
const status = uploadStatus?.status;
|
|
39
|
+
const statusColor = statusColorMap[status] ?? color;
|
|
40
|
+
const isPending = status === 'pending';
|
|
41
|
+
// isPreview means the file is uploaded and previewed while other files can still be uploaded
|
|
42
|
+
const isSuccess = status === 'success' || file?.isPreview;
|
|
43
|
+
const handlePreview = () => {
|
|
44
|
+
if (!isFileObject) {
|
|
45
|
+
window.open(file.url, '_blank');
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const canDelete = file?.isPreview || isFileObject;
|
|
49
|
+
const isFileBroken = !writeOnly && !file?.url;
|
|
50
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
51
|
+
id: id,
|
|
52
|
+
className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
|
|
53
|
+
style: style
|
|
54
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
55
|
+
className: "identity"
|
|
56
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
57
|
+
className: ['type', type.toLowerCase()].filter(e => e).join(' ')
|
|
58
|
+
}, type), /*#__PURE__*/React.createElement("span", {
|
|
59
|
+
title: file.name,
|
|
60
|
+
className: "name"
|
|
61
|
+
}, file.name), isFileBroken ? /*#__PURE__*/React.createElement(Tooltip, {
|
|
62
|
+
description: "bulk-updates-text",
|
|
63
|
+
content: /*#__PURE__*/React.createElement("p", null, "This file doesn't have a link or the link is invalid.")
|
|
64
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
65
|
+
className: "icon x-ui-icons c-x"
|
|
66
|
+
}, "h")) : /*#__PURE__*/React.createElement(Button, {
|
|
67
|
+
color: "ui-icons",
|
|
68
|
+
isCompact: true,
|
|
69
|
+
isSimple: true,
|
|
70
|
+
onClick: () => canDelete ? handleDelete() : handlePreview()
|
|
71
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
72
|
+
className: "icon"
|
|
73
|
+
}, canDelete ? 'Y' : '9'))), writeOnly && /*#__PURE__*/React.createElement("div", {
|
|
74
|
+
className: `progress-status x-${statusColor}`
|
|
75
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
76
|
+
className: "status"
|
|
77
|
+
}, isPending ? /*#__PURE__*/React.createElement(LoadingCircle, {
|
|
78
|
+
className: `x-${statusColor}`,
|
|
79
|
+
heightWidth: "1rem",
|
|
80
|
+
color: statusColor
|
|
81
|
+
}) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
82
|
+
className: "icon"
|
|
83
|
+
}, isSuccess ? 'I' : 'Y'), /*#__PURE__*/React.createElement("span", null, isSuccess ? 'Uploaded' : 'Error'))), /*#__PURE__*/React.createElement(ProgressBar, {
|
|
84
|
+
color: statusColor,
|
|
85
|
+
progress: isPending ? uploadProgress : FILE_UPLOAD_COMPLETE_PROGRESS,
|
|
86
|
+
height: "3px"
|
|
87
|
+
})));
|
|
88
|
+
};
|
|
89
|
+
Preview.propTypes = {
|
|
90
|
+
/**
|
|
91
|
+
* The HTML id for this element
|
|
92
|
+
*/
|
|
93
|
+
id: PropTypes.string,
|
|
94
|
+
/**
|
|
95
|
+
* The HTML class names for this element
|
|
96
|
+
*/
|
|
97
|
+
className: PropTypes.string,
|
|
98
|
+
/**
|
|
99
|
+
* The React-written, css properties for this element.
|
|
100
|
+
*/
|
|
101
|
+
style: PropTypes.objectOf(PropTypes.string),
|
|
102
|
+
/**
|
|
103
|
+
* The base color of the file preview
|
|
104
|
+
*/
|
|
105
|
+
color: PropTypes.string,
|
|
106
|
+
/**
|
|
107
|
+
* The upload progress of the file
|
|
108
|
+
*/
|
|
109
|
+
uploadProgress: PropTypes.number,
|
|
110
|
+
/**
|
|
111
|
+
* Whether the preview is used in a context where the user can upload new files
|
|
112
|
+
*/
|
|
113
|
+
writeOnly: PropTypes.bool
|
|
114
|
+
};
|
|
115
|
+
Preview.defaultProps = {
|
|
116
|
+
color: 'green',
|
|
117
|
+
uploadProgress: 45
|
|
118
|
+
};
|
|
119
|
+
export default Preview;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
|
-
export { default as
|
|
2
|
+
export { default as Preview } from "./Preview";
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@use "@pareto-engineering/bem";
|
|
4
4
|
|
|
5
5
|
$default-padding: 1rem;
|
|
6
|
+
$default-margin: 1rem;
|
|
6
7
|
$files-per-row: 3;
|
|
7
8
|
$total-gaps: $files-per-row - 1;
|
|
8
9
|
$combined-gap: calc(var(--gap) * $total-gaps);
|
|
@@ -22,13 +23,16 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
22
23
|
align-items: center;
|
|
23
24
|
display: flex;
|
|
24
25
|
gap: calc(var(--gap) / 2);
|
|
26
|
+
justify-content: space-between;
|
|
25
27
|
|
|
26
28
|
> .type {
|
|
27
29
|
padding-block: calc($default-padding / 4);
|
|
28
30
|
padding-inline: calc($default-padding / 2);
|
|
29
31
|
|
|
30
32
|
// TODO: update the colors to use variables. These are colors from the new DS
|
|
31
|
-
&.pdf
|
|
33
|
+
&.pdf,
|
|
34
|
+
&.broken,
|
|
35
|
+
&.unknown {
|
|
32
36
|
background-color: #fdead7;
|
|
33
37
|
color: #b93814;
|
|
34
38
|
}
|
|
@@ -67,7 +71,6 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
> .#{bem.$base}.button {
|
|
70
|
-
cursor: pointer;
|
|
71
74
|
margin-left: auto;
|
|
72
75
|
}
|
|
73
76
|
}
|
|
@@ -75,15 +78,15 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
75
78
|
> .progress-status {
|
|
76
79
|
> .status {
|
|
77
80
|
align-items: center;
|
|
78
|
-
|
|
79
|
-
color: var(--green);
|
|
81
|
+
color: var(--x);
|
|
80
82
|
display: flex;
|
|
81
83
|
gap: calc(var(--gap) / 2);
|
|
84
|
+
margin-bottom: calc($default-margin / 4);
|
|
82
85
|
|
|
83
86
|
> .icon {
|
|
84
|
-
background-color: var(--
|
|
87
|
+
background-color: var(--x);
|
|
85
88
|
border-radius: 50%;
|
|
86
|
-
color: var(--on-
|
|
89
|
+
color: var(--on-x);
|
|
87
90
|
font-size: calc(var(--s-2) * 1rem);
|
|
88
91
|
padding: calc($default-padding / 4);
|
|
89
92
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
|
-
export {
|
|
2
|
+
export { Preview } from "./Preview";
|
|
@@ -22,4 +22,19 @@ export const fileUploadOptions = [{
|
|
|
22
22
|
key: 'AUD',
|
|
23
23
|
value: '.mp3,.ogg,.wav,audio/mpeg,audio/ogg,audio/wav',
|
|
24
24
|
label: 'Audio'
|
|
25
|
-
}];
|
|
25
|
+
}];
|
|
26
|
+
export const getFileType = file => {
|
|
27
|
+
const mimeType = file?.type;
|
|
28
|
+
const extension = file?.name?.split('.').pop().toLowerCase();
|
|
29
|
+
const fileType = fileUploadOptions.find(option => option.value.includes(mimeType) || option.value.includes(extension));
|
|
30
|
+
return fileType ? fileType.key : 'unknown';
|
|
31
|
+
};
|
|
32
|
+
export const getFileTypeFromUrl = url => {
|
|
33
|
+
if (!url) {
|
|
34
|
+
return 'BROKEN';
|
|
35
|
+
}
|
|
36
|
+
const urlWithoutParams = url.split('?')[0];
|
|
37
|
+
const extension = urlWithoutParams.split('.').pop().toLowerCase();
|
|
38
|
+
const fileType = fileUploadOptions.find(option => option.value.includes(extension));
|
|
39
|
+
return fileType ? fileType.key : 'UNKNOWN';
|
|
40
|
+
};
|
|
@@ -9,4 +9,4 @@ export { Checkbox } from "./Checkbox";
|
|
|
9
9
|
export { QueryChoices } from "./QueryChoices";
|
|
10
10
|
export { LinkInput } from "./LinkInput";
|
|
11
11
|
export { EditorInput } from "./EditorInput";
|
|
12
|
-
export { FileUpload, fileUploadOptions } from "./FileUpload";
|
|
12
|
+
export { FileUpload, fileUploadOptions, getFileType, getFileTypeFromUrl } from "./FileUpload";
|
|
@@ -27,7 +27,9 @@ const FormBuilder = ({
|
|
|
27
27
|
onRendererError,
|
|
28
28
|
onRendererFormSave,
|
|
29
29
|
onBuilderValidate,
|
|
30
|
-
initialBuilderValues
|
|
30
|
+
initialBuilderValues,
|
|
31
|
+
fileUploadStatus,
|
|
32
|
+
handleFileDelete
|
|
31
33
|
// ...otherProps
|
|
32
34
|
}) => /*#__PURE__*/React.createElement("div", {
|
|
33
35
|
id: id,
|
|
@@ -45,7 +47,9 @@ const FormBuilder = ({
|
|
|
45
47
|
onSave: onRendererFormSave,
|
|
46
48
|
readOnly: readOnly,
|
|
47
49
|
shouldSubmit: shouldSubmit,
|
|
48
|
-
onError: onRendererError
|
|
50
|
+
onError: onRendererError,
|
|
51
|
+
fileUploadStatus: fileUploadStatus,
|
|
52
|
+
handleFileDelete: handleFileDelete
|
|
49
53
|
}));
|
|
50
54
|
FormBuilder.propTypes = {
|
|
51
55
|
/**
|
|
@@ -87,7 +91,11 @@ FormBuilder.propTypes = {
|
|
|
87
91
|
/**
|
|
88
92
|
* Whether the form builder in renderer mode should submit the form values itself
|
|
89
93
|
*/
|
|
90
|
-
shouldSubmit: PropTypes.bool
|
|
94
|
+
shouldSubmit: PropTypes.bool,
|
|
95
|
+
/**
|
|
96
|
+
* The upload status of files if any
|
|
97
|
+
*/
|
|
98
|
+
fileUploadStatus: PropTypes.objectOf(PropTypes.string)
|
|
91
99
|
};
|
|
92
100
|
FormBuilder.defaultProps = {
|
|
93
101
|
mode: 'build',
|
|
@@ -51,7 +51,9 @@ const Renderer = ({
|
|
|
51
51
|
readOnly,
|
|
52
52
|
onSave,
|
|
53
53
|
onError,
|
|
54
|
-
shouldSubmit
|
|
54
|
+
shouldSubmit,
|
|
55
|
+
fileUploadStatus,
|
|
56
|
+
handleFileDelete
|
|
55
57
|
// ...otherProps
|
|
56
58
|
}) => {
|
|
57
59
|
const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
|
|
@@ -124,7 +126,9 @@ const Renderer = ({
|
|
|
124
126
|
key: `${section.title}`
|
|
125
127
|
}, section, {
|
|
126
128
|
readOnly: readOnly,
|
|
127
|
-
setUpdatedFormData: setUpdatedFormData
|
|
129
|
+
setUpdatedFormData: setUpdatedFormData,
|
|
130
|
+
fileUploadStatus: fileUploadStatus,
|
|
131
|
+
handleFileDelete: handleFileDelete
|
|
128
132
|
}))), /*#__PURE__*/React.createElement("div", {
|
|
129
133
|
className: "navigator-container"
|
|
130
134
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
@@ -22,7 +22,9 @@ const Section = ({
|
|
|
22
22
|
title,
|
|
23
23
|
description,
|
|
24
24
|
inputs,
|
|
25
|
-
readOnly
|
|
25
|
+
readOnly,
|
|
26
|
+
fileUploadStatus,
|
|
27
|
+
handleFileDelete
|
|
26
28
|
// ...otherProps
|
|
27
29
|
}) => /*#__PURE__*/React.createElement("div", {
|
|
28
30
|
id: id,
|
|
@@ -36,7 +38,9 @@ const Section = ({
|
|
|
36
38
|
}), inputs?.map(input => /*#__PURE__*/React.createElement(FormInput, _extends({
|
|
37
39
|
key: input.name
|
|
38
40
|
}, input, {
|
|
39
|
-
disabled: readOnly
|
|
41
|
+
disabled: readOnly,
|
|
42
|
+
uploadStatus: fileUploadStatus,
|
|
43
|
+
handleFileDelete: handleFileDelete
|
|
40
44
|
}))));
|
|
41
45
|
Section.propTypes = {
|
|
42
46
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pareto-engineering/design-system",
|
|
3
|
-
"version": "4.2
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/es/index.js",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"relay-test-utils": "^15.0.0"
|
|
83
83
|
},
|
|
84
84
|
"browserslist": "> 2%",
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "6e334c8fc47b8d7fc8448c25a587a5b059085a32"
|
|
86
86
|
}
|
|
@@ -53,3 +53,16 @@ export const WithLimitedCount = {
|
|
|
53
53
|
maxCount:2,
|
|
54
54
|
},
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
const file = {
|
|
58
|
+
id :'101568dad8544564901830661183e281',
|
|
59
|
+
name :'test.tar',
|
|
60
|
+
url :'https://forte-development-assets.pareto.ai/task/73/101568dad8544564901830661183e281.tar?Expires=1725387927&Signature=D8wIYnCQKyS6kkZbumgHHJ8fXnciwz1f31YGpKUH47InfPFDqGTDpEDub4xsjDPlxlDWomNj05xJs~uiaef49ZuRnPKRgcUFzw86Pn9A81Rw5gawfmuIpJPaUIkhG6VpsmH5pKnnEX2jWb0t5OqD~jMiL1aQU8f7HS241E9qdLy1kG9BHAHYR8IxSz9mHeZD3k~cjVW3I4QtB1PuN3nOgLH1ebBHI0Y1bk6kyVT1Bihz0bpbdg6o5KkoV6DKqLUGbsxvYliWOX8XCjkoDujtl8hDnTFS6Hix75a9idf3qVbTA7IDhoGodzVRw8MbyIHashxQ5-5XRF0mAblJr~2QTQ__&Key-Pair-Id=K3IY7IU3GPUR0N',
|
|
61
|
+
writeOnly:false,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const Preview = {
|
|
65
|
+
render:() => (
|
|
66
|
+
<FileUpload.Preview file={file} />
|
|
67
|
+
),
|
|
68
|
+
}
|
|
@@ -9,7 +9,7 @@ import styleNames from '@pareto-engineering/bem/exports'
|
|
|
9
9
|
|
|
10
10
|
import './styles.scss'
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { Preview } from './common'
|
|
13
13
|
|
|
14
14
|
import { FormLabel, FormDescription } from '../../common'
|
|
15
15
|
|
|
@@ -37,16 +37,13 @@ const FileUpload = ({
|
|
|
37
37
|
maxSize,
|
|
38
38
|
maxSizeError,
|
|
39
39
|
onChange,
|
|
40
|
-
|
|
41
|
-
// TODO: TBD when connecting with BE
|
|
42
|
-
// onPreview,
|
|
43
|
-
// progress,
|
|
44
|
-
// percent,
|
|
45
|
-
// uploadStatus,
|
|
40
|
+
uploadStatus,
|
|
46
41
|
filePreviewAlignment,
|
|
47
42
|
optional,
|
|
48
43
|
labelColor,
|
|
49
44
|
color,
|
|
45
|
+
viewOnly,
|
|
46
|
+
handleFileDelete,
|
|
50
47
|
// ...otherProps
|
|
51
48
|
}) => {
|
|
52
49
|
const [field, , helpers] = useField({ name })
|
|
@@ -92,11 +89,11 @@ const FileUpload = ({
|
|
|
92
89
|
event.target.value = null
|
|
93
90
|
}
|
|
94
91
|
|
|
95
|
-
const handleDelete = (fileIndex) => {
|
|
96
|
-
const updatedFiles = value
|
|
92
|
+
const handleDelete = ({ fileIndex, identifier }) => {
|
|
93
|
+
const updatedFiles = value?.filter((_, i) => i !== fileIndex)
|
|
97
94
|
setValue(updatedFiles)
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
handleFileDelete?.({ fileIndex, identifier })
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
const acceptOptions = Array.isArray(accept) ? accept?.join(',') : accept
|
|
@@ -119,7 +116,7 @@ const FileUpload = ({
|
|
|
119
116
|
>
|
|
120
117
|
<p>{label}</p>
|
|
121
118
|
<FormDescription className="s-1" description={description} name={name} />
|
|
122
|
-
{!
|
|
119
|
+
{!viewOnly && (
|
|
123
120
|
<FormLabel
|
|
124
121
|
name={name}
|
|
125
122
|
color={labelColor}
|
|
@@ -147,11 +144,15 @@ const FileUpload = ({
|
|
|
147
144
|
<p>Attached files:</p>
|
|
148
145
|
<div className="files">
|
|
149
146
|
{value.map((file, indx) => (
|
|
150
|
-
<
|
|
147
|
+
<Preview
|
|
151
148
|
file={file}
|
|
152
149
|
key={file.name}
|
|
153
|
-
|
|
154
|
-
handleDelete={() => handleDelete(
|
|
150
|
+
writeOnly={!disabled}
|
|
151
|
+
handleDelete={() => handleDelete({
|
|
152
|
+
fileIndex :indx,
|
|
153
|
+
identifier:file?.id || file.name,
|
|
154
|
+
})}
|
|
155
|
+
uploadStatus={uploadStatus?.[file?.name]}
|
|
155
156
|
/>
|
|
156
157
|
))}
|
|
157
158
|
</div>
|
|
@@ -245,33 +246,31 @@ FileUpload.propTypes = {
|
|
|
245
246
|
/**
|
|
246
247
|
* The function to handle file delete events
|
|
247
248
|
*/
|
|
248
|
-
|
|
249
|
+
handleFileDelete:PropTypes.func,
|
|
249
250
|
|
|
250
251
|
/**
|
|
251
|
-
* The
|
|
252
|
+
* The upload status of the file with the file name as the key
|
|
252
253
|
*/
|
|
253
|
-
|
|
254
|
+
uploadStatus:PropTypes.objectOf(PropTypes.string),
|
|
254
255
|
|
|
255
256
|
/**
|
|
256
|
-
* The
|
|
257
|
-
*/
|
|
258
|
-
// onPreview:PropTypes.func,
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* The upload status of the file if being sent to an external service (e.g., 'uploading', 'done')
|
|
257
|
+
* The alignment for file previews WRT the upload input
|
|
262
258
|
*/
|
|
263
|
-
|
|
259
|
+
filePreviewAlignment:PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
|
|
264
260
|
|
|
265
261
|
/**
|
|
266
|
-
*
|
|
262
|
+
* Whether the file upload is in view only mode
|
|
267
263
|
*/
|
|
268
|
-
|
|
264
|
+
viewOnly:PropTypes.bool,
|
|
269
265
|
}
|
|
270
266
|
|
|
271
267
|
FileUpload.defaultProps = {
|
|
272
268
|
disabled :false,
|
|
273
269
|
color :'paragraph',
|
|
274
270
|
filePreviewAlignment:'bottom',
|
|
271
|
+
viewOnly :false,
|
|
275
272
|
}
|
|
276
273
|
|
|
274
|
+
FileUpload.Preview = Preview
|
|
275
|
+
|
|
277
276
|
export default FileUpload
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
|
|
6
|
+
import styleNames from '@pareto-engineering/bem/exports'
|
|
7
|
+
|
|
8
|
+
import { LoadingCircle, ProgressBar, Tooltip } from 'ui/a'
|
|
9
|
+
|
|
10
|
+
// Local Definitions
|
|
11
|
+
|
|
12
|
+
import './styles.scss'
|
|
13
|
+
|
|
14
|
+
import { Button } from 'ui/b'
|
|
15
|
+
|
|
16
|
+
import { getFileType, getFileTypeFromUrl } from '../../utils'
|
|
17
|
+
|
|
18
|
+
const baseClassName = styleNames.base
|
|
19
|
+
|
|
20
|
+
const componentClassName = 'file-preview'
|
|
21
|
+
|
|
22
|
+
const statusColorMap = {
|
|
23
|
+
pending:'yellow',
|
|
24
|
+
error :'red',
|
|
25
|
+
success:'green',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const FILE_UPLOAD_COMPLETE_PROGRESS = 100
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* This is the component description.
|
|
32
|
+
*/
|
|
33
|
+
const Preview = ({
|
|
34
|
+
id,
|
|
35
|
+
className:userClassName,
|
|
36
|
+
style,
|
|
37
|
+
file,
|
|
38
|
+
handleDelete,
|
|
39
|
+
uploadStatus,
|
|
40
|
+
color,
|
|
41
|
+
uploadProgress,
|
|
42
|
+
writeOnly,
|
|
43
|
+
// ...otherProps
|
|
44
|
+
}) => {
|
|
45
|
+
const isFileObject = file instanceof File
|
|
46
|
+
const type = isFileObject ? getFileType(file) : getFileTypeFromUrl(file?.url)
|
|
47
|
+
|
|
48
|
+
const status = uploadStatus?.status
|
|
49
|
+
const statusColor = statusColorMap[status] ?? color
|
|
50
|
+
|
|
51
|
+
const isPending = status === 'pending'
|
|
52
|
+
// isPreview means the file is uploaded and previewed while other files can still be uploaded
|
|
53
|
+
const isSuccess = status === 'success' || file?.isPreview
|
|
54
|
+
|
|
55
|
+
const handlePreview = () => {
|
|
56
|
+
if (!isFileObject) {
|
|
57
|
+
window.open(file.url, '_blank')
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const canDelete = file?.isPreview || isFileObject
|
|
62
|
+
|
|
63
|
+
const isFileBroken = !writeOnly && !file?.url
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
id={id}
|
|
68
|
+
className={[
|
|
69
|
+
|
|
70
|
+
baseClassName,
|
|
71
|
+
|
|
72
|
+
componentClassName,
|
|
73
|
+
userClassName,
|
|
74
|
+
]
|
|
75
|
+
.filter((e) => e)
|
|
76
|
+
.join(' ')}
|
|
77
|
+
style={style}
|
|
78
|
+
>
|
|
79
|
+
<div className="identity">
|
|
80
|
+
<span
|
|
81
|
+
className={
|
|
82
|
+
[
|
|
83
|
+
'type',
|
|
84
|
+
type.toLowerCase(),
|
|
85
|
+
]
|
|
86
|
+
.filter((e) => e)
|
|
87
|
+
.join(' ')
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
{type}
|
|
91
|
+
</span>
|
|
92
|
+
<span title={file.name} className="name">{file.name}</span>
|
|
93
|
+
{isFileBroken
|
|
94
|
+
? (
|
|
95
|
+
<Tooltip
|
|
96
|
+
description="bulk-updates-text"
|
|
97
|
+
content={(
|
|
98
|
+
<p>
|
|
99
|
+
This file doesn't have a link or the link is invalid.
|
|
100
|
+
</p>
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
<span className="icon x-ui-icons c-x">
|
|
104
|
+
h
|
|
105
|
+
</span>
|
|
106
|
+
</Tooltip>
|
|
107
|
+
)
|
|
108
|
+
: (
|
|
109
|
+
<Button
|
|
110
|
+
color="ui-icons"
|
|
111
|
+
isCompact
|
|
112
|
+
isSimple
|
|
113
|
+
onClick={() => (canDelete ? handleDelete() : handlePreview())}
|
|
114
|
+
>
|
|
115
|
+
<span className="icon">{canDelete ? 'Y' : '9'}</span>
|
|
116
|
+
</Button>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
{writeOnly && (
|
|
120
|
+
<div className={`progress-status x-${statusColor}`}>
|
|
121
|
+
<div className="status">
|
|
122
|
+
{isPending ? (
|
|
123
|
+
<LoadingCircle
|
|
124
|
+
className={`x-${statusColor}`}
|
|
125
|
+
heightWidth="1rem"
|
|
126
|
+
color={statusColor}
|
|
127
|
+
/>
|
|
128
|
+
) : (
|
|
129
|
+
<>
|
|
130
|
+
<span className="icon">
|
|
131
|
+
{isSuccess ? 'I' : 'Y'}
|
|
132
|
+
</span>
|
|
133
|
+
<span>{isSuccess ? 'Uploaded' : 'Error'}</span>
|
|
134
|
+
</>
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
<ProgressBar
|
|
138
|
+
color={statusColor}
|
|
139
|
+
progress={isPending ? uploadProgress : FILE_UPLOAD_COMPLETE_PROGRESS}
|
|
140
|
+
height="3px"
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
Preview.propTypes = {
|
|
149
|
+
/**
|
|
150
|
+
* The HTML id for this element
|
|
151
|
+
*/
|
|
152
|
+
id:PropTypes.string,
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The HTML class names for this element
|
|
156
|
+
*/
|
|
157
|
+
className:PropTypes.string,
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The React-written, css properties for this element.
|
|
161
|
+
*/
|
|
162
|
+
style:PropTypes.objectOf(PropTypes.string),
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The base color of the file preview
|
|
166
|
+
*/
|
|
167
|
+
color:PropTypes.string,
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The upload progress of the file
|
|
171
|
+
*/
|
|
172
|
+
uploadProgress:PropTypes.number,
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Whether the preview is used in a context where the user can upload new files
|
|
176
|
+
*/
|
|
177
|
+
writeOnly:PropTypes.bool,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Preview.defaultProps = {
|
|
181
|
+
color :'green',
|
|
182
|
+
uploadProgress:45,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default Preview
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/* @pareto-engineering/generator-front 1.1.1-alpha.2 */
|
|
2
|
-
export { default as
|
|
2
|
+
export { default as Preview } from './Preview'
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@use "@pareto-engineering/bem";
|
|
4
4
|
|
|
5
5
|
$default-padding: 1rem;
|
|
6
|
+
$default-margin: 1rem;
|
|
6
7
|
$files-per-row: 3;
|
|
7
8
|
$total-gaps: $files-per-row - 1;
|
|
8
9
|
$combined-gap: calc(var(--gap) * $total-gaps);
|
|
@@ -22,13 +23,16 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
22
23
|
align-items: center;
|
|
23
24
|
display: flex;
|
|
24
25
|
gap: calc(var(--gap) / 2);
|
|
26
|
+
justify-content: space-between;
|
|
25
27
|
|
|
26
28
|
> .type {
|
|
27
29
|
padding-block: calc($default-padding / 4);
|
|
28
30
|
padding-inline: calc($default-padding / 2);
|
|
29
31
|
|
|
30
32
|
// TODO: update the colors to use variables. These are colors from the new DS
|
|
31
|
-
&.pdf
|
|
33
|
+
&.pdf,
|
|
34
|
+
&.broken,
|
|
35
|
+
&.unknown {
|
|
32
36
|
background-color: #fdead7;
|
|
33
37
|
color: #b93814;
|
|
34
38
|
}
|
|
@@ -67,7 +71,6 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
> .#{bem.$base}.button {
|
|
70
|
-
cursor: pointer;
|
|
71
74
|
margin-left: auto;
|
|
72
75
|
}
|
|
73
76
|
}
|
|
@@ -75,15 +78,15 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
75
78
|
> .progress-status {
|
|
76
79
|
> .status {
|
|
77
80
|
align-items: center;
|
|
78
|
-
|
|
79
|
-
color: var(--green);
|
|
81
|
+
color: var(--x);
|
|
80
82
|
display: flex;
|
|
81
83
|
gap: calc(var(--gap) / 2);
|
|
84
|
+
margin-bottom: calc($default-margin / 4);
|
|
82
85
|
|
|
83
86
|
> .icon {
|
|
84
|
-
background-color: var(--
|
|
87
|
+
background-color: var(--x);
|
|
85
88
|
border-radius: 50%;
|
|
86
|
-
color: var(--on-
|
|
89
|
+
color: var(--on-x);
|
|
87
90
|
font-size: calc(var(--s-2) * 1rem);
|
|
88
91
|
padding: calc($default-padding / 4);
|
|
89
92
|
}
|