@pareto-engineering/design-system 4.2.3 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/f/fields/FileUpload/FileUpload.js +38 -32
- package/dist/cjs/f/fields/FileUpload/common/FilePreview/FilePreview.js +46 -18
- package/dist/cjs/f/fields/FileUpload/common/FilePreview/styles.scss +5 -4
- package/dist/cjs/f/fields/FileUpload/index.js +14 -2
- package/dist/cjs/f/fields/FileUpload/{fileUploadOptions.js → utils.js} +16 -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 +37 -32
- package/dist/es/f/fields/FileUpload/common/FilePreview/FilePreview.js +46 -18
- package/dist/es/f/fields/FileUpload/common/FilePreview/styles.scss +5 -4
- package/dist/es/f/fields/FileUpload/index.js +1 -1
- package/dist/es/f/fields/FileUpload/{fileUploadOptions.js → utils.js} +13 -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/ui/f/fields/FileUpload/FileUpload.jsx +32 -30
- package/src/ui/f/fields/FileUpload/common/FilePreview/FilePreview.jsx +64 -19
- package/src/ui/f/fields/FileUpload/common/FilePreview/styles.scss +5 -4
- package/src/ui/f/fields/FileUpload/index.js +1 -1
- package/src/ui/f/fields/FileUpload/{fileUploadOptions.js → utils.js} +17 -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 +22 -0
|
@@ -40,16 +40,13 @@ const FileUpload = _ref => {
|
|
|
40
40
|
maxSize,
|
|
41
41
|
maxSizeError,
|
|
42
42
|
onChange,
|
|
43
|
-
|
|
44
|
-
// TODO: TBD when connecting with BE
|
|
45
|
-
// onPreview,
|
|
46
|
-
// progress,
|
|
47
|
-
// percent,
|
|
48
|
-
// uploadStatus,
|
|
43
|
+
uploadStatus,
|
|
49
44
|
filePreviewAlignment,
|
|
50
45
|
optional,
|
|
51
46
|
labelColor,
|
|
52
|
-
color
|
|
47
|
+
color,
|
|
48
|
+
viewOnly,
|
|
49
|
+
handleFileDelete
|
|
53
50
|
// ...otherProps
|
|
54
51
|
} = _ref;
|
|
55
52
|
const [field,, helpers] = (0, _formik.useField)({
|
|
@@ -91,10 +88,17 @@ const FileUpload = _ref => {
|
|
|
91
88
|
// eslint-disable-next-line no-param-reassign
|
|
92
89
|
event.target.value = null;
|
|
93
90
|
};
|
|
94
|
-
const handleDelete =
|
|
95
|
-
|
|
91
|
+
const handleDelete = _ref2 => {
|
|
92
|
+
let {
|
|
93
|
+
fileIndex,
|
|
94
|
+
identifier
|
|
95
|
+
} = _ref2;
|
|
96
|
+
const updatedFiles = value?.filter((_, i) => i !== fileIndex);
|
|
96
97
|
setValue(updatedFiles);
|
|
97
|
-
|
|
98
|
+
handleFileDelete?.({
|
|
99
|
+
fileIndex,
|
|
100
|
+
identifier
|
|
101
|
+
});
|
|
98
102
|
};
|
|
99
103
|
const acceptOptions = Array.isArray(accept) ? accept?.join(',') : accept;
|
|
100
104
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -105,7 +109,7 @@ const FileUpload = _ref => {
|
|
|
105
109
|
className: "s-1",
|
|
106
110
|
description: description,
|
|
107
111
|
name: name
|
|
108
|
-
}), !
|
|
112
|
+
}), !viewOnly && /*#__PURE__*/React.createElement(_common2.FormLabel, {
|
|
109
113
|
name: name,
|
|
110
114
|
color: labelColor,
|
|
111
115
|
optional: optional
|
|
@@ -123,12 +127,20 @@ const FileUpload = _ref => {
|
|
|
123
127
|
className: "attached-files"
|
|
124
128
|
}, /*#__PURE__*/React.createElement("p", null, "Attached files:"), /*#__PURE__*/React.createElement("div", {
|
|
125
129
|
className: "files"
|
|
126
|
-
}, value.map((file, indx) =>
|
|
127
|
-
file
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
}, value.map((file, indx) => {
|
|
131
|
+
const isFileObject = file instanceof File;
|
|
132
|
+
return /*#__PURE__*/React.createElement(_common.FilePreview, {
|
|
133
|
+
file: file,
|
|
134
|
+
key: isFileObject ? file.name : file,
|
|
135
|
+
disabled: disabled,
|
|
136
|
+
handleDelete: () => handleDelete({
|
|
137
|
+
fileIndex: indx,
|
|
138
|
+
identifier: isFileObject ? file.name : file?.id
|
|
139
|
+
}),
|
|
140
|
+
uploadStatus: uploadStatus?.[file?.name],
|
|
141
|
+
viewOnly: viewOnly
|
|
142
|
+
});
|
|
143
|
+
}))));
|
|
132
144
|
};
|
|
133
145
|
FileUpload.propTypes = {
|
|
134
146
|
/**
|
|
@@ -199,30 +211,24 @@ FileUpload.propTypes = {
|
|
|
199
211
|
/**
|
|
200
212
|
* The function to handle file delete events
|
|
201
213
|
*/
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* The function to handle file download events
|
|
205
|
-
*/
|
|
206
|
-
// onDownload:PropTypes.func,
|
|
207
|
-
|
|
214
|
+
handleFileDelete: _propTypes.default.func,
|
|
208
215
|
/**
|
|
209
|
-
* The
|
|
216
|
+
* The upload status of the file with the file name as the key
|
|
210
217
|
*/
|
|
211
|
-
|
|
212
|
-
|
|
218
|
+
uploadStatus: _propTypes.default.objectOf(_propTypes.default.string),
|
|
213
219
|
/**
|
|
214
|
-
* The
|
|
220
|
+
* The alignment for file previews WRT the upload input
|
|
215
221
|
*/
|
|
216
|
-
|
|
217
|
-
|
|
222
|
+
filePreviewAlignment: _propTypes.default.oneOf(['left', 'right', 'top', 'bottom']),
|
|
218
223
|
/**
|
|
219
|
-
*
|
|
224
|
+
* Whether the file upload is in view only mode
|
|
220
225
|
*/
|
|
221
|
-
|
|
226
|
+
viewOnly: _propTypes.default.bool
|
|
222
227
|
};
|
|
223
228
|
FileUpload.defaultProps = {
|
|
224
229
|
disabled: false,
|
|
225
230
|
color: 'paragraph',
|
|
226
|
-
filePreviewAlignment: 'bottom'
|
|
231
|
+
filePreviewAlignment: 'bottom',
|
|
232
|
+
viewOnly: false
|
|
227
233
|
};
|
|
228
234
|
var _default = exports.default = FileUpload;
|
|
@@ -7,10 +7,10 @@ exports.default = void 0;
|
|
|
7
7
|
var React = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
9
|
var _exports = _interopRequireDefault(require("@pareto-engineering/bem/exports"));
|
|
10
|
-
require("./styles.scss");
|
|
11
10
|
var _a = require("../../../../../a");
|
|
11
|
+
require("./styles.scss");
|
|
12
12
|
var _b = require("../../../../../b");
|
|
13
|
-
var
|
|
13
|
+
var _utils = require("../../utils");
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
16
16
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -20,12 +20,12 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
|
|
|
20
20
|
|
|
21
21
|
const baseClassName = _exports.default.base;
|
|
22
22
|
const componentClassName = 'file-preview';
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return fileType ? fileType.key : 'unknown';
|
|
23
|
+
const statusColorMap = {
|
|
24
|
+
pending: 'yellow',
|
|
25
|
+
error: 'red',
|
|
26
|
+
success: 'green'
|
|
28
27
|
};
|
|
28
|
+
const FILE_UPLOAD_COMPLETE_PROGRESS = 100;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* This is the component description.
|
|
@@ -37,10 +37,25 @@ const FilePreview = _ref => {
|
|
|
37
37
|
style,
|
|
38
38
|
file,
|
|
39
39
|
disabled,
|
|
40
|
-
handleDelete
|
|
40
|
+
handleDelete,
|
|
41
|
+
uploadStatus,
|
|
42
|
+
color,
|
|
43
|
+
uploadProgress
|
|
41
44
|
// ...otherProps
|
|
42
45
|
} = _ref;
|
|
43
|
-
const
|
|
46
|
+
const isFileObject = file instanceof File;
|
|
47
|
+
const type = isFileObject ? (0, _utils.getFileType)(file) : (0, _utils.getFileTypeFromUrl)(file?.url);
|
|
48
|
+
const status = uploadStatus?.status;
|
|
49
|
+
const statusColor = statusColorMap[status] ?? color;
|
|
50
|
+
const isPending = status === 'pending';
|
|
51
|
+
// isPreview means the file is uploaded and previewed while other files can still be uploaded
|
|
52
|
+
const isSuccess = status === 'success' || file?.isPreview;
|
|
53
|
+
const handlePreview = () => {
|
|
54
|
+
if (!isFileObject) {
|
|
55
|
+
window.open(file.url, '_blank');
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const canDelete = file?.isPreview || isFileObject;
|
|
44
59
|
return /*#__PURE__*/React.createElement("div", {
|
|
45
60
|
id: id,
|
|
46
61
|
className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
|
|
@@ -55,18 +70,22 @@ const FilePreview = _ref => {
|
|
|
55
70
|
}, file.name), /*#__PURE__*/React.createElement(_b.Button, {
|
|
56
71
|
isCompact: true,
|
|
57
72
|
isSimple: true,
|
|
58
|
-
onClick: handleDelete
|
|
73
|
+
onClick: () => canDelete ? handleDelete() : handlePreview()
|
|
59
74
|
}, /*#__PURE__*/React.createElement("span", {
|
|
60
75
|
className: "icon x-ui-icons c-x"
|
|
61
|
-
},
|
|
62
|
-
className:
|
|
76
|
+
}, canDelete ? 'Y' : '9'))), !disabled && /*#__PURE__*/React.createElement("div", {
|
|
77
|
+
className: `progress-status x-${statusColor}`
|
|
63
78
|
}, /*#__PURE__*/React.createElement("div", {
|
|
64
79
|
className: "status"
|
|
65
|
-
}, /*#__PURE__*/React.createElement(
|
|
80
|
+
}, isPending ? /*#__PURE__*/React.createElement(_a.LoadingCircle, {
|
|
81
|
+
className: `x-${statusColor}`,
|
|
82
|
+
heightWidth: "1rem",
|
|
83
|
+
color: statusColor
|
|
84
|
+
}) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
66
85
|
className: "icon"
|
|
67
|
-
},
|
|
68
|
-
color:
|
|
69
|
-
progress:
|
|
86
|
+
}, isSuccess ? 'I' : 'Y'), /*#__PURE__*/React.createElement("span", null, isSuccess ? 'Uploaded' : 'Error'))), /*#__PURE__*/React.createElement(_a.ProgressBar, {
|
|
87
|
+
color: statusColor,
|
|
88
|
+
progress: isPending ? uploadProgress : FILE_UPLOAD_COMPLETE_PROGRESS,
|
|
70
89
|
height: "3px"
|
|
71
90
|
})));
|
|
72
91
|
};
|
|
@@ -82,9 +101,18 @@ FilePreview.propTypes = {
|
|
|
82
101
|
/**
|
|
83
102
|
* The React-written, css properties for this element.
|
|
84
103
|
*/
|
|
85
|
-
style: _propTypes.default.objectOf(_propTypes.default.string)
|
|
104
|
+
style: _propTypes.default.objectOf(_propTypes.default.string),
|
|
105
|
+
/**
|
|
106
|
+
* The base color of the file preview
|
|
107
|
+
*/
|
|
108
|
+
color: _propTypes.default.string,
|
|
109
|
+
/**
|
|
110
|
+
* The upload progress of the file
|
|
111
|
+
*/
|
|
112
|
+
uploadProgress: _propTypes.default.number
|
|
86
113
|
};
|
|
87
114
|
FilePreview.defaultProps = {
|
|
88
|
-
|
|
115
|
+
color: 'green',
|
|
116
|
+
uploadProgress: 45
|
|
89
117
|
};
|
|
90
118
|
var _default = exports.default = FilePreview;
|
|
@@ -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);
|
|
@@ -75,15 +76,15 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
75
76
|
> .progress-status {
|
|
76
77
|
> .status {
|
|
77
78
|
align-items: center;
|
|
78
|
-
|
|
79
|
-
color: var(--green);
|
|
79
|
+
color: var(--x);
|
|
80
80
|
display: flex;
|
|
81
81
|
gap: calc(var(--gap) / 2);
|
|
82
|
+
margin-bottom: calc($default-margin / 4);
|
|
82
83
|
|
|
83
84
|
> .icon {
|
|
84
|
-
background-color: var(--
|
|
85
|
+
background-color: var(--x);
|
|
85
86
|
border-radius: 50%;
|
|
86
|
-
color: var(--on-
|
|
87
|
+
color: var(--on-x);
|
|
87
88
|
font-size: calc(var(--s-2) * 1rem);
|
|
88
89
|
padding: calc($default-padding / 4);
|
|
89
90
|
}
|
|
@@ -12,9 +12,21 @@ Object.defineProperty(exports, "FileUpload", {
|
|
|
12
12
|
Object.defineProperty(exports, "fileUploadOptions", {
|
|
13
13
|
enumerable: true,
|
|
14
14
|
get: function () {
|
|
15
|
-
return
|
|
15
|
+
return _utils.fileUploadOptions;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "getFileType", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _utils.getFileType;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "getFileTypeFromUrl", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _utils.getFileTypeFromUrl;
|
|
16
28
|
}
|
|
17
29
|
});
|
|
18
30
|
var _FileUpload = _interopRequireDefault(require("./FileUpload"));
|
|
19
|
-
var
|
|
31
|
+
var _utils = require("./utils");
|
|
20
32
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.fileUploadOptions = void 0;
|
|
6
|
+
exports.getFileTypeFromUrl = exports.getFileType = exports.fileUploadOptions = void 0;
|
|
7
7
|
const fileUploadOptions = exports.fileUploadOptions = [{
|
|
8
8
|
key: 'FILE',
|
|
9
9
|
value: '.doc,.docx,.odt,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.text',
|
|
@@ -28,4 +28,18 @@ const fileUploadOptions = exports.fileUploadOptions = [{
|
|
|
28
28
|
key: 'AUD',
|
|
29
29
|
value: '.mp3,.ogg,.wav,audio/mpeg,audio/ogg,audio/wav',
|
|
30
30
|
label: 'Audio'
|
|
31
|
-
}];
|
|
31
|
+
}];
|
|
32
|
+
const getFileType = file => {
|
|
33
|
+
const mimeType = file?.type;
|
|
34
|
+
const extension = file?.name?.split('.').pop().toLowerCase();
|
|
35
|
+
const fileType = fileUploadOptions.find(option => option.value.includes(mimeType) || option.value.includes(extension));
|
|
36
|
+
return fileType ? fileType.key : 'unknown';
|
|
37
|
+
};
|
|
38
|
+
exports.getFileType = getFileType;
|
|
39
|
+
const getFileTypeFromUrl = url => {
|
|
40
|
+
const urlWithoutParams = url.split('?')[0];
|
|
41
|
+
const extension = urlWithoutParams.split('.').pop().toLowerCase();
|
|
42
|
+
const fileType = fileUploadOptions.find(option => option.value.includes(extension));
|
|
43
|
+
return fileType ? fileType.key : 'unknown';
|
|
44
|
+
};
|
|
45
|
+
exports.getFileTypeFromUrl = getFileTypeFromUrl;
|
|
@@ -81,6 +81,18 @@ Object.defineProperty(exports, "fileUploadOptions", {
|
|
|
81
81
|
return _FileUpload.fileUploadOptions;
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
|
+
Object.defineProperty(exports, "getFileType", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function () {
|
|
87
|
+
return _FileUpload.getFileType;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
Object.defineProperty(exports, "getFileTypeFromUrl", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
get: function () {
|
|
93
|
+
return _FileUpload.getFileTypeFromUrl;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
84
96
|
var _TextInput = require("./TextInput");
|
|
85
97
|
var _SelectInput = require("./SelectInput");
|
|
86
98
|
var _ChoicesInput = require("./ChoicesInput");
|
|
@@ -37,7 +37,9 @@ const FormBuilder = _ref => {
|
|
|
37
37
|
onRendererError,
|
|
38
38
|
onRendererFormSave,
|
|
39
39
|
onBuilderValidate,
|
|
40
|
-
initialBuilderValues
|
|
40
|
+
initialBuilderValues,
|
|
41
|
+
fileUploadStatus,
|
|
42
|
+
handleFileDelete
|
|
41
43
|
// ...otherProps
|
|
42
44
|
} = _ref;
|
|
43
45
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -56,7 +58,9 @@ const FormBuilder = _ref => {
|
|
|
56
58
|
onSave: onRendererFormSave,
|
|
57
59
|
readOnly: readOnly,
|
|
58
60
|
shouldSubmit: shouldSubmit,
|
|
59
|
-
onError: onRendererError
|
|
61
|
+
onError: onRendererError,
|
|
62
|
+
fileUploadStatus: fileUploadStatus,
|
|
63
|
+
handleFileDelete: handleFileDelete
|
|
60
64
|
}));
|
|
61
65
|
};
|
|
62
66
|
FormBuilder.propTypes = {
|
|
@@ -99,7 +103,11 @@ FormBuilder.propTypes = {
|
|
|
99
103
|
/**
|
|
100
104
|
* Whether the form builder in renderer mode should submit the form values itself
|
|
101
105
|
*/
|
|
102
|
-
shouldSubmit: _propTypes.default.bool
|
|
106
|
+
shouldSubmit: _propTypes.default.bool,
|
|
107
|
+
/**
|
|
108
|
+
* The upload status of files if any
|
|
109
|
+
*/
|
|
110
|
+
fileUploadStatus: _propTypes.default.objectOf(_propTypes.default.string)
|
|
103
111
|
};
|
|
104
112
|
FormBuilder.defaultProps = {
|
|
105
113
|
mode: 'build',
|
|
@@ -57,7 +57,9 @@ const Renderer = _ref => {
|
|
|
57
57
|
readOnly,
|
|
58
58
|
onSave,
|
|
59
59
|
onError,
|
|
60
|
-
shouldSubmit
|
|
60
|
+
shouldSubmit,
|
|
61
|
+
fileUploadStatus,
|
|
62
|
+
handleFileDelete
|
|
61
63
|
// ...otherProps
|
|
62
64
|
} = _ref;
|
|
63
65
|
const [currentSectionIndex, setCurrentSectionIndex] = (0, _react.useState)(0);
|
|
@@ -131,7 +133,9 @@ const Renderer = _ref => {
|
|
|
131
133
|
key: `${section.title}`
|
|
132
134
|
}, section, {
|
|
133
135
|
readOnly: readOnly,
|
|
134
|
-
setUpdatedFormData: setUpdatedFormData
|
|
136
|
+
setUpdatedFormData: setUpdatedFormData,
|
|
137
|
+
fileUploadStatus: fileUploadStatus,
|
|
138
|
+
handleFileDelete: handleFileDelete
|
|
135
139
|
}))), /*#__PURE__*/React.createElement("div", {
|
|
136
140
|
className: "navigator-container"
|
|
137
141
|
}, /*#__PURE__*/React.createElement(_b.Button, {
|
|
@@ -27,7 +27,9 @@ const Section = _ref => {
|
|
|
27
27
|
title,
|
|
28
28
|
description,
|
|
29
29
|
inputs,
|
|
30
|
-
readOnly
|
|
30
|
+
readOnly,
|
|
31
|
+
fileUploadStatus,
|
|
32
|
+
handleFileDelete
|
|
31
33
|
// ...otherProps
|
|
32
34
|
} = _ref;
|
|
33
35
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -42,7 +44,9 @@ const Section = _ref => {
|
|
|
42
44
|
}), inputs?.map(input => /*#__PURE__*/React.createElement(_f.FormInput, _extends({
|
|
43
45
|
key: input.name
|
|
44
46
|
}, input, {
|
|
45
|
-
disabled: readOnly
|
|
47
|
+
disabled: readOnly,
|
|
48
|
+
uploadStatus: fileUploadStatus,
|
|
49
|
+
handleFileDelete: handleFileDelete
|
|
46
50
|
}))));
|
|
47
51
|
};
|
|
48
52
|
Section.propTypes = {
|
|
@@ -30,16 +30,13 @@ const FileUpload = ({
|
|
|
30
30
|
maxSize,
|
|
31
31
|
maxSizeError,
|
|
32
32
|
onChange,
|
|
33
|
-
|
|
34
|
-
// TODO: TBD when connecting with BE
|
|
35
|
-
// onPreview,
|
|
36
|
-
// progress,
|
|
37
|
-
// percent,
|
|
38
|
-
// uploadStatus,
|
|
33
|
+
uploadStatus,
|
|
39
34
|
filePreviewAlignment,
|
|
40
35
|
optional,
|
|
41
36
|
labelColor,
|
|
42
|
-
color
|
|
37
|
+
color,
|
|
38
|
+
viewOnly,
|
|
39
|
+
handleFileDelete
|
|
43
40
|
// ...otherProps
|
|
44
41
|
}) => {
|
|
45
42
|
const [field,, helpers] = useField({
|
|
@@ -81,10 +78,16 @@ const FileUpload = ({
|
|
|
81
78
|
// eslint-disable-next-line no-param-reassign
|
|
82
79
|
event.target.value = null;
|
|
83
80
|
};
|
|
84
|
-
const handleDelete =
|
|
85
|
-
|
|
81
|
+
const handleDelete = ({
|
|
82
|
+
fileIndex,
|
|
83
|
+
identifier
|
|
84
|
+
}) => {
|
|
85
|
+
const updatedFiles = value?.filter((_, i) => i !== fileIndex);
|
|
86
86
|
setValue(updatedFiles);
|
|
87
|
-
|
|
87
|
+
handleFileDelete?.({
|
|
88
|
+
fileIndex,
|
|
89
|
+
identifier
|
|
90
|
+
});
|
|
88
91
|
};
|
|
89
92
|
const acceptOptions = Array.isArray(accept) ? accept?.join(',') : accept;
|
|
90
93
|
return /*#__PURE__*/React.createElement("div", {
|
|
@@ -95,7 +98,7 @@ const FileUpload = ({
|
|
|
95
98
|
className: "s-1",
|
|
96
99
|
description: description,
|
|
97
100
|
name: name
|
|
98
|
-
}), !
|
|
101
|
+
}), !viewOnly && /*#__PURE__*/React.createElement(FormLabel, {
|
|
99
102
|
name: name,
|
|
100
103
|
color: labelColor,
|
|
101
104
|
optional: optional
|
|
@@ -113,12 +116,20 @@ const FileUpload = ({
|
|
|
113
116
|
className: "attached-files"
|
|
114
117
|
}, /*#__PURE__*/React.createElement("p", null, "Attached files:"), /*#__PURE__*/React.createElement("div", {
|
|
115
118
|
className: "files"
|
|
116
|
-
}, value.map((file, indx) =>
|
|
117
|
-
file
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
}, value.map((file, indx) => {
|
|
120
|
+
const isFileObject = file instanceof File;
|
|
121
|
+
return /*#__PURE__*/React.createElement(FilePreview, {
|
|
122
|
+
file: file,
|
|
123
|
+
key: isFileObject ? file.name : file,
|
|
124
|
+
disabled: disabled,
|
|
125
|
+
handleDelete: () => handleDelete({
|
|
126
|
+
fileIndex: indx,
|
|
127
|
+
identifier: isFileObject ? file.name : file?.id
|
|
128
|
+
}),
|
|
129
|
+
uploadStatus: uploadStatus?.[file?.name],
|
|
130
|
+
viewOnly: viewOnly
|
|
131
|
+
});
|
|
132
|
+
}))));
|
|
122
133
|
};
|
|
123
134
|
FileUpload.propTypes = {
|
|
124
135
|
/**
|
|
@@ -189,30 +200,24 @@ FileUpload.propTypes = {
|
|
|
189
200
|
/**
|
|
190
201
|
* The function to handle file delete events
|
|
191
202
|
*/
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* The function to handle file download events
|
|
195
|
-
*/
|
|
196
|
-
// onDownload:PropTypes.func,
|
|
197
|
-
|
|
203
|
+
handleFileDelete: PropTypes.func,
|
|
198
204
|
/**
|
|
199
|
-
* The
|
|
205
|
+
* The upload status of the file with the file name as the key
|
|
200
206
|
*/
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
uploadStatus: PropTypes.objectOf(PropTypes.string),
|
|
203
208
|
/**
|
|
204
|
-
* The
|
|
209
|
+
* The alignment for file previews WRT the upload input
|
|
205
210
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
filePreviewAlignment: PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
|
|
208
212
|
/**
|
|
209
|
-
*
|
|
213
|
+
* Whether the file upload is in view only mode
|
|
210
214
|
*/
|
|
211
|
-
|
|
215
|
+
viewOnly: PropTypes.bool
|
|
212
216
|
};
|
|
213
217
|
FileUpload.defaultProps = {
|
|
214
218
|
disabled: false,
|
|
215
219
|
color: 'paragraph',
|
|
216
|
-
filePreviewAlignment: 'bottom'
|
|
220
|
+
filePreviewAlignment: 'bottom',
|
|
221
|
+
viewOnly: false
|
|
217
222
|
};
|
|
218
223
|
export default FileUpload;
|
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import styleNames from '@pareto-engineering/bem/exports';
|
|
5
|
+
import { LoadingCircle, ProgressBar } from "../../../../../a";
|
|
5
6
|
|
|
6
7
|
// Local Definitions
|
|
7
8
|
|
|
8
9
|
import "./styles.scss";
|
|
9
|
-
import { ProgressBar } from "../../../../../a";
|
|
10
10
|
import { Button } from "../../../../../b";
|
|
11
|
-
import {
|
|
11
|
+
import { getFileType, getFileTypeFromUrl } from "../../utils";
|
|
12
12
|
const baseClassName = styleNames.base;
|
|
13
13
|
const componentClassName = 'file-preview';
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return fileType ? fileType.key : 'unknown';
|
|
14
|
+
const statusColorMap = {
|
|
15
|
+
pending: 'yellow',
|
|
16
|
+
error: 'red',
|
|
17
|
+
success: 'green'
|
|
19
18
|
};
|
|
19
|
+
const FILE_UPLOAD_COMPLETE_PROGRESS = 100;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* This is the component description.
|
|
@@ -27,10 +27,25 @@ const FilePreview = ({
|
|
|
27
27
|
style,
|
|
28
28
|
file,
|
|
29
29
|
disabled,
|
|
30
|
-
handleDelete
|
|
30
|
+
handleDelete,
|
|
31
|
+
uploadStatus,
|
|
32
|
+
color,
|
|
33
|
+
uploadProgress
|
|
31
34
|
// ...otherProps
|
|
32
35
|
}) => {
|
|
33
|
-
const
|
|
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;
|
|
34
49
|
return /*#__PURE__*/React.createElement("div", {
|
|
35
50
|
id: id,
|
|
36
51
|
className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
|
|
@@ -45,18 +60,22 @@ const FilePreview = ({
|
|
|
45
60
|
}, file.name), /*#__PURE__*/React.createElement(Button, {
|
|
46
61
|
isCompact: true,
|
|
47
62
|
isSimple: true,
|
|
48
|
-
onClick: handleDelete
|
|
63
|
+
onClick: () => canDelete ? handleDelete() : handlePreview()
|
|
49
64
|
}, /*#__PURE__*/React.createElement("span", {
|
|
50
65
|
className: "icon x-ui-icons c-x"
|
|
51
|
-
},
|
|
52
|
-
className:
|
|
66
|
+
}, canDelete ? 'Y' : '9'))), !disabled && /*#__PURE__*/React.createElement("div", {
|
|
67
|
+
className: `progress-status x-${statusColor}`
|
|
53
68
|
}, /*#__PURE__*/React.createElement("div", {
|
|
54
69
|
className: "status"
|
|
55
|
-
}, /*#__PURE__*/React.createElement(
|
|
70
|
+
}, isPending ? /*#__PURE__*/React.createElement(LoadingCircle, {
|
|
71
|
+
className: `x-${statusColor}`,
|
|
72
|
+
heightWidth: "1rem",
|
|
73
|
+
color: statusColor
|
|
74
|
+
}) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
|
56
75
|
className: "icon"
|
|
57
|
-
},
|
|
58
|
-
color:
|
|
59
|
-
progress:
|
|
76
|
+
}, isSuccess ? 'I' : 'Y'), /*#__PURE__*/React.createElement("span", null, isSuccess ? 'Uploaded' : 'Error'))), /*#__PURE__*/React.createElement(ProgressBar, {
|
|
77
|
+
color: statusColor,
|
|
78
|
+
progress: isPending ? uploadProgress : FILE_UPLOAD_COMPLETE_PROGRESS,
|
|
60
79
|
height: "3px"
|
|
61
80
|
})));
|
|
62
81
|
};
|
|
@@ -72,9 +91,18 @@ FilePreview.propTypes = {
|
|
|
72
91
|
/**
|
|
73
92
|
* The React-written, css properties for this element.
|
|
74
93
|
*/
|
|
75
|
-
style: PropTypes.objectOf(PropTypes.string)
|
|
94
|
+
style: PropTypes.objectOf(PropTypes.string),
|
|
95
|
+
/**
|
|
96
|
+
* The base color of the file preview
|
|
97
|
+
*/
|
|
98
|
+
color: PropTypes.string,
|
|
99
|
+
/**
|
|
100
|
+
* The upload progress of the file
|
|
101
|
+
*/
|
|
102
|
+
uploadProgress: PropTypes.number
|
|
76
103
|
};
|
|
77
104
|
FilePreview.defaultProps = {
|
|
78
|
-
|
|
105
|
+
color: 'green',
|
|
106
|
+
uploadProgress: 45
|
|
79
107
|
};
|
|
80
108
|
export default FilePreview;
|
|
@@ -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);
|
|
@@ -75,15 +76,15 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
75
76
|
> .progress-status {
|
|
76
77
|
> .status {
|
|
77
78
|
align-items: center;
|
|
78
|
-
|
|
79
|
-
color: var(--green);
|
|
79
|
+
color: var(--x);
|
|
80
80
|
display: flex;
|
|
81
81
|
gap: calc(var(--gap) / 2);
|
|
82
|
+
margin-bottom: calc($default-margin / 4);
|
|
82
83
|
|
|
83
84
|
> .icon {
|
|
84
|
-
background-color: var(--
|
|
85
|
+
background-color: var(--x);
|
|
85
86
|
border-radius: 50%;
|
|
86
|
-
color: var(--on-
|
|
87
|
+
color: var(--on-x);
|
|
87
88
|
font-size: calc(var(--s-2) * 1rem);
|
|
88
89
|
padding: calc($default-padding / 4);
|
|
89
90
|
}
|
|
@@ -22,4 +22,16 @@ 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
|
+
const urlWithoutParams = url.split('?')[0];
|
|
34
|
+
const extension = urlWithoutParams.split('.').pop().toLowerCase();
|
|
35
|
+
const fileType = fileUploadOptions.find(option => option.value.includes(extension));
|
|
36
|
+
return fileType ? fileType.key : 'unknown';
|
|
37
|
+
};
|
|
@@ -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.
|
|
3
|
+
"version": "4.3.0",
|
|
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": "f9d0eb4bf3a4d825b2da863d3a27c8d4d25ee818"
|
|
86
86
|
}
|
|
@@ -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}
|
|
@@ -146,14 +143,23 @@ const FileUpload = ({
|
|
|
146
143
|
<div className="attached-files">
|
|
147
144
|
<p>Attached files:</p>
|
|
148
145
|
<div className="files">
|
|
149
|
-
{value.map((file, indx) =>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
{value.map((file, indx) => {
|
|
147
|
+
const isFileObject = file instanceof File
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<FilePreview
|
|
151
|
+
file={file}
|
|
152
|
+
key={isFileObject ? file.name : file}
|
|
153
|
+
disabled={disabled}
|
|
154
|
+
handleDelete={() => handleDelete({
|
|
155
|
+
fileIndex :indx,
|
|
156
|
+
identifier:isFileObject ? file.name : file?.id,
|
|
157
|
+
})}
|
|
158
|
+
uploadStatus={uploadStatus?.[file?.name]}
|
|
159
|
+
viewOnly={viewOnly}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
162
|
+
})}
|
|
157
163
|
</div>
|
|
158
164
|
</div>
|
|
159
165
|
)}
|
|
@@ -245,33 +251,29 @@ FileUpload.propTypes = {
|
|
|
245
251
|
/**
|
|
246
252
|
* The function to handle file delete events
|
|
247
253
|
*/
|
|
248
|
-
|
|
254
|
+
handleFileDelete:PropTypes.func,
|
|
249
255
|
|
|
250
256
|
/**
|
|
251
|
-
* The
|
|
257
|
+
* The upload status of the file with the file name as the key
|
|
252
258
|
*/
|
|
253
|
-
|
|
259
|
+
uploadStatus:PropTypes.objectOf(PropTypes.string),
|
|
254
260
|
|
|
255
261
|
/**
|
|
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')
|
|
262
|
+
* The alignment for file previews WRT the upload input
|
|
262
263
|
*/
|
|
263
|
-
|
|
264
|
+
filePreviewAlignment:PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
|
|
264
265
|
|
|
265
266
|
/**
|
|
266
|
-
*
|
|
267
|
+
* Whether the file upload is in view only mode
|
|
267
268
|
*/
|
|
268
|
-
|
|
269
|
+
viewOnly:PropTypes.bool,
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
FileUpload.defaultProps = {
|
|
272
273
|
disabled :false,
|
|
273
274
|
color :'paragraph',
|
|
274
275
|
filePreviewAlignment:'bottom',
|
|
276
|
+
viewOnly :false,
|
|
275
277
|
}
|
|
276
278
|
|
|
277
279
|
export default FileUpload
|
|
@@ -5,29 +5,28 @@ import PropTypes from 'prop-types'
|
|
|
5
5
|
|
|
6
6
|
import styleNames from '@pareto-engineering/bem/exports'
|
|
7
7
|
|
|
8
|
+
import { LoadingCircle, ProgressBar } from 'ui/a'
|
|
9
|
+
|
|
8
10
|
// Local Definitions
|
|
9
11
|
|
|
10
12
|
import './styles.scss'
|
|
11
13
|
|
|
12
|
-
import { ProgressBar } from 'ui/a'
|
|
13
|
-
|
|
14
14
|
import { Button } from 'ui/b'
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { getFileType, getFileTypeFromUrl } from '../../utils'
|
|
17
17
|
|
|
18
18
|
const baseClassName = styleNames.base
|
|
19
19
|
|
|
20
20
|
const componentClassName = 'file-preview'
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(option) => option.value.includes(mimeType) || option.value.includes(extension))
|
|
27
|
-
|
|
28
|
-
return fileType ? fileType.key : 'unknown'
|
|
22
|
+
const statusColorMap = {
|
|
23
|
+
pending:'yellow',
|
|
24
|
+
error :'red',
|
|
25
|
+
success:'green',
|
|
29
26
|
}
|
|
30
27
|
|
|
28
|
+
const FILE_UPLOAD_COMPLETE_PROGRESS = 100
|
|
29
|
+
|
|
31
30
|
/**
|
|
32
31
|
* This is the component description.
|
|
33
32
|
*/
|
|
@@ -38,9 +37,28 @@ const FilePreview = ({
|
|
|
38
37
|
file,
|
|
39
38
|
disabled,
|
|
40
39
|
handleDelete,
|
|
40
|
+
uploadStatus,
|
|
41
|
+
color,
|
|
42
|
+
uploadProgress,
|
|
41
43
|
// ...otherProps
|
|
42
44
|
}) => {
|
|
43
|
-
const
|
|
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
|
|
44
62
|
|
|
45
63
|
return (
|
|
46
64
|
<div
|
|
@@ -70,19 +88,35 @@ const FilePreview = ({
|
|
|
70
88
|
{type}
|
|
71
89
|
</span>
|
|
72
90
|
<span title={file.name} className="name">{file.name}</span>
|
|
73
|
-
<Button
|
|
74
|
-
|
|
91
|
+
<Button
|
|
92
|
+
isCompact
|
|
93
|
+
isSimple
|
|
94
|
+
onClick={() => (canDelete ? handleDelete() : handlePreview())}
|
|
95
|
+
>
|
|
96
|
+
<span className="icon x-ui-icons c-x">{canDelete ? 'Y' : '9'}</span>
|
|
75
97
|
</Button>
|
|
76
98
|
</div>
|
|
77
99
|
{!disabled && (
|
|
78
|
-
<div className=
|
|
100
|
+
<div className={`progress-status x-${statusColor}`}>
|
|
79
101
|
<div className="status">
|
|
80
|
-
|
|
81
|
-
|
|
102
|
+
{isPending ? (
|
|
103
|
+
<LoadingCircle
|
|
104
|
+
className={`x-${statusColor}`}
|
|
105
|
+
heightWidth="1rem"
|
|
106
|
+
color={statusColor}
|
|
107
|
+
/>
|
|
108
|
+
) : (
|
|
109
|
+
<>
|
|
110
|
+
<span className="icon">
|
|
111
|
+
{isSuccess ? 'I' : 'Y'}
|
|
112
|
+
</span>
|
|
113
|
+
<span>{isSuccess ? 'Uploaded' : 'Error'}</span>
|
|
114
|
+
</>
|
|
115
|
+
)}
|
|
82
116
|
</div>
|
|
83
117
|
<ProgressBar
|
|
84
|
-
color=
|
|
85
|
-
progress={
|
|
118
|
+
color={statusColor}
|
|
119
|
+
progress={isPending ? uploadProgress : FILE_UPLOAD_COMPLETE_PROGRESS}
|
|
86
120
|
height="3px"
|
|
87
121
|
/>
|
|
88
122
|
</div>
|
|
@@ -106,10 +140,21 @@ FilePreview.propTypes = {
|
|
|
106
140
|
* The React-written, css properties for this element.
|
|
107
141
|
*/
|
|
108
142
|
style:PropTypes.objectOf(PropTypes.string),
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The base color of the file preview
|
|
146
|
+
*/
|
|
147
|
+
color:PropTypes.string,
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The upload progress of the file
|
|
151
|
+
*/
|
|
152
|
+
uploadProgress:PropTypes.number,
|
|
109
153
|
}
|
|
110
154
|
|
|
111
155
|
FilePreview.defaultProps = {
|
|
112
|
-
|
|
156
|
+
color :'green',
|
|
157
|
+
uploadProgress:45,
|
|
113
158
|
}
|
|
114
159
|
|
|
115
160
|
export default FilePreview
|
|
@@ -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);
|
|
@@ -75,15 +76,15 @@ $default-file-width: calc($width-without-gaps / $files-per-row);
|
|
|
75
76
|
> .progress-status {
|
|
76
77
|
> .status {
|
|
77
78
|
align-items: center;
|
|
78
|
-
|
|
79
|
-
color: var(--green);
|
|
79
|
+
color: var(--x);
|
|
80
80
|
display: flex;
|
|
81
81
|
gap: calc(var(--gap) / 2);
|
|
82
|
+
margin-bottom: calc($default-margin / 4);
|
|
82
83
|
|
|
83
84
|
> .icon {
|
|
84
|
-
background-color: var(--
|
|
85
|
+
background-color: var(--x);
|
|
85
86
|
border-radius: 50%;
|
|
86
|
-
color: var(--on-
|
|
87
|
+
color: var(--on-x);
|
|
87
88
|
font-size: calc(var(--s-2) * 1rem);
|
|
88
89
|
padding: calc($default-padding / 4);
|
|
89
90
|
}
|
|
@@ -30,3 +30,20 @@ export const fileUploadOptions = [
|
|
|
30
30
|
label:'Audio',
|
|
31
31
|
},
|
|
32
32
|
]
|
|
33
|
+
|
|
34
|
+
export const getFileType = (file) => {
|
|
35
|
+
const mimeType = file?.type
|
|
36
|
+
const extension = file?.name?.split('.').pop().toLowerCase()
|
|
37
|
+
const fileType = fileUploadOptions.find(
|
|
38
|
+
(option) => option.value.includes(mimeType) || option.value.includes(extension))
|
|
39
|
+
|
|
40
|
+
return fileType ? fileType.key : 'unknown'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const getFileTypeFromUrl = (url) => {
|
|
44
|
+
const urlWithoutParams = url.split('?')[0]
|
|
45
|
+
const extension = urlWithoutParams.split('.').pop().toLowerCase()
|
|
46
|
+
const fileType = fileUploadOptions.find((option) => option.value.includes(extension))
|
|
47
|
+
|
|
48
|
+
return fileType ? fileType.key : 'unknown'
|
|
49
|
+
}
|
package/src/ui/f/fields/index.js
CHANGED
|
@@ -9,4 +9,9 @@ export { Checkbox } from './Checkbox'
|
|
|
9
9
|
export { QueryChoices } from './QueryChoices'
|
|
10
10
|
export { LinkInput } from './LinkInput'
|
|
11
11
|
export { EditorInput } from './EditorInput'
|
|
12
|
-
export {
|
|
12
|
+
export {
|
|
13
|
+
FileUpload,
|
|
14
|
+
fileUploadOptions,
|
|
15
|
+
getFileType,
|
|
16
|
+
getFileTypeFromUrl,
|
|
17
|
+
} from './FileUpload'
|
|
@@ -33,6 +33,8 @@ const FormBuilder = ({
|
|
|
33
33
|
onRendererFormSave,
|
|
34
34
|
onBuilderValidate,
|
|
35
35
|
initialBuilderValues,
|
|
36
|
+
fileUploadStatus,
|
|
37
|
+
handleFileDelete,
|
|
36
38
|
// ...otherProps
|
|
37
39
|
}) => (
|
|
38
40
|
<div
|
|
@@ -65,6 +67,8 @@ const FormBuilder = ({
|
|
|
65
67
|
readOnly={readOnly}
|
|
66
68
|
shouldSubmit={shouldSubmit}
|
|
67
69
|
onError={onRendererError}
|
|
70
|
+
fileUploadStatus={fileUploadStatus}
|
|
71
|
+
handleFileDelete={handleFileDelete}
|
|
68
72
|
/>
|
|
69
73
|
)}
|
|
70
74
|
</div>
|
|
@@ -119,6 +123,11 @@ FormBuilder.propTypes = {
|
|
|
119
123
|
* Whether the form builder in renderer mode should submit the form values itself
|
|
120
124
|
*/
|
|
121
125
|
shouldSubmit:PropTypes.bool,
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The upload status of files if any
|
|
129
|
+
*/
|
|
130
|
+
fileUploadStatus:PropTypes.objectOf(PropTypes.string),
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
FormBuilder.defaultProps = {
|
|
@@ -62,6 +62,8 @@ const Renderer = ({
|
|
|
62
62
|
onSave,
|
|
63
63
|
onError,
|
|
64
64
|
shouldSubmit,
|
|
65
|
+
fileUploadStatus,
|
|
66
|
+
handleFileDelete,
|
|
65
67
|
// ...otherProps
|
|
66
68
|
}) => {
|
|
67
69
|
const [currentSectionIndex, setCurrentSectionIndex] = useState(0)
|
|
@@ -154,6 +156,8 @@ const Renderer = ({
|
|
|
154
156
|
{...section}
|
|
155
157
|
readOnly={readOnly}
|
|
156
158
|
setUpdatedFormData={setUpdatedFormData}
|
|
159
|
+
fileUploadStatus={fileUploadStatus}
|
|
160
|
+
handleFileDelete={handleFileDelete}
|
|
157
161
|
/>
|
|
158
162
|
)
|
|
159
163
|
))}
|
|
@@ -27,6 +27,8 @@ const Section = ({
|
|
|
27
27
|
description,
|
|
28
28
|
inputs,
|
|
29
29
|
readOnly,
|
|
30
|
+
fileUploadStatus,
|
|
31
|
+
handleFileDelete,
|
|
30
32
|
// ...otherProps
|
|
31
33
|
}) => (
|
|
32
34
|
<div
|
|
@@ -52,6 +54,8 @@ const Section = ({
|
|
|
52
54
|
key={input.name}
|
|
53
55
|
{...input}
|
|
54
56
|
disabled={readOnly}
|
|
57
|
+
uploadStatus={fileUploadStatus}
|
|
58
|
+
handleFileDelete={handleFileDelete}
|
|
55
59
|
/>
|
|
56
60
|
))}
|
|
57
61
|
</div>
|
|
@@ -32908,6 +32908,28 @@ exports[`Storyshots g/FormBuilder Renderer With Disabled 1`] = `
|
|
|
32908
32908
|
<p>
|
|
32909
32909
|
The file upload
|
|
32910
32910
|
</p>
|
|
32911
|
+
<label
|
|
32912
|
+
className="base form-label x-paragraph"
|
|
32913
|
+
htmlFor="section_0_input_0"
|
|
32914
|
+
>
|
|
32915
|
+
<input
|
|
32916
|
+
accept=".mp4,.ogg,.webm,video/mp4,video/ogg,video/webm,.pdf,application/pdf"
|
|
32917
|
+
className="file"
|
|
32918
|
+
disabled={true}
|
|
32919
|
+
id="section_0_input_0"
|
|
32920
|
+
multiple={true}
|
|
32921
|
+
onChange={[Function]}
|
|
32922
|
+
type="file"
|
|
32923
|
+
/>
|
|
32924
|
+
<span
|
|
32925
|
+
className="ai-icon"
|
|
32926
|
+
>
|
|
32927
|
+
U
|
|
32928
|
+
</span>
|
|
32929
|
+
<span>
|
|
32930
|
+
Attach file
|
|
32931
|
+
</span>
|
|
32932
|
+
</label>
|
|
32911
32933
|
</div>
|
|
32912
32934
|
</div>
|
|
32913
32935
|
<div
|