kamotive_ui 1.2.15 → 1.2.17
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/components/Dropdown/Dropdown.module.css +7 -13
- package/dist/components/FileAttach/FileAttach.js +10 -73
- package/dist/components/FileAttach/FileAttach.module.css +18 -33
- package/dist/components/FileItem/FileItem.js +100 -9
- package/dist/components/FileItem/FileItem.module.css +56 -34
- package/dist/components/FileListAttached/FileListAttached.module.css +47 -0
- package/dist/components/FileListAttached/FileListAtta/321/201hed.d.ts +3 -0
- package/dist/components/FileListAttached/FileListAtta/321/201hed.js +14 -0
- package/dist/components/FileLoader/FileLoader.d.ts +3 -0
- package/dist/components/FileLoader/FileLoader.js +162 -0
- package/dist/components/FileLoader/FileLoader.module.css +69 -0
- package/dist/components/IconButton/IconButton.js +8 -3
- package/dist/components/IconButton/IconButton.module.css +26 -16
- package/dist/components/Input/Input.module.css +19 -19
- package/dist/components/ProgressBar/ProgressBar.js +8 -4
- package/dist/components/Snackbar/Snackbar.js +2 -2
- package/dist/components/Snackbar/Snackbar.module.css +13 -6
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/types/index.d.ts +84 -13
- package/package.json +1 -1
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
|
|
209
209
|
.label {
|
|
210
210
|
transition: 0.3ms ease-out;
|
|
211
|
-
font-weight: 500;
|
|
211
|
+
font-weight: 500 !important;
|
|
212
212
|
/* color: var(--text-grey); */
|
|
213
213
|
}
|
|
214
214
|
.label--readOnly {
|
|
@@ -225,13 +225,13 @@
|
|
|
225
225
|
text-overflow: ellipsis;
|
|
226
226
|
max-width: calc(100% - 32px);
|
|
227
227
|
color: var(--text-dark);
|
|
228
|
-
font-weight: var(--font-weight-semiBold);
|
|
228
|
+
/* font-weight: var(--font-weight-semiBold); */
|
|
229
229
|
}
|
|
230
230
|
.label--default.lg {
|
|
231
|
-
font-size: 12px;
|
|
231
|
+
font-size: 12px !important;
|
|
232
232
|
}
|
|
233
233
|
.label--default.md {
|
|
234
|
-
font-size: 10px;
|
|
234
|
+
font-size: 10px !important;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
.label--left {
|
|
@@ -239,24 +239,18 @@
|
|
|
239
239
|
min-width: fit-content;
|
|
240
240
|
}
|
|
241
241
|
.label--left.lg {
|
|
242
|
-
font-size: 14px;
|
|
242
|
+
font-size: 14px !important;
|
|
243
243
|
}
|
|
244
244
|
.label--left.md {
|
|
245
|
-
font-size: 12px;
|
|
245
|
+
font-size: 12px !important;
|
|
246
246
|
}
|
|
247
247
|
/* Стили для обязательного поля */
|
|
248
|
-
/* .label--default.label--required {
|
|
249
|
-
position: relative;
|
|
250
|
-
top: 0;
|
|
251
|
-
} */
|
|
252
248
|
.label--required::after {
|
|
253
249
|
content: '*';
|
|
254
250
|
color: var(--error-main);
|
|
255
|
-
/* position: absolute; */
|
|
256
251
|
margin-left: 2px;
|
|
257
252
|
top: 2px;
|
|
258
|
-
|
|
259
|
-
font-size: 12px;
|
|
253
|
+
font-size: 12px !important;
|
|
260
254
|
}
|
|
261
255
|
|
|
262
256
|
.item-selected {
|
|
@@ -1,80 +1,17 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { useDropzone } from 'react-dropzone';
|
|
1
|
+
import React from 'react';
|
|
3
2
|
import styles from './FileAttach.module.css';
|
|
4
|
-
import { Typography } from '../Typography/Typography';
|
|
5
|
-
import { IconUpload } from '../../Icons';
|
|
6
|
-
import { FileItem } from '../FileItem/FileItem';
|
|
7
3
|
import classNames from 'classnames';
|
|
8
|
-
|
|
4
|
+
import { FileLoader } from '../FileLoader/FileLoader';
|
|
5
|
+
import { FileListAttaсhed } from '../FileListAttached/FileListAttaсhed';
|
|
6
|
+
export const FileAttach = ({ filesList = [], maxFileSize = 2, maxFileCount = 10, acceptedFormats = {
|
|
9
7
|
'image/*': ['.png', '.gif', '.jpeg', '.jpg'],
|
|
10
8
|
'application/pdf': ['.pdf'],
|
|
11
9
|
'application/msword': ['.doc', '.docx'],
|
|
12
|
-
}, addedFiles, setAddedFiles, onDownload,
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
if (file.size > maxFileSize * 1024 * 1024 * 1024) {
|
|
16
|
-
return {
|
|
17
|
-
code: 'name-too-large',
|
|
18
|
-
message: `Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
if (addedFiles.find((addedFile) => addedFile.name === file.name)) {
|
|
22
|
-
return {
|
|
23
|
-
code: 'repeating-file-name',
|
|
24
|
-
message: `Файл уже добавлен`,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
if (addedFiles.length > maxFileCount - 1) {
|
|
28
|
-
return {
|
|
29
|
-
code: 'files-count-too-large',
|
|
30
|
-
message: `Максимальное количество файлов ${maxFileCount}`,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
};
|
|
35
|
-
const { getRootProps, getInputProps } = useDropzone({
|
|
36
|
-
onDrop: (acceptedFiles, fileRejections) => {
|
|
37
|
-
setAddedFiles([...addedFiles, ...acceptedFiles]);
|
|
38
|
-
setErrorFiles([...errorFiles, ...fileRejections]);
|
|
39
|
-
},
|
|
40
|
-
validator: fileValidator,
|
|
41
|
-
accept: acceptedFormats,
|
|
42
|
-
maxFiles: maxFileCount,
|
|
43
|
-
disabled: disabled,
|
|
10
|
+
}, addedFiles, setAddedFiles, onDownload, onDelete, canAdd = true, canDelete = true, canDownload = true, position = 'bottom', lng = 'ru', className, style, }) => {
|
|
11
|
+
const fileAttachClasses = classNames(styles['fileAttach'], className, {
|
|
12
|
+
[styles[`fileAttach_position_${position}`]]: position,
|
|
44
13
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
setAddedFiles(addedFiles.filter((file) => file.name !== fileName));
|
|
49
|
-
};
|
|
50
|
-
const deleteRejectedFile = (errorFiles, setErrorFiles, fileName) => {
|
|
51
|
-
setErrorFiles(errorFiles.filter(({ file }) => file.name !== fileName));
|
|
52
|
-
};
|
|
53
|
-
// Функция для получения всех доступных форматов в виде строки
|
|
54
|
-
const getAcceptedFormatsString = (acceptedFormats) => {
|
|
55
|
-
const formats = [];
|
|
56
|
-
for (const key in acceptedFormats) {
|
|
57
|
-
if (acceptedFormats.hasOwnProperty(key)) {
|
|
58
|
-
formats.push(...acceptedFormats[key].map((format) => format.replace('.', '')));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return formats.join(', ');
|
|
62
|
-
};
|
|
63
|
-
return (React.createElement("section", { className: classNames(styles['fileAttach'], className), style: style },
|
|
64
|
-
React.createElement("div", Object.assign({}, getRootProps({ className: `${styles['dropzone']} ${disabled ? styles['disabled'] : ''}` })),
|
|
65
|
-
React.createElement("input", Object.assign({}, getInputProps())),
|
|
66
|
-
React.createElement(IconUpload, { htmlColor: disabled ? 'var(--grey-medium)' : 'var(--icons-grey)' }),
|
|
67
|
-
React.createElement(Typography, { variant: "Body1", color: disabled ? 'var(--grey-medium)' : 'var(--icons-grey)' },
|
|
68
|
-
React.createElement("span", { style: { textDecoration: 'underline' } }, "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u043E\u0431\u043B\u0430\u0441\u0442\u044C"),
|
|
69
|
-
React.createElement("span", null, " \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B")),
|
|
70
|
-
React.createElement("div", null,
|
|
71
|
-
maxFileSize && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" },
|
|
72
|
-
`Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`,
|
|
73
|
-
" ",
|
|
74
|
-
React.createElement("br", null))),
|
|
75
|
-
maxFileCount && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `За раз можно загрузить ${maxFileCount} ${maxFileCount > 1 ? `файлов` : `файл`}`)))),
|
|
76
|
-
acceptedFormats && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `Поддерживаемые форматы: ${getAcceptedFormatsString(acceptedFormats)}`)),
|
|
77
|
-
(addedFiles === null || addedFiles === void 0 ? void 0 : addedFiles.length) > 0 || (errorFiles === null || errorFiles === void 0 ? void 0 : errorFiles.length) > 0 ? (React.createElement("div", { className: styles['addedFiles'] },
|
|
78
|
-
acceptedFileItems,
|
|
79
|
-
fileRejectionItems)) : (React.createElement(Typography, { variant: "Caption", color: "var(--text-dark)" }, "\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B"))));
|
|
14
|
+
return (React.createElement("div", { className: fileAttachClasses, style: style },
|
|
15
|
+
React.createElement(FileLoader, { maxFileSize: maxFileSize, maxFileCount: maxFileCount, acceptedFormats: acceptedFormats, addedFiles: addedFiles, setAddedFiles: setAddedFiles, canAdd: canAdd, lng: lng }),
|
|
16
|
+
React.createElement(FileListAttaсhed, { filesList: filesList, onDelete: onDelete, onDownload: onDownload, canDelete: canDelete, canDownload: canDownload, lng: lng })));
|
|
80
17
|
};
|
|
@@ -1,36 +1,21 @@
|
|
|
1
1
|
.fileAttach {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
display: flex;
|
|
3
|
+
gap: 15px;
|
|
4
|
+
min-width: 260px;
|
|
5
|
+
flex: 1;
|
|
6
|
+
max-width: 100%;
|
|
7
|
+
}
|
|
8
|
+
.fileAttach_position_left {
|
|
9
|
+
flex-direction: row;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
flex-wrap: wrap;
|
|
12
|
+
}
|
|
13
|
+
.fileAttach_position_right {
|
|
14
|
+
flex-direction: row-reverse;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
flex-wrap: wrap;
|
|
6
17
|
}
|
|
7
18
|
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
align-items: center;
|
|
12
|
-
justify-content: center;
|
|
13
|
-
|
|
14
|
-
gap: 10px;
|
|
15
|
-
padding: 30px 20px;
|
|
16
|
-
border: 1px dashed var(--blue-main);
|
|
17
|
-
border-radius: 15px;
|
|
18
|
-
cursor: pointer;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.dropzone:hover {
|
|
22
|
-
background-color: var(--fills-active);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.dropzone.disabled {
|
|
26
|
-
background-color: var(--fills-disabled);
|
|
27
|
-
border-color: var(--grey-medium);
|
|
28
|
-
cursor: not-allowed;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.addedFiles {
|
|
32
|
-
display: flex;
|
|
33
|
-
flex-direction: column;
|
|
34
|
-
gap: 10px;
|
|
35
|
-
overflow-y: auto;
|
|
36
|
-
}
|
|
19
|
+
.fileAttach_position_bottom {
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
@@ -1,20 +1,111 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import styles from './FileItem.module.css';
|
|
3
3
|
import { Typography } from '../Typography/Typography';
|
|
4
4
|
import { ProgressBar } from '../ProgressBar/ProgressBar';
|
|
5
5
|
import { IconButton } from '../IconButton/IconButton';
|
|
6
6
|
import { IconClose10, IconDownload, IconFile } from '../../Icons';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { Tooltip } from '../Tooltip/Tooltip';
|
|
8
|
+
import classNames from 'classnames';
|
|
9
|
+
export const FileItem = ({ file, loading = false, error = '', onDownload, onDelete, canDelete = true, canDownload = true, style, isAddedFile, isRejectedFile, }) => {
|
|
10
|
+
const [isLoadingFinished, setIsLoadingFinished] = useState(false);
|
|
11
|
+
const [animationDuration, setAnimationDuration] = useState(0);
|
|
12
|
+
const [maxLength, setMaxLength] = useState(30);
|
|
13
|
+
const fileItemRef = useRef(null);
|
|
14
|
+
const fileNameRef = useRef(null);
|
|
15
|
+
const calculateMaxLength = () => {
|
|
16
|
+
if (fileItemRef.current && fileNameRef.current) {
|
|
17
|
+
const containerWidth = fileItemRef.current.clientWidth;
|
|
18
|
+
const iconsWidth = 100; // Примерная ширина для иконок и отступов
|
|
19
|
+
const availableWidth = containerWidth - iconsWidth; // Доступная ширина для текста
|
|
20
|
+
const charWidth = 8;
|
|
21
|
+
const calculatedMaxLength = Math.floor(availableWidth / charWidth);
|
|
22
|
+
const newMaxLength = Math.max(calculatedMaxLength, 20);
|
|
23
|
+
setMaxLength(newMaxLength);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const croppedName = (filename) => {
|
|
27
|
+
if (filename.length <= maxLength) {
|
|
28
|
+
return filename;
|
|
29
|
+
}
|
|
30
|
+
const lastDotIndex = filename.lastIndexOf('.');
|
|
31
|
+
let name, extension;
|
|
32
|
+
if (lastDotIndex === -1) {
|
|
33
|
+
name = filename;
|
|
34
|
+
extension = '';
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
name = filename.slice(0, lastDotIndex);
|
|
38
|
+
extension = filename.slice(lastDotIndex);
|
|
39
|
+
}
|
|
40
|
+
const availableLength = maxLength - extension.length;
|
|
41
|
+
if (availableLength <= 3) {
|
|
42
|
+
return filename.slice(0, maxLength - 3) + '...';
|
|
43
|
+
}
|
|
44
|
+
const charsFromStart = Math.ceil((availableLength - 3) / 2);
|
|
45
|
+
const charsFromEnd = Math.floor((availableLength - 3) / 2);
|
|
46
|
+
return name.slice(0, charsFromStart) + '...' + name.slice(name.length - charsFromEnd) + extension;
|
|
47
|
+
};
|
|
48
|
+
// Для расчета ширины контейнера и обновления maxLength при изменении размера окна
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
calculateMaxLength();
|
|
51
|
+
window.addEventListener('resize', calculateMaxLength);
|
|
52
|
+
return () => {
|
|
53
|
+
window.removeEventListener('resize', calculateMaxLength);
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
// Расчет длительности анимации в зависимости от размера файла
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!file.size)
|
|
59
|
+
return;
|
|
60
|
+
const baseDuration = 2000; // для маленьких файлов (до 100 КБ) 1 секунда
|
|
61
|
+
const fileSizeKB = file.size / 1024; // Размер файла в КБ
|
|
62
|
+
let calculatedDuration;
|
|
63
|
+
if (fileSizeKB <= 100) {
|
|
64
|
+
calculatedDuration = baseDuration;
|
|
65
|
+
}
|
|
66
|
+
else if (fileSizeKB <= 1024) {
|
|
67
|
+
const ratio = (fileSizeKB - 100) / (1024 - 100);
|
|
68
|
+
calculatedDuration = baseDuration + ratio * 2000; // от 2 до 4 секунд
|
|
69
|
+
}
|
|
70
|
+
else if (fileSizeKB <= 10240) {
|
|
71
|
+
const ratio = (fileSizeKB - 1024) / (10240 - 1024);
|
|
72
|
+
calculatedDuration = 4000 + ratio * 4000; // от 4 до 8 секунд
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const ratio = Math.min((fileSizeKB - 10240) / (102400 - 10240), 1);
|
|
76
|
+
calculatedDuration = 10000 + ratio * 3000; // от 8 до 13 секунд
|
|
77
|
+
}
|
|
78
|
+
setAnimationDuration(calculatedDuration);
|
|
79
|
+
}, [file]);
|
|
80
|
+
const fileItemClasses = classNames(styles['fileItem'], {
|
|
81
|
+
[styles['loading']]: loading,
|
|
82
|
+
[styles['error']]: error,
|
|
83
|
+
[styles[`fileItem_attached`]]: !(isAddedFile || isRejectedFile),
|
|
84
|
+
});
|
|
85
|
+
const handleDeleteClick = (e, id) => {
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
if (onDelete && id) {
|
|
88
|
+
onDelete(id);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const handleDownloadClick = (e, file) => {
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
if (onDownload && file) {
|
|
94
|
+
onDownload(file);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return (React.createElement("div", { className: fileItemClasses, style: style, ref: fileItemRef, onClick: () => !(isAddedFile || isRejectedFile) && canDownload && file && onDownload && onDownload(file) },
|
|
9
98
|
React.createElement("div", { className: styles['fileItemFile'] },
|
|
10
99
|
React.createElement("div", { className: styles['fileItemInfo'] },
|
|
11
100
|
React.createElement("div", { className: styles['fileItemIcon'] },
|
|
12
101
|
React.createElement(IconFile, { htmlColor: 'var(--icons-grey)' })),
|
|
13
|
-
React.createElement("div", { className: styles['fileItemName'] },
|
|
14
|
-
React.createElement(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
102
|
+
React.createElement("div", { className: styles['fileItemName'], ref: fileNameRef },
|
|
103
|
+
file.filename.length > maxLength ? (React.createElement(Tooltip, { label: file.filename, position: "bottom-center", displayDelay: 300 },
|
|
104
|
+
React.createElement(Typography, { variant: "Body1", color: "var(--text-dark)" }, croppedName(file.filename)))) : (React.createElement(Typography, { variant: "Body1", color: "var(--text-dark)" }, croppedName(file.filename))),
|
|
105
|
+
file.size !== 0 && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `${file.size ? (file.size / 1024).toFixed(1) : 0} кБ`)))),
|
|
106
|
+
React.createElement("div", { className: styles['fileItemActions'] },
|
|
107
|
+
!(isAddedFile || isRejectedFile) && canDownload && (React.createElement(IconButton, { icon: React.createElement(IconDownload, null), onClick: (e) => handleDownloadClick(e, file), color: "var(--icons-grey)" })),
|
|
108
|
+
canDelete && (React.createElement(IconButton, { icon: React.createElement(IconClose10, null), onClick: (e) => handleDeleteClick(e, file.id || ''), color: "var(--icons-grey)" })))),
|
|
109
|
+
loading && !isLoadingFinished && (React.createElement(ProgressBar, { animated: true, size: "sm", value: 100, setIsLoadingFinished: setIsLoadingFinished, animationDuration: animationDuration })),
|
|
19
110
|
error && (React.createElement(Typography, { variant: "Caption", color: "var(--error-main)" }, error))));
|
|
20
111
|
};
|
|
@@ -1,45 +1,67 @@
|
|
|
1
|
-
|
|
2
1
|
.fileItem {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
align-items: flex-start;
|
|
28
|
-
overflow: hidden;
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 5px;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
padding: 10px 15px;
|
|
7
|
+
border: 1px solid var(--info-secondary);
|
|
8
|
+
border-radius: 10px;
|
|
9
|
+
}
|
|
10
|
+
.error {
|
|
11
|
+
border-color: var(--error-main);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.fileItemFile {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: row;
|
|
17
|
+
align-items: center;
|
|
18
|
+
}
|
|
19
|
+
.fileItemInfo {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: row;
|
|
22
|
+
gap: 5px;
|
|
23
|
+
flex-grow: 1;
|
|
24
|
+
align-items: flex-start;
|
|
25
|
+
overflow: hidden;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
.fileItemIcon {
|
|
32
|
-
|
|
29
|
+
flex-shrink: 0;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
.fileItemName {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
flex-grow: 1;
|
|
36
|
+
align-items: flex-start;
|
|
37
|
+
overflow: auto;
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
.fileIcon svg path {
|
|
44
|
-
|
|
41
|
+
stroke-width: 0.8;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.fileItem_attached {
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
transition: all 0.15s ease-in-out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.fileItem_attached:hover {
|
|
50
|
+
background-color: rgba(0, 0, 0, 0.03);
|
|
51
|
+
transform: translateY(-1px);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.fileItem_attached .fileIcon,
|
|
55
|
+
.fileItem_attached button {
|
|
56
|
+
background-color: transparent;
|
|
57
|
+
transition: background-color 0.15s ease-in-out;
|
|
58
|
+
border-radius: 50%;
|
|
59
|
+
height: 28px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.fileItemActions {
|
|
63
|
+
display: flex;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 3px;
|
|
45
67
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.fileList {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
flex: 1;
|
|
5
|
+
min-width: 260px;
|
|
6
|
+
max-height: 100%;
|
|
7
|
+
overflow-y: auto;
|
|
8
|
+
position: relative;
|
|
9
|
+
}
|
|
10
|
+
.fileListHeader {
|
|
11
|
+
position: sticky;
|
|
12
|
+
top: 0;
|
|
13
|
+
z-index: 1;
|
|
14
|
+
margin-bottom: 5px;
|
|
15
|
+
font-weight: 500 !important;
|
|
16
|
+
}
|
|
17
|
+
.fileListFiles {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: 5px;
|
|
21
|
+
overflow-y: auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Стилизация скроллбара для WebKit (Chrome, Safari, новые версии Edge) */
|
|
25
|
+
.fileList::-webkit-scrollbar {
|
|
26
|
+
width: 6px; /* Ширина скроллбара */
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.fileList::-webkit-scrollbar-track {
|
|
30
|
+
background: rgba(0, 0, 0, 0.03); /* Светлый фон для трека */
|
|
31
|
+
border-radius: 10px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.fileList::-webkit-scrollbar-thumb {
|
|
35
|
+
background: var(--grey-medium, #c4c4c4); /* Цвет ползунка */
|
|
36
|
+
border-radius: 10px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.fileList::-webkit-scrollbar-thumb:hover {
|
|
40
|
+
background: var(--grey-dark, #a0a0a0); /* Цвет ползунка при наведении */
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Стилизация скроллбара для Firefox */
|
|
44
|
+
.fileList {
|
|
45
|
+
scrollbar-width: thin; /* "auto", "thin" или "none" */
|
|
46
|
+
scrollbar-color: var(--grey-medium, #c4c4c4) rgba(0, 0, 0, 0.03); /* цвет ползунка и трека */
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './FileListAttached.module.css';
|
|
3
|
+
import { Typography } from '../Typography/Typography';
|
|
4
|
+
import { FileItem } from '../FileItem/FileItem';
|
|
5
|
+
import classNames from 'classnames';
|
|
6
|
+
export const FileListAttaсhed = ({ filesList, onDelete, onDownload, canDelete, canDownload, isInfoShown = true, lng = 'ru', className, style, }) => {
|
|
7
|
+
if (!filesList || filesList.length === 0) {
|
|
8
|
+
return lng === 'ru' || lng.includes('ru') ? (React.createElement(Typography, { variant: "Body2-SemiBold", color: "var(--grey-medium)", style: { marginTop: '5px' } }, "\u041D\u0435\u0442 \u043F\u0440\u0438\u043A\u0440\u0435\u043F\u043B\u0435\u043D\u043D\u044B\u0445 \u0444\u0430\u0439\u043B\u043E\u0432")) : (React.createElement(Typography, { variant: "Body2-SemiBold", color: "var(--grey-medium)", style: { marginTop: '5px' } }, "No attached files"));
|
|
9
|
+
}
|
|
10
|
+
return (React.createElement("div", { className: classNames(styles['fileList'], className), style: style },
|
|
11
|
+
isInfoShown &&
|
|
12
|
+
(lng === 'ru' || lng.includes('ru') ? (React.createElement(Typography, { variant: "Body2-SemiBold", color: "var(--text-dark)", style: { lineHeight: '20px' }, className: styles['fileListHeader'] }, `Прикрепленные файлы (${filesList.length})`)) : (React.createElement(Typography, { variant: "Body2-SemiBold", color: "var(--text-dark)", style: { lineHeight: '20px' }, className: styles['fileListHeader'] }, `Attached files (${filesList.length})`))),
|
|
13
|
+
React.createElement("div", { className: styles['fileListFiles'] }, filesList.map((file) => (React.createElement(FileItem, { key: file.id, file: file, onDownload: onDownload, onDelete: onDelete, canDelete: canDelete, canDownload: canDownload }))))));
|
|
14
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useDropzone } from 'react-dropzone';
|
|
3
|
+
import styles from './FileLoader.module.css';
|
|
4
|
+
import { Typography } from '../Typography/Typography';
|
|
5
|
+
import { IconUpload } from '../../Icons';
|
|
6
|
+
import { FileItem } from '../FileItem/FileItem';
|
|
7
|
+
import classNames from 'classnames';
|
|
8
|
+
export const FileLoader = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats = {
|
|
9
|
+
'image/*': ['.png', '.gif', '.jpeg', '.jpg'],
|
|
10
|
+
'application/pdf': ['.pdf'],
|
|
11
|
+
'application/msword': ['.doc', '.docx'],
|
|
12
|
+
}, addedFiles, setAddedFiles, canAdd = true, lng = 'ru', className, style, }) => {
|
|
13
|
+
const [isLoadingFiles, setIsLoadingFiles] = useState(false);
|
|
14
|
+
const [loadingFilesNames, setLoadingFilesNames] = useState([]);
|
|
15
|
+
const [errorFiles, setErrorFiles] = useState([]);
|
|
16
|
+
const [addedFilesFormated, setAddedFilesFormatted] = useState([]);
|
|
17
|
+
const fileValidator = (file) => {
|
|
18
|
+
if (file.size > maxFileSize * 1024 * 1024 * 1024) {
|
|
19
|
+
return {
|
|
20
|
+
code: 'name-too-large',
|
|
21
|
+
message: lng === 'ru' || lng.includes('ru')
|
|
22
|
+
? `Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`
|
|
23
|
+
: `Maximum file size ${maxFileSize.toFixed(0)} GB`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (addedFiles.find((addedFile) => addedFile.name === file.name)) {
|
|
27
|
+
return {
|
|
28
|
+
code: 'repeating-file-name',
|
|
29
|
+
message: lng === 'ru' || lng.includes('ru') ? `Файл уже добавлен` : `File already added`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (addedFiles.length > maxFileCount - 1) {
|
|
33
|
+
return {
|
|
34
|
+
code: 'files-count-too-large',
|
|
35
|
+
message: lng === 'ru' || lng.includes('ru') ? `Максимальное количество файлов ${maxFileCount}` : `Maximum number of files ${maxFileCount}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
const { getRootProps, getInputProps } = useDropzone({
|
|
41
|
+
onDrop: (acceptedFiles, fileRejections) => {
|
|
42
|
+
setAddedFiles([...addedFiles, ...acceptedFiles]);
|
|
43
|
+
//преобразование типа файлов для отрисовки в списке
|
|
44
|
+
const newFormatAttachments = acceptedFiles.map((file) => {
|
|
45
|
+
return {
|
|
46
|
+
id: `file-${file.name}`,
|
|
47
|
+
filename: file.name,
|
|
48
|
+
size: file.size,
|
|
49
|
+
type: file.type,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
setLoadingFilesNames(newFormatAttachments.map((file) => file.filename));
|
|
53
|
+
setIsLoadingFiles(true);
|
|
54
|
+
setAddedFilesFormatted([...addedFilesFormated, ...newFormatAttachments]);
|
|
55
|
+
let formattedRejections = [];
|
|
56
|
+
// Проверяем, есть ли ошибка превышения количества файлов
|
|
57
|
+
const hasTooManyFilesError = fileRejections.some((rejection) => rejection.errors.some((error) => error.code === 'too-many-files'));
|
|
58
|
+
if (hasTooManyFilesError) {
|
|
59
|
+
const remainingFiles = Math.max(0, maxFileCount - addedFiles.length);
|
|
60
|
+
const filesToAdd = fileRejections.slice(0, remainingFiles).map((rejection) => rejection.file);
|
|
61
|
+
setAddedFiles([...addedFiles, ...filesToAdd]);
|
|
62
|
+
const newFormatFilesToAdd = filesToAdd.map((rejectionAdd) => ({
|
|
63
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
64
|
+
filename: rejectionAdd.name,
|
|
65
|
+
size: rejectionAdd.size,
|
|
66
|
+
type: rejectionAdd.type,
|
|
67
|
+
}));
|
|
68
|
+
setAddedFilesFormatted([...addedFilesFormated, ...newFormatFilesToAdd]);
|
|
69
|
+
const filesToReject = fileRejections.slice(remainingFiles);
|
|
70
|
+
formattedRejections = filesToReject.map((rejection) => ({
|
|
71
|
+
errors: [
|
|
72
|
+
{
|
|
73
|
+
code: 'files-count-too-large',
|
|
74
|
+
message: lng === 'ru' || lng.includes('ru')
|
|
75
|
+
? `Максимальное количество файлов ${maxFileCount}`
|
|
76
|
+
: `Maximum number of files ${maxFileCount}`,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
file: {
|
|
80
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
81
|
+
filename: rejection.file.name,
|
|
82
|
+
size: rejection.file.size,
|
|
83
|
+
path: rejection.file.path,
|
|
84
|
+
},
|
|
85
|
+
}));
|
|
86
|
+
setErrorFiles([...errorFiles, ...formattedRejections]);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
formattedRejections = fileRejections.map((rejection) => ({
|
|
90
|
+
errors: rejection.errors,
|
|
91
|
+
file: {
|
|
92
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
93
|
+
filename: rejection.file.name,
|
|
94
|
+
size: rejection.file.size,
|
|
95
|
+
path: rejection.file.path,
|
|
96
|
+
},
|
|
97
|
+
}));
|
|
98
|
+
setErrorFiles([...errorFiles, ...formattedRejections]);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
validator: fileValidator,
|
|
102
|
+
accept: acceptedFormats,
|
|
103
|
+
maxFiles: maxFileCount,
|
|
104
|
+
disabled: !canAdd,
|
|
105
|
+
});
|
|
106
|
+
const handleDeleteFiles = (id) => {
|
|
107
|
+
var _a;
|
|
108
|
+
const filename = (_a = addedFilesFormated.find((file) => file.id === id)) === null || _a === void 0 ? void 0 : _a.filename;
|
|
109
|
+
setAddedFiles(addedFiles.filter((file) => file.name !== filename));
|
|
110
|
+
setAddedFilesFormatted(addedFilesFormated.filter((file) => file.filename !== filename));
|
|
111
|
+
setLoadingFilesNames(loadingFilesNames.filter((id) => id !== id));
|
|
112
|
+
};
|
|
113
|
+
const acceptedFileItems = addedFilesFormated.map((file) => {
|
|
114
|
+
return (React.createElement(FileItem, { key: file.id, file: file, loading: loadingFilesNames.includes(file.filename), onDelete: handleDeleteFiles, isAddedFile: true }));
|
|
115
|
+
});
|
|
116
|
+
const handleDeleteRejectedFile = (id) => {
|
|
117
|
+
setErrorFiles(errorFiles.filter((rejection) => rejection.file.id !== id));
|
|
118
|
+
};
|
|
119
|
+
const fileRejectionItems = errorFiles.map(({ file, errors }) => (React.createElement(FileItem, { key: file.id, file: file, error: errors[0].message, onDelete: handleDeleteRejectedFile, isRejectedFile: true })));
|
|
120
|
+
// Функция для получения всех доступных форматов в виде строки
|
|
121
|
+
const getAcceptedFormatsString = (acceptedFormats) => {
|
|
122
|
+
const formats = [];
|
|
123
|
+
for (const key in acceptedFormats) {
|
|
124
|
+
if (acceptedFormats.hasOwnProperty(key)) {
|
|
125
|
+
formats.push(...acceptedFormats[key].map((format) => format.replace('.', '')));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return formats.join(', ');
|
|
129
|
+
};
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (loadingFilesNames.length === 0 && isLoadingFiles) {
|
|
132
|
+
setIsLoadingFiles(false);
|
|
133
|
+
}
|
|
134
|
+
}, [loadingFilesNames, isLoadingFiles]);
|
|
135
|
+
return (React.createElement("section", { className: classNames(styles['fileLoader'], className), style: style },
|
|
136
|
+
React.createElement("div", Object.assign({}, getRootProps({ className: `${styles['dropzone']} ${!canAdd ? styles['disabled'] : ''}` })),
|
|
137
|
+
React.createElement("input", Object.assign({}, getInputProps())),
|
|
138
|
+
React.createElement(IconUpload, { htmlColor: !canAdd ? 'var(--grey-medium)' : 'var(--icons-grey)' }),
|
|
139
|
+
React.createElement(Typography, { variant: "Body1", color: !canAdd ? 'var(--grey-medium)' : 'var(--icons-grey)', style: { textAlign: 'center' } }, lng === 'ru' || lng.includes('ru') ? (React.createElement(React.Fragment, null,
|
|
140
|
+
React.createElement("span", { style: { textDecoration: 'underline' } }, "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u043E\u0431\u043B\u0430\u0441\u0442\u044C"),
|
|
141
|
+
" ",
|
|
142
|
+
React.createElement("span", null, " \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B"))) : (React.createElement(React.Fragment, null,
|
|
143
|
+
React.createElement("span", { style: { textDecoration: 'underline' } }, "\u0421lick on this area"),
|
|
144
|
+
" ",
|
|
145
|
+
React.createElement("span", null, "or drag files here")))),
|
|
146
|
+
React.createElement("div", null,
|
|
147
|
+
maxFileSize &&
|
|
148
|
+
(lng === 'ru' || lng.includes('ru') ? (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" },
|
|
149
|
+
`Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`,
|
|
150
|
+
" ",
|
|
151
|
+
React.createElement("br", null))) : (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" },
|
|
152
|
+
`Maximum file size ${maxFileSize.toFixed(0)} GB`,
|
|
153
|
+
" ",
|
|
154
|
+
React.createElement("br", null)))),
|
|
155
|
+
maxFileCount &&
|
|
156
|
+
(lng === 'ru' || lng.includes('ru') ? (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" }, `За раз можно загрузить ${maxFileCount} ${maxFileCount > 1 ? `файлов` : `файл`}`)) : (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" }, `You can upload ${maxFileCount} ${maxFileCount > 1 ? `files` : `file`}`))))),
|
|
157
|
+
acceptedFormats &&
|
|
158
|
+
(lng === 'ru' || lng.includes('ru') ? (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" }, `Поддерживаемые форматы: ${getAcceptedFormatsString(acceptedFormats)}`)) : (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" }, `Supported formats: ${getAcceptedFormatsString(acceptedFormats)}`))),
|
|
159
|
+
(addedFiles === null || addedFiles === void 0 ? void 0 : addedFiles.length) > 0 || (errorFiles === null || errorFiles === void 0 ? void 0 : errorFiles.length) > 0 ? (React.createElement("div", { className: styles['addedFiles'] },
|
|
160
|
+
acceptedFileItems,
|
|
161
|
+
fileRejectionItems)) : lng === 'ru' || lng.includes('ru') ? (React.createElement(Typography, { variant: "Body2-SemiBold", color: "var(--grey-medium)", style: { marginTop: '5px' } }, "\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B")) : (React.createElement(Typography, { variant: "Body2-SemiBold", color: "var(--grey-medium)", style: { marginTop: '5px' } }, "Files not added"))));
|
|
162
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
.fileLoader {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
height: inherit;
|
|
5
|
+
gap: 5px;
|
|
6
|
+
min-width: 260px;
|
|
7
|
+
flex: 1;
|
|
8
|
+
max-width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.dropzone {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
|
|
17
|
+
gap: 10px;
|
|
18
|
+
padding: 30px 20px;
|
|
19
|
+
border: 1px dashed var(--blue-main);
|
|
20
|
+
border-radius: 15px;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
margin-right: 10px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.dropzone:hover {
|
|
26
|
+
background-color: var(--fills-active);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.dropzone.disabled {
|
|
30
|
+
background-color: var(--fills-disabled);
|
|
31
|
+
border-color: var(--grey-medium);
|
|
32
|
+
cursor: not-allowed;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.addedFiles {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
gap: 10px;
|
|
39
|
+
min-width: 260px;
|
|
40
|
+
flex: 1;
|
|
41
|
+
max-height: 100%;
|
|
42
|
+
overflow-y: auto;
|
|
43
|
+
padding-right: 10px;
|
|
44
|
+
margin-top: 10px;
|
|
45
|
+
}
|
|
46
|
+
/* Стилизация скроллбара для WebKit (Chrome, Safari, новые версии Edge) */
|
|
47
|
+
.addedFiles::-webkit-scrollbar {
|
|
48
|
+
width: 6px; /* Ширина скроллбара */
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.addedFiles::-webkit-scrollbar-track {
|
|
52
|
+
background: rgba(0, 0, 0, 0.03); /* Светлый фон для трека */
|
|
53
|
+
border-radius: 10px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.addedFiles::-webkit-scrollbar-thumb {
|
|
57
|
+
background: var(--grey-medium, #c4c4c4); /* Цвет ползунка */
|
|
58
|
+
border-radius: 10px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.addedFiles::-webkit-scrollbar-thumb:hover {
|
|
62
|
+
background: var(--grey-dark, #a0a0a0); /* Цвет ползунка при наведении */
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Стилизация скроллбара для Firefox */
|
|
66
|
+
.addedFiles {
|
|
67
|
+
scrollbar-width: thin; /* "auto", "thin" или "none" */
|
|
68
|
+
scrollbar-color: var(--grey-medium, #c4c4c4) rgba(0, 0, 0, 0.03); /* цвет ползунка и трека */
|
|
69
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import styles from './IconButton.module.css';
|
|
4
|
-
export const IconButton = ({ icon, size = 'md', color, style, disabled = false, onClick, children, className }) => {
|
|
5
|
-
const validChildren = React.Children.toArray(children).filter(child => React.isValidElement(child));
|
|
4
|
+
export const IconButton = ({ icon, size = 'md', color, style, disabled = false, onClick, children, className, }) => {
|
|
5
|
+
const validChildren = React.Children.toArray(children).filter((child) => React.isValidElement(child));
|
|
6
6
|
const renderIcon = icon || validChildren[0];
|
|
7
|
-
|
|
7
|
+
const combinedStyle = Object.assign(Object.assign(Object.assign({}, style), ((style === null || style === void 0 ? void 0 : style.backgroundColor) && {
|
|
8
|
+
'--hover-background': `color-mix(in ${style.backgroundColor} 85%, black)`,
|
|
9
|
+
})), ((style === null || style === void 0 ? void 0 : style.borderRadius) && {
|
|
10
|
+
'--hover-border-radius': style.borderRadius,
|
|
11
|
+
}));
|
|
12
|
+
return (React.createElement("button", { className: classNames(styles['iconButton'], styles[`iconButton--${size}`], className), disabled: disabled, "aria-disabled": disabled, type: "button", onClick: (e) => onClick(e), style: combinedStyle }, renderIcon &&
|
|
8
13
|
React.cloneElement(renderIcon, {
|
|
9
14
|
htmlColor: color,
|
|
10
15
|
strokeWidth: size === 'lg' ? '0.5' : size === 'md' ? '0.3' : '0.0',
|
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
.iconButton {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
cursor: pointer;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
background-color: var(--white);
|
|
5
|
+
border: none;
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
aspect-ratio: 1 / 1;
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
.iconButton--sm svg{
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
.iconButton--sm svg {
|
|
13
|
+
width: 14px;
|
|
14
|
+
height: 14px;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
.iconButton--md svg{
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
.iconButton--md svg {
|
|
18
|
+
width: 16px;
|
|
19
|
+
height: 16px;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
.iconButton--lg svg{
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
.iconButton--lg svg {
|
|
23
|
+
width: 18px;
|
|
24
|
+
height: 18px;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
.iconButton:disabled {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
28
|
+
cursor: not-allowed;
|
|
29
|
+
box-shadow: none;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.iconButton:hover {
|
|
33
|
+
background-color: var(--hover-background, var(--grey-extraLight));
|
|
34
|
+
border-radius: var(--hover-border-radius, 50%);
|
|
35
|
+
filter: brightness(var(--hover-brightness, 0.97));
|
|
36
|
+
}
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
|
|
129
129
|
.label {
|
|
130
130
|
line-height: 14px;
|
|
131
|
-
font-weight: 500;
|
|
131
|
+
font-weight: 500 !important;
|
|
132
132
|
transition: 0.3ms ease-out;
|
|
133
133
|
/* color: var(--text-grey); */
|
|
134
134
|
}
|
|
@@ -140,30 +140,30 @@
|
|
|
140
140
|
overflow: hidden;
|
|
141
141
|
text-overflow: ellipsis;
|
|
142
142
|
max-width: calc(100% - 32px);
|
|
143
|
-
color: var(--text-dark)
|
|
144
|
-
font-weight: var(--font-weight-semiBold)
|
|
143
|
+
color: var(--text-dark) !important;;
|
|
144
|
+
/* font-weight: var(--font-weight-semiBold) !important;; */
|
|
145
145
|
}
|
|
146
|
-
.label--default
|
|
147
|
-
font-size: 12px
|
|
146
|
+
.label--default.lg {
|
|
147
|
+
font-size: 12px !important;;
|
|
148
148
|
}
|
|
149
149
|
.label--default.md {
|
|
150
|
-
font-size: 10px
|
|
150
|
+
font-size: 10px !important;;
|
|
151
151
|
}
|
|
152
|
-
.label--default
|
|
153
|
-
font-size: 16px
|
|
152
|
+
.label--default.sm {
|
|
153
|
+
font-size: 16px !important;;
|
|
154
154
|
}
|
|
155
155
|
.label--left {
|
|
156
|
-
color: var(--text-dark)
|
|
156
|
+
color: var(--text-dark) !important;;
|
|
157
157
|
text-wrap-mode: nowrap;
|
|
158
158
|
}
|
|
159
159
|
.label--left.lg {
|
|
160
|
-
font-size: 14px
|
|
160
|
+
font-size: 14px !important;;
|
|
161
161
|
}
|
|
162
|
-
.label--left
|
|
163
|
-
font-size: 12px
|
|
162
|
+
.label--left.md {
|
|
163
|
+
font-size: 12px !important;;
|
|
164
164
|
}
|
|
165
|
-
.label--left
|
|
166
|
-
font-size: 10px
|
|
165
|
+
.label--left.sm {
|
|
166
|
+
font-size: 10px !important;;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/* Стили для обязательного input */
|
|
@@ -193,13 +193,13 @@
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
.helperText.md {
|
|
196
|
-
font-size: 10px;
|
|
196
|
+
font-size: 10px !important;
|
|
197
197
|
}
|
|
198
198
|
.helperText.sm {
|
|
199
|
-
font-size: 8px;
|
|
199
|
+
font-size: 8px !important;
|
|
200
200
|
}
|
|
201
201
|
.helperText.lg {
|
|
202
|
-
font-size: 12px;
|
|
202
|
+
font-size: 12px !important;
|
|
203
203
|
}
|
|
204
204
|
.helperText {
|
|
205
205
|
position: absolute;
|
|
@@ -211,13 +211,13 @@
|
|
|
211
211
|
white-space: nowrap;
|
|
212
212
|
overflow: hidden;
|
|
213
213
|
text-overflow: ellipsis;
|
|
214
|
-
color: var(--error-main);
|
|
214
|
+
color: var(--error-main) !important;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
/* Стили для disabled */
|
|
218
218
|
.input:disabled {
|
|
219
219
|
background-color: rgba(120, 120, 128, 0.08);
|
|
220
|
-
color: var(--text-grey);
|
|
220
|
+
color: var(--text-grey) !important;
|
|
221
221
|
cursor: not-allowed;
|
|
222
222
|
border: none;
|
|
223
223
|
box-shadow: none;
|
|
@@ -5,7 +5,7 @@ import classNames from 'classnames';
|
|
|
5
5
|
/**
|
|
6
6
|
* Компонент ProgressBar отображает прогресс в виде заполненной полосы.
|
|
7
7
|
*/
|
|
8
|
-
export const ProgressBar = ({ value = 0, max = 100, size = 'md', showValue = true, animated = false, }) => {
|
|
8
|
+
export const ProgressBar = ({ value = 0, max = 100, size = 'md', showValue = true, animated = false, animationDuration = 8000, setIsLoadingFinished, }) => {
|
|
9
9
|
const [percent, setPercent] = useState(value);
|
|
10
10
|
const validPercentage = Math.min(Math.max(value, 0), max);
|
|
11
11
|
const progressBarClasses = classNames(styles['progress-bar'], styles[size], {
|
|
@@ -15,7 +15,7 @@ export const ProgressBar = ({ value = 0, max = 100, size = 'md', showValue = tru
|
|
|
15
15
|
useEffect(() => {
|
|
16
16
|
if (animated) {
|
|
17
17
|
const targetPercent = validPercentage;
|
|
18
|
-
const animationDuration = 8000; // Длительность анимации в миллисекундах
|
|
18
|
+
// const animationDuration = animationDuration ?? 8000; // Длительность анимации в миллисекундах
|
|
19
19
|
const stepTime = 100; // Интервал обновления в миллисекундах
|
|
20
20
|
const totalSteps = animationDuration / stepTime;
|
|
21
21
|
const increment = targetPercent / totalSteps;
|
|
@@ -25,6 +25,10 @@ export const ProgressBar = ({ value = 0, max = 100, size = 'md', showValue = tru
|
|
|
25
25
|
setPercent(Math.round(currentPercent));
|
|
26
26
|
if (currentPercent >= targetPercent) {
|
|
27
27
|
clearInterval(intervalId);
|
|
28
|
+
// Вызываем callback, когда прогресс достиг 100%
|
|
29
|
+
if (setIsLoadingFinished) {
|
|
30
|
+
setIsLoadingFinished(true);
|
|
31
|
+
}
|
|
28
32
|
}
|
|
29
33
|
}, stepTime);
|
|
30
34
|
return () => clearInterval(intervalId);
|
|
@@ -32,8 +36,8 @@ export const ProgressBar = ({ value = 0, max = 100, size = 'md', showValue = tru
|
|
|
32
36
|
else {
|
|
33
37
|
setPercent(validPercentage);
|
|
34
38
|
}
|
|
35
|
-
}, [animated, validPercentage]);
|
|
36
|
-
return (React.createElement("div", { className: styles[
|
|
39
|
+
}, [animated, validPercentage, setIsLoadingFinished, animationDuration]);
|
|
40
|
+
return (React.createElement("div", { className: styles['progress-bar--wrapper'] },
|
|
37
41
|
React.createElement("progress", { id: "linear-progress", className: progressBarClasses, value: percent, max: max }),
|
|
38
42
|
React.createElement("label", { htmlFor: "progress", className: styles['progress-bar-percentage'] }, showValue && (React.createElement(Typography, { variant: "Body1", color: '#9CA0A7', className: styles['progress-bar-percentage'] },
|
|
39
43
|
percent,
|
|
@@ -26,7 +26,7 @@ export const title = {
|
|
|
26
26
|
warning: 'Внимание',
|
|
27
27
|
info: 'Информация',
|
|
28
28
|
};
|
|
29
|
-
export const Snackbar = ({ children, type, duration = 10000, icon = true, onClose }) => {
|
|
29
|
+
export const Snackbar = ({ children, type, duration = 10000, icon = true, onClose, style }) => {
|
|
30
30
|
const [isVisible, setIsVisible] = useState(true);
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
if (duration > 0) {
|
|
@@ -44,7 +44,7 @@ export const Snackbar = ({ children, type, duration = 10000, icon = true, onClos
|
|
|
44
44
|
if (!isVisible)
|
|
45
45
|
return null;
|
|
46
46
|
const snackbarClasses = classNames(styles['snackbar-wrapper'], styles[`snackbar--${type}`]);
|
|
47
|
-
return (React.createElement("div", { className: snackbarClasses },
|
|
47
|
+
return (React.createElement("div", { className: snackbarClasses, style: style },
|
|
48
48
|
React.createElement("div", { className: styles['snackbar-textAndIcon'] },
|
|
49
49
|
icon && icons[type],
|
|
50
50
|
React.createElement("div", { className: styles['snackbar-text'] },
|
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
padding: 10px 25px;
|
|
6
6
|
gap: 20px;
|
|
7
7
|
box-sizing: border-box;
|
|
8
|
-
position: relative;
|
|
8
|
+
/* position: relative; */
|
|
9
|
+
position: fixed;
|
|
10
|
+
top: 20px;
|
|
11
|
+
left: 50%;
|
|
12
|
+
transform: translateX(-50%);
|
|
13
|
+
z-index: 1000;
|
|
14
|
+
|
|
9
15
|
min-width: 340px;
|
|
10
16
|
max-width: 500px;
|
|
11
17
|
|
|
12
18
|
border-radius: 15px;
|
|
13
|
-
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1)
|
|
19
|
+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
|
|
20
|
+
background-color: var(--white);
|
|
14
21
|
}
|
|
15
22
|
.snackbar-textAndIcon {
|
|
16
23
|
gap: 10px;
|
|
@@ -26,16 +33,16 @@
|
|
|
26
33
|
width: 100%;
|
|
27
34
|
}
|
|
28
35
|
.snackbar--success {
|
|
29
|
-
background-color: rgba(
|
|
36
|
+
background-color: rgba(216, 244, 223, 0.9);
|
|
30
37
|
}
|
|
31
38
|
.snackbar--error {
|
|
32
|
-
background-color: rgba(255,
|
|
39
|
+
background-color: rgba(255, 214, 212, 0.9);
|
|
33
40
|
}
|
|
34
41
|
.snackbar--warning {
|
|
35
|
-
background-color: rgba(255,
|
|
42
|
+
background-color: rgba(255, 240, 195, 0.9);
|
|
36
43
|
}
|
|
37
44
|
.snackbar--info {
|
|
38
|
-
background-color:
|
|
45
|
+
background-color: rgba(244, 244, 244, 0.9);
|
|
39
46
|
border: 1px solid #f2f2f7;
|
|
40
47
|
}
|
|
41
48
|
.button {
|
package/dist/index.d.ts
CHANGED
|
@@ -14,8 +14,10 @@ export { Tabs as Tabs } from './components/Tabs/Tabs';
|
|
|
14
14
|
export { Tag as Tag } from './components/Tag/Tag';
|
|
15
15
|
export { ToggleButton as ToggleButton } from './components/ToggleButton/ToggleButton';
|
|
16
16
|
export { Typography as Typography } from './components/Typography/Typography';
|
|
17
|
-
export { FileItem as FileItem } from './components/FileItem/FileItem';
|
|
18
17
|
export { FileAttach as FileAttach } from './components/FileAttach/FileAttach';
|
|
18
|
+
export { FileListAttaсhed as FileListAttaсhed } from './components/FileListAttached/FileListAttaсhed';
|
|
19
|
+
export { FileItem as FileItem } from './components/FileItem/FileItem';
|
|
20
|
+
export { FileLoader as FileLoader } from './components/FileLoader/FileLoader';
|
|
19
21
|
export { Spinner as Spinner } from './components/Spinner/Spinner';
|
|
20
22
|
export { Dialog as Dialog } from './components/Dialog/Dialog';
|
|
21
23
|
export { IconButton as IconButton } from './components/IconButton/IconButton';
|
|
@@ -24,5 +26,6 @@ export { ListItem as ListItem } from './components/ListItem/ListItem';
|
|
|
24
26
|
export { Breadcrumb as Breadcrumb } from './components/Breadcrumb/Breadcrumb';
|
|
25
27
|
export { Breadcrumbs as Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs';
|
|
26
28
|
export { Tooltip as Tooltip } from './components/Tooltip/Tooltip';
|
|
27
|
-
export type { ButtonProps, InputProps, DateInputProps, TagProps, SettingTagProps, ToggleButtonProps, BaseOptions, TOptions, DropdownProps, TypographyProps, ProgressBarProps, ProgressLoaderProps, RadioProps, TabsProps, ColorPickerProps, SnackbarProps, FileItemProps,
|
|
29
|
+
export type { ButtonProps, InputProps, DateInputProps, TagProps, SettingTagProps, ToggleButtonProps, BaseOptions, TOptions, DropdownProps, TypographyProps, ProgressBarProps, ProgressLoaderProps, RadioProps, TabsProps, ColorPickerProps, SnackbarProps, FileAttachProps, FileListAttaсhedProps, FileItemProps, FileLoaderProps, SpinnerProps, DialogProps, IconButtonProps, BaseListProps, ListProps, ListItemProps, BreadcrumbProps, BreadcrumbsProps, TooltipProps, } from './types';
|
|
28
30
|
import './fonts.css';
|
|
31
|
+
import './colors.css';
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,10 @@ export { Tabs as Tabs } from './components/Tabs/Tabs';
|
|
|
14
14
|
export { Tag as Tag } from './components/Tag/Tag';
|
|
15
15
|
export { ToggleButton as ToggleButton } from './components/ToggleButton/ToggleButton';
|
|
16
16
|
export { Typography as Typography } from './components/Typography/Typography';
|
|
17
|
-
export { FileItem as FileItem } from './components/FileItem/FileItem';
|
|
18
17
|
export { FileAttach as FileAttach } from './components/FileAttach/FileAttach';
|
|
18
|
+
export { FileListAttaсhed as FileListAttaсhed } from './components/FileListAttached/FileListAttaсhed';
|
|
19
|
+
export { FileItem as FileItem } from './components/FileItem/FileItem';
|
|
20
|
+
export { FileLoader as FileLoader } from './components/FileLoader/FileLoader';
|
|
19
21
|
export { Spinner as Spinner } from './components/Spinner/Spinner';
|
|
20
22
|
export { Dialog as Dialog } from './components/Dialog/Dialog';
|
|
21
23
|
export { IconButton as IconButton } from './components/IconButton/IconButton';
|
|
@@ -25,3 +27,4 @@ export { Breadcrumb as Breadcrumb } from './components/Breadcrumb/Breadcrumb';
|
|
|
25
27
|
export { Breadcrumbs as Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs';
|
|
26
28
|
export { Tooltip as Tooltip } from './components/Tooltip/Tooltip';
|
|
27
29
|
import './fonts.css';
|
|
30
|
+
import './colors.css';
|
package/dist/types/index.d.ts
CHANGED
|
@@ -252,6 +252,10 @@ export interface ProgressBarProps {
|
|
|
252
252
|
showValue?: boolean;
|
|
253
253
|
/** Анимация */
|
|
254
254
|
animated?: boolean;
|
|
255
|
+
/** Длительность анимации */
|
|
256
|
+
animationDuration?: number;
|
|
257
|
+
/**Для выставления флага окончания загрузки */
|
|
258
|
+
setIsLoadingFinished?: (value: boolean) => void;
|
|
255
259
|
}
|
|
256
260
|
export interface ProgressLoaderProps {
|
|
257
261
|
/** Значение */
|
|
@@ -348,27 +352,94 @@ export type SnackbarProps = {
|
|
|
348
352
|
/** Иконка */
|
|
349
353
|
icon?: boolean;
|
|
350
354
|
/** Длительность показа сообщения */
|
|
351
|
-
duration
|
|
355
|
+
duration?: number;
|
|
352
356
|
/** Функция обработки закрытия сообщения */
|
|
353
357
|
onClose?: () => void;
|
|
358
|
+
/** Стили передаваемые напрямую */
|
|
359
|
+
style?: CSSProperties;
|
|
354
360
|
};
|
|
355
|
-
export
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
export type TAttachments = {
|
|
362
|
+
id: string;
|
|
363
|
+
filename: string;
|
|
364
|
+
uri?: string;
|
|
359
365
|
size?: number;
|
|
366
|
+
createDateTime?: string;
|
|
367
|
+
updateDateTime?: string;
|
|
368
|
+
};
|
|
369
|
+
export interface FileAttachProps {
|
|
370
|
+
filesList: TAttachments[];
|
|
371
|
+
/** Максимальный размер файла */
|
|
372
|
+
maxFileSize?: number;
|
|
373
|
+
/** Максимальное количество файлов */
|
|
374
|
+
maxFileCount?: number;
|
|
375
|
+
/**Поддерживаемые форматы файлов */
|
|
376
|
+
acceptedFormats?: Accept;
|
|
377
|
+
/**Добавленные файлы */
|
|
378
|
+
addedFiles: File[];
|
|
379
|
+
/**Сосотояние для добавления файлов */
|
|
380
|
+
setAddedFiles: (addedFiles: File[]) => void;
|
|
381
|
+
/** Функция обработки скачивания файла */
|
|
382
|
+
onDownload?: (file: TAttachments) => void;
|
|
383
|
+
/** Функция обработки удаления файла */
|
|
384
|
+
onDelete?: (id: string) => void;
|
|
385
|
+
/**Разрешени на добавление файлов*/
|
|
386
|
+
canAdd?: boolean;
|
|
387
|
+
/**Разрешение на удаление файлов */
|
|
388
|
+
canDelete?: boolean;
|
|
389
|
+
/**Разрешение на скачивание файлов */
|
|
390
|
+
canDownload?: boolean;
|
|
391
|
+
/**Позиционирование блока прикрепленных файлов */
|
|
392
|
+
position?: 'left' | 'right' | 'bottom';
|
|
393
|
+
/** Язык */
|
|
394
|
+
lng?: string;
|
|
395
|
+
/** Дополнительный класс */
|
|
396
|
+
className?: string;
|
|
397
|
+
/** Стили передаваемые напрямую */
|
|
398
|
+
style?: React.CSSProperties;
|
|
399
|
+
}
|
|
400
|
+
export interface FileListAttaсhedProps {
|
|
401
|
+
/** Список прикрепленных файлов */
|
|
402
|
+
filesList: TAttachments[] | [] | undefined;
|
|
403
|
+
/** Функция обработки удаления файла */
|
|
404
|
+
onDelete?: (id: string) => void;
|
|
405
|
+
/** Функция обработки скачивания файла */
|
|
406
|
+
onDownload?: (file: TAttachments) => void;
|
|
407
|
+
/**Разрешение на удаление файлов */
|
|
408
|
+
canDelete?: boolean;
|
|
409
|
+
/**Разрешение на скачивание файлов */
|
|
410
|
+
canDownload?: boolean;
|
|
411
|
+
/**Флаг для показа информационного текста */
|
|
412
|
+
isInfoShown?: boolean;
|
|
413
|
+
/** Язык */
|
|
414
|
+
lng?: string;
|
|
415
|
+
/** Дополнительный класс */
|
|
416
|
+
className?: string;
|
|
417
|
+
/** Стили передаваемые напрямую */
|
|
418
|
+
style?: React.CSSProperties;
|
|
419
|
+
}
|
|
420
|
+
export interface FileItemProps {
|
|
421
|
+
/** Файл */
|
|
422
|
+
file: TAttachments;
|
|
360
423
|
/** Флаг загрузки файла */
|
|
361
424
|
loading?: boolean;
|
|
362
425
|
/** Текст ошибки загрузки файла */
|
|
363
426
|
error?: string;
|
|
364
427
|
/** Функция обработки скачивания файла */
|
|
365
|
-
onDownload?: () => void;
|
|
428
|
+
onDownload?: (file: TAttachments) => void;
|
|
366
429
|
/** Функция обработки удаления файла */
|
|
367
|
-
onDelete?: () => void;
|
|
430
|
+
onDelete?: (id: string) => void;
|
|
431
|
+
/**Разрешение на удаление файлов */
|
|
432
|
+
canDelete?: boolean;
|
|
433
|
+
/**Разрешение на скачивание файлов */
|
|
434
|
+
canDownload?: boolean;
|
|
368
435
|
/** Стили передаваемые напрямую */
|
|
369
436
|
style?: CSSProperties;
|
|
437
|
+
/** Флаг добавленного файла */
|
|
438
|
+
isAddedFile?: boolean;
|
|
439
|
+
/** Флаг отклоненного файла */
|
|
440
|
+
isRejectedFile?: boolean;
|
|
370
441
|
}
|
|
371
|
-
export interface
|
|
442
|
+
export interface FileLoaderProps {
|
|
372
443
|
/** Максимальный размер файла */
|
|
373
444
|
maxFileSize?: number;
|
|
374
445
|
/** Максимальное количество файлов */
|
|
@@ -379,10 +450,10 @@ export interface FileAttachProps {
|
|
|
379
450
|
addedFiles: File[];
|
|
380
451
|
/**Сосотояние для добавления файлов */
|
|
381
452
|
setAddedFiles: (addedFiles: File[]) => void;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
453
|
+
/**Разрешени на добавление файлов*/
|
|
454
|
+
canAdd?: boolean;
|
|
455
|
+
/** Язык */
|
|
456
|
+
lng?: string;
|
|
386
457
|
/** Дополнительный класс */
|
|
387
458
|
className?: string;
|
|
388
459
|
/** Стили передаваемые напрямую */
|
|
@@ -416,7 +487,7 @@ export interface IconButtonProps {
|
|
|
416
487
|
/** Заблокированная кнопка */
|
|
417
488
|
disabled?: boolean;
|
|
418
489
|
/** Callback, который будет вызван при клике по кнопке */
|
|
419
|
-
onClick: () => void;
|
|
490
|
+
onClick: (e: React.MouseEvent) => void;
|
|
420
491
|
/** Дочерние элементы */
|
|
421
492
|
children?: ReactNode;
|
|
422
493
|
/** Дополнительный класс */
|