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.
@@ -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
- /* font-size: 22px; */
259
- font-size: 12px;
253
+ font-size: 12px !important;
260
254
  }
261
255
 
262
256
  .item-selected {
@@ -1,80 +1,17 @@
1
- import React, { useState } from '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
- export const FileAttach = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats = {
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, disabled = false, className, style, }) => {
13
- const [errorFiles, setErrorFiles] = useState([]);
14
- const fileValidator = (file) => {
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
- const acceptedFileItems = addedFiles.map((file, index) => (React.createElement(FileItem, { name: file.name, size: file.size, onDelete: () => deleteAcceptedFile(addedFiles, setAddedFiles, file.name), onDownload: onDownload, key: index })));
46
- const fileRejectionItems = errorFiles.map(({ file, errors }) => (React.createElement(FileItem, { name: file.name, size: file.size, error: errors[0].message, onDelete: () => deleteRejectedFile(errorFiles, setErrorFiles, file.name), key: file.path })));
47
- const deleteAcceptedFile = (addedFiles, setAddedFiles, fileName) => {
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
- display: flex;
3
- flex-direction: column;
4
- height: inherit;
5
- gap: 10px;
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
- .dropzone {
9
- display: flex;
10
- flex-direction: column;
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
- export const FileItem = ({ name, size = 0, loading = false, error = '', onDownload, onDelete, style }) => {
8
- return (React.createElement("div", { className: `${styles['fileItem']} ${error ? styles['error'] : ''}`, style: style },
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(Typography, { variant: "Body1", color: "var(--text-dark)" }, name),
15
- size !== 0 && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, `${(size / 1024).toFixed(1)} кБ`)))),
16
- onDownload && (React.createElement(IconButton, { icon: React.createElement(IconDownload, null), onClick: onDownload, color: 'var(--icons-grey)', className: styles['fileIcon'] })),
17
- onDelete && (React.createElement(IconButton, { icon: React.createElement(IconClose10, null), onClick: onDelete, color: 'var(--icons-grey)' }))),
18
- loading && React.createElement(ProgressBar, { animated: true, size: "sm", value: 100 }),
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
- display: flex;
4
- flex-direction: column;
5
- gap: 5px;
6
- justify-content: space-between;
7
-
8
- padding: 10px 15px;
9
- border: 1px solid var(--info-secondary);
10
- border-radius: 10px;
11
-
12
- }
13
- .error {
14
- border-color: var(--error-main);
15
- }
16
-
17
- .fileItemFile{
18
- display: flex;
19
- flex-direction: row;
20
- align-items: center;
21
- }
22
- .fileItemInfo {
23
- display: flex;
24
- flex-direction: row;
25
- gap: 5px;
26
- flex-grow: 1;
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
- flex-shrink: 0
29
+ flex-shrink: 0;
33
30
  }
34
31
 
35
32
  .fileItemName {
36
- display: flex;
37
- flex-direction: column;
38
- flex-grow: 1;
39
- align-items: flex-start;
40
- overflow: auto;
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
- stroke-width: 0.8;
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,3 @@
1
+ import { FC } from 'react';
2
+ import { FileListAttaсhedProps } from '../../types';
3
+ export declare const FileListAttaсhed: FC<FileListAttaсhedProps>;
@@ -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,3 @@
1
+ import { FC } from 'react';
2
+ import { FileLoaderProps } from '../../types';
3
+ export declare const FileLoader: FC<FileLoaderProps>;
@@ -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
- return (React.createElement("button", { className: classNames(styles['iconButton'], styles[`iconButton--${size}`], className), disabled: disabled, "aria-disabled": disabled, type: "button", onClick: onClick, style: style }, (renderIcon) &&
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
- cursor: pointer;
3
- box-sizing: border-box;
4
- background-color: var(--white);
5
- border: none;
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
- width: 14px;
10
- height: 14px;
12
+ .iconButton--sm svg {
13
+ width: 14px;
14
+ height: 14px;
11
15
  }
12
16
 
13
- .iconButton--md svg{
14
- width: 16px;
15
- height: 16px;
17
+ .iconButton--md svg {
18
+ width: 16px;
19
+ height: 16px;
16
20
  }
17
21
 
18
- .iconButton--lg svg{
19
- width: 18px;
20
- height: 18px;
22
+ .iconButton--lg svg {
23
+ width: 18px;
24
+ height: 18px;
21
25
  }
22
26
 
23
27
  .iconButton:disabled {
24
- cursor: not-allowed;
25
- box-shadow: none;
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 .lg {
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 .sm {
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 .md {
163
- font-size: 12px;
162
+ .label--left.md {
163
+ font-size: 12px !important;;
164
164
  }
165
- .label--left .sm {
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["progress-bar--wrapper"] },
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(52, 199, 89, 0.15);
36
+ background-color: rgba(216, 244, 223, 0.9);
30
37
  }
31
38
  .snackbar--error {
32
- background-color: rgba(255, 59, 48, 0.15);
39
+ background-color: rgba(255, 214, 212, 0.9);
33
40
  }
34
41
  .snackbar--warning {
35
- background-color: rgba(255, 204, 0, 0.15);
42
+ background-color: rgba(255, 240, 195, 0.9);
36
43
  }
37
44
  .snackbar--info {
38
- background-color: var(--white);
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, FileAttachProps, SpinnerProps, DialogProps, IconButtonProps, BaseListProps, ListProps, ListItemProps, BreadcrumbProps, BreadcrumbsProps, TooltipProps, } from './types';
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';
@@ -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: number;
355
+ duration?: number;
352
356
  /** Функция обработки закрытия сообщения */
353
357
  onClose?: () => void;
358
+ /** Стили передаваемые напрямую */
359
+ style?: CSSProperties;
354
360
  };
355
- export interface FileItemProps {
356
- /** Название файла */
357
- name?: string;
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 FileAttachProps {
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
- onDownload?: () => void;
384
- /**Заблокировано добавление файлов*/
385
- disabled?: boolean;
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
  /** Дополнительный класс */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kamotive_ui",
3
- "version": "1.2.15",
3
+ "version": "1.2.17",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [