kamotive_ui 1.2.14 → 1.2.16
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/Icons/IconAdd/IconAdd.d.ts +6 -0
- package/dist/Icons/IconAdd/IconAdd.js +5 -0
- package/dist/Icons/index.d.ts +1 -0
- package/dist/Icons/index.js +1 -0
- package/dist/components/Checkbox/Checkbox.stories.d.ts +66 -0
- package/dist/components/Checkbox/Checkbox.stories.js +75 -0
- 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 +46 -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 +10 -3
- package/dist/components/IconButton/IconButton.module.css +26 -16
- package/dist/components/Input/Input.module.css +4 -4
- package/dist/components/List/List.js +6 -4
- 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/components/Tag/Tag.d.ts +1 -0
- package/dist/components/Tag/Tag.js +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +3 -0
- package/dist/components/Tooltip/Tooltip.js +198 -0
- package/dist/components/Tooltip/Tooltip.module.css +22 -0
- package/dist/components/Typography/Typography.module.css +9 -9
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -1
- package/dist/types/index.d.ts +116 -17
- package/package.json +1 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const IconAdd = ({ color = 'inherit', htmlColor, strokeWidth, }) => {
|
|
3
|
+
return (React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: color },
|
|
4
|
+
React.createElement("path", { fill: htmlColor || 'currentColor', stroke: htmlColor || 'currentColor', strokeWidth: strokeWidth || '0', d: "M5,13V12H11V6H12V12H18V13H12V19H11V13H5Z" })));
|
|
5
|
+
};
|
package/dist/Icons/index.d.ts
CHANGED
|
@@ -19,3 +19,4 @@ export { IconColorPicker10 } from './IconColorPicker/IconColorPicker10';
|
|
|
19
19
|
export { IconFile } from './IconFile/IconFile';
|
|
20
20
|
export { IconUpload } from './IconUpload/IconUpload';
|
|
21
21
|
export { IconDownload } from './IconDownload/IconDownload';
|
|
22
|
+
export { IconAdd } from './IconAdd/IconAdd';
|
package/dist/Icons/index.js
CHANGED
|
@@ -19,3 +19,4 @@ export { IconColorPicker10 } from './IconColorPicker/IconColorPicker10';
|
|
|
19
19
|
export { IconFile } from './IconFile/IconFile';
|
|
20
20
|
export { IconUpload } from './IconUpload/IconUpload';
|
|
21
21
|
export { IconDownload } from './IconDownload/IconDownload';
|
|
22
|
+
export { IconAdd } from './IconAdd/IconAdd';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ChangeEventHandler } from 'react';
|
|
2
|
+
import type { Meta } from '@storybook/react';
|
|
3
|
+
export interface CheckboxProps {
|
|
4
|
+
/** Идентификатор */
|
|
5
|
+
checked?: boolean;
|
|
6
|
+
/** Обработчик изменения состояния */
|
|
7
|
+
onChange?: ChangeEventHandler<HTMLInputElement>;
|
|
8
|
+
/** Заблокированный чекбокс */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Размер чекбокса */
|
|
11
|
+
size?: 'sm' | 'md';
|
|
12
|
+
/** Текст лейбла */
|
|
13
|
+
label?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const meta: Meta<CheckboxProps>;
|
|
16
|
+
export default meta;
|
|
17
|
+
export declare const CheckboxOff: {
|
|
18
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
19
|
+
storyName: string;
|
|
20
|
+
};
|
|
21
|
+
export declare const CheckboxLabel: {
|
|
22
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
23
|
+
storyName: string;
|
|
24
|
+
args: {
|
|
25
|
+
label: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export declare const CheckboxChecked: {
|
|
29
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
30
|
+
storyName: string;
|
|
31
|
+
args: {
|
|
32
|
+
checked: boolean;
|
|
33
|
+
disabled: boolean;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export declare const CheckboxDisabled: {
|
|
37
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
38
|
+
storyName: string;
|
|
39
|
+
args: {
|
|
40
|
+
checked: boolean;
|
|
41
|
+
disabled: boolean;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export declare const CheckboxCheckedDisabled: {
|
|
45
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
46
|
+
storyName: string;
|
|
47
|
+
args: {
|
|
48
|
+
checked: boolean;
|
|
49
|
+
disabled: boolean;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
export declare const CheckboxCustomColor: {
|
|
53
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
54
|
+
storyName: string;
|
|
55
|
+
args: {
|
|
56
|
+
color: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
export declare const CheckboxCustomColorFilled: {
|
|
60
|
+
(argTypes: CheckboxProps): JSX.Element;
|
|
61
|
+
storyName: string;
|
|
62
|
+
args: {
|
|
63
|
+
color: string;
|
|
64
|
+
filled: boolean;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Checkbox } from './Checkbox';
|
|
3
|
+
const meta = {
|
|
4
|
+
component: Checkbox,
|
|
5
|
+
parameters: {
|
|
6
|
+
layout: 'centered',
|
|
7
|
+
},
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
decorators: [
|
|
10
|
+
(Story) => (React.createElement("div", { style: {
|
|
11
|
+
backgroundColor: 'var(--white)',
|
|
12
|
+
padding: '30px',
|
|
13
|
+
borderRadius: '10px',
|
|
14
|
+
width: '900px'
|
|
15
|
+
} },
|
|
16
|
+
React.createElement(Story, null))),
|
|
17
|
+
],
|
|
18
|
+
args: {
|
|
19
|
+
size: 'sm',
|
|
20
|
+
disabled: false,
|
|
21
|
+
},
|
|
22
|
+
argTypes: {
|
|
23
|
+
size: {
|
|
24
|
+
description: 'Свойство, позволяющее регулировать размер чекбокса',
|
|
25
|
+
control: { type: 'radio' },
|
|
26
|
+
options: ['sm', 'md'],
|
|
27
|
+
},
|
|
28
|
+
disabled: { description: 'Устанавливает атрибут disabled', control: { type: 'boolean' } },
|
|
29
|
+
checked: { description: 'Задаёт включённое состояние для компонента' },
|
|
30
|
+
label: { description: 'Текст лейбла чекбокса', type: 'string' },
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default meta;
|
|
34
|
+
export const CheckboxOff = (argTypes) => {
|
|
35
|
+
const [checked, setChecked] = useState(false);
|
|
36
|
+
const handleChange = () => {
|
|
37
|
+
setChecked(prev => !prev);
|
|
38
|
+
};
|
|
39
|
+
return (React.createElement(Checkbox, Object.assign({ checked: checked, onChange: handleChange }, argTypes)));
|
|
40
|
+
};
|
|
41
|
+
CheckboxOff.storyName = 'Checkbox по умолчанию';
|
|
42
|
+
export const CheckboxLabel = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
43
|
+
CheckboxLabel.storyName = 'Checkbox c label';
|
|
44
|
+
CheckboxLabel.args = {
|
|
45
|
+
label: 'Чекбокс',
|
|
46
|
+
};
|
|
47
|
+
export const CheckboxChecked = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
48
|
+
CheckboxChecked.storyName = 'Checkbox выбран';
|
|
49
|
+
CheckboxChecked.args = {
|
|
50
|
+
checked: true,
|
|
51
|
+
disabled: false,
|
|
52
|
+
};
|
|
53
|
+
export const CheckboxDisabled = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
54
|
+
CheckboxDisabled.storyName = 'Checkbox заблокирован';
|
|
55
|
+
CheckboxDisabled.args = {
|
|
56
|
+
checked: false,
|
|
57
|
+
disabled: true,
|
|
58
|
+
};
|
|
59
|
+
export const CheckboxCheckedDisabled = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
60
|
+
CheckboxCheckedDisabled.storyName = 'Checkbox выбран и заблокирован';
|
|
61
|
+
CheckboxCheckedDisabled.args = {
|
|
62
|
+
checked: true,
|
|
63
|
+
disabled: true,
|
|
64
|
+
};
|
|
65
|
+
export const CheckboxCustomColor = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
66
|
+
CheckboxCustomColor.storyName = 'Checkbox с кастомным цветом';
|
|
67
|
+
CheckboxCustomColor.args = {
|
|
68
|
+
color: 'red',
|
|
69
|
+
};
|
|
70
|
+
export const CheckboxCustomColorFilled = (argTypes) => React.createElement(Checkbox, Object.assign({}, argTypes));
|
|
71
|
+
CheckboxCustomColorFilled.storyName = 'Checkbox с кастомным цветом заполненный';
|
|
72
|
+
CheckboxCustomColorFilled.args = {
|
|
73
|
+
color: 'red',
|
|
74
|
+
filled: true,
|
|
75
|
+
};
|
|
@@ -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,46 @@
|
|
|
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
|
+
}
|
|
16
|
+
.fileListFiles {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 5px;
|
|
20
|
+
overflow-y: auto;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Стилизация скроллбара для WebKit (Chrome, Safari, новые версии Edge) */
|
|
24
|
+
.fileList::-webkit-scrollbar {
|
|
25
|
+
width: 6px; /* Ширина скроллбара */
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.fileList::-webkit-scrollbar-track {
|
|
29
|
+
background: rgba(0, 0, 0, 0.03); /* Светлый фон для трека */
|
|
30
|
+
border-radius: 10px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.fileList::-webkit-scrollbar-thumb {
|
|
34
|
+
background: var(--grey-medium, #c4c4c4); /* Цвет ползунка */
|
|
35
|
+
border-radius: 10px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.fileList::-webkit-scrollbar-thumb:hover {
|
|
39
|
+
background: var(--grey-dark, #a0a0a0); /* Цвет ползунка при наведении */
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Стилизация скроллбара для Firefox */
|
|
43
|
+
.fileList {
|
|
44
|
+
scrollbar-width: thin; /* "auto", "thin" или "none" */
|
|
45
|
+
scrollbar-color: var(--grey-medium, #c4c4c4) rgba(0, 0, 0, 0.03); /* цвет ползунка и трека */
|
|
46
|
+
}
|
|
@@ -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' ? (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' ? (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
|
+
};
|