kamotive_ui 2.10.5 → 8.12.25

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.
@@ -3,9 +3,10 @@ export interface FilePreview {
3
3
  file: File;
4
4
  id: string;
5
5
  preview?: string;
6
+ lng: string;
6
7
  }
7
8
  export declare const getFileIcon: (file: File) => React.JSX.Element;
8
- export declare const formatFileSize: (bytes?: number) => string;
9
+ export declare const formatFileSize: (bytes?: number, lng?: string) => string;
9
10
  interface AttachedFilesProps {
10
11
  files: FilePreview[];
11
12
  onDelete?: (id: string) => void;
@@ -14,6 +15,7 @@ interface AttachedFilesProps {
14
15
  className?: string;
15
16
  isEdit?: boolean;
16
17
  allowDownload?: boolean;
18
+ lng: string;
17
19
  }
18
20
  export declare const AttachedFilesPreview: React.FC<AttachedFilesProps>;
19
21
  export {};
@@ -31,15 +31,18 @@ export const getFileIcon = (file) => {
31
31
  return React.createElement(IconFileDefault, null);
32
32
  };
33
33
  // Функция для форматирования размера файла
34
- export const formatFileSize = (bytes) => {
35
- if (!bytes || bytes === 0)
36
- return '0 Bytes';
34
+ export const formatFileSize = (bytes, lng) => {
35
+ if (!bytes || bytes === 0) {
36
+ return lng === 'ru' || (lng === null || lng === void 0 ? void 0 : lng.includes('ru')) ? '0 Байт' : '0 Bytes';
37
+ }
37
38
  const k = 1024;
38
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
39
+ const sizesEn = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
40
+ const sizesRu = ['Байт', 'КБ', 'МБ', 'ГБ', 'ТБ'];
39
41
  const i = Math.floor(Math.log(bytes) / Math.log(k));
42
+ const sizes = lng === 'ru' || (lng === null || lng === void 0 ? void 0 : lng.includes('ru')) ? sizesRu : sizesEn;
40
43
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
41
44
  };
42
- export const AttachedFilesPreview = ({ files, onDelete, onDownload, style, className, isEdit, allowDownload = true, }) => {
45
+ export const AttachedFilesPreview = ({ files, onDelete, onDownload, style, className, isEdit, allowDownload = true, lng, }) => {
43
46
  const handleDelete = (event, id) => {
44
47
  event.stopPropagation();
45
48
  if (onDelete) {
@@ -65,5 +68,5 @@ export const AttachedFilesPreview = ({ files, onDelete, onDownload, style, class
65
68
  React.createElement("div", { onClick: allowDownload ? () => handleDownload(file.file) : undefined, className: styles.filePreview },
66
69
  file.preview ? (React.createElement("img", { src: file.preview, alt: file.file.name, className: styles.previewImage })) : (React.createElement("div", { className: styles.previewImage }, getFileIcon(file.file))),
67
70
  isEdit && (React.createElement("button", { className: styles.removeFileButton, onClick: (event) => handleDelete(event, file.id) }, "\u2715")),
68
- React.createElement("div", { className: styles.fileSize }, formatFileSize(file.file.size))))))));
71
+ React.createElement("div", { className: styles.fileSize }, formatFileSize(file.file.size, lng))))))));
69
72
  };
@@ -6,7 +6,7 @@ import { TextEditor } from '../TextEditor/TextEditor';
6
6
  import { AttachedFilesPreview } from '../AttachedFilesPreview/AttachedFilesPreview';
7
7
  import { IconAccount, IconDeleteFilled, IconPencilFilled } from '../../Icons';
8
8
  import { IconButton } from '../IconButton/IconButton';
9
- export const Comment = ({ id, value, style, className, username, avatar, creationDate, canAttachFiles = false, files = [], canEdit = false, isEdit = false, label, error = false, helperText, onChange, onSubmit, onDelete, }) => {
9
+ export const Comment = ({ id, value, style, className, username, avatar, creationDate, canAttachFiles = false, files = [], canEdit = false, isEdit = false, label, error = false, helperText, onChange, onSubmit, onDelete, lng = 'ru', }) => {
10
10
  const [commentText, setCommentText] = useState(value || '');
11
11
  const [isEditMode, setIsEditMode] = useState(isEdit);
12
12
  const [attachedFiles, setAttachedFiles] = useState(files);
@@ -45,10 +45,10 @@ export const Comment = ({ id, value, style, className, username, avatar, creatio
45
45
  React.createElement(Typography, { variant: "Body2-Medium", className: labelClasses }, username),
46
46
  React.createElement(Typography, { variant: "Caption", className: styles.label, style: { color: '#8E8E93' } }, creationDate))),
47
47
  canEdit && (React.createElement("div", { className: styles.iconsWrapper },
48
- React.createElement(IconButton, { icon: React.createElement(IconPencilFilled, null), onClick: handleEditClick, size: "sm", style: { aspectRatio: 0 } }),
49
- React.createElement(IconButton, { icon: React.createElement(IconDeleteFilled, null), onClick: handleDeleteClick, size: "sm", style: { aspectRatio: 0 } })))),
50
- isEditMode ? (React.createElement(TextEditor, { defaultValue: commentText, onSubmit: handleSubmit, onChange: handleChange, error: error, helperText: helperText, files: attachedFiles, canAttachFiles: canAttachFiles })) : (React.createElement("div", { className: styles.commentWrapper },
51
- attachedFiles.length > 0 && (React.createElement(AttachedFilesPreview, { files: attachedFiles, className: styles.attachedFilesContainer })),
48
+ React.createElement(IconButton, { icon: React.createElement(IconPencilFilled, null), onClick: handleEditClick, size: "sm", style: { aspectRatio: 0, width: '30px', height: '30px' } }),
49
+ React.createElement(IconButton, { icon: React.createElement(IconDeleteFilled, null), onClick: handleDeleteClick, size: "sm", style: { aspectRatio: 0, width: '30px', height: '30px' } })))),
50
+ isEditMode ? (React.createElement(TextEditor, { defaultValue: commentText, onSubmit: handleSubmit, onChange: handleChange, error: error, helperText: helperText, files: attachedFiles, canAttachFiles: canAttachFiles, lng: lng })) : (React.createElement("div", { className: styles.commentWrapper },
51
+ attachedFiles.length > 0 && (React.createElement(AttachedFilesPreview, { files: attachedFiles, className: styles.attachedFilesContainer, lng: lng })),
52
52
  React.createElement("div", { id: id, className: inputClassess, dangerouslySetInnerHTML: { __html: commentText || '' } }))),
53
53
  error && helperText && (React.createElement(Typography, { variant: "Caption", className: classNames(styles.helperText) }, helperText))));
54
54
  };
@@ -25,7 +25,7 @@
25
25
  .iconsWrapper {
26
26
  display: flex;
27
27
  flex-direction: row;
28
- gap: 15px;
28
+ gap: 5px;
29
29
  }
30
30
 
31
31
  .commentWrapper {
@@ -168,11 +168,11 @@ export const DropdownListItem = ({ item, getOptionLabel, size = 'md', selectedIt
168
168
  (item === null || item === void 0 ? void 0 : item.isDivider) && React.createElement("div", { className: styles.divider })),
169
169
  hasChildren && (React.createElement("div", { className: styles.nestedMenu }, (_a = item.children) === null || _a === void 0 ? void 0 : _a.map((child, childIndex) => {
170
170
  var _a;
171
- return (React.createElement(DropdownListItem, { key: (_a = child === null || child === void 0 ? void 0 : child.key) !== null && _a !== void 0 ? _a : `${index}-${childIndex}`, item: child, getOptionLabel: getOptionLabel, size: size, selectedItem: selectedItem, onChange: onChange, isActive: false, activeIndex: activeIndex, index: childIndex, isChild: true }));
171
+ return (React.createElement(DropdownListItem, { key: (_a = child === null || child === void 0 ? void 0 : child.id) !== null && _a !== void 0 ? _a : `${index}-${childIndex}`, item: child, getOptionLabel: getOptionLabel, size: size, selectedItem: selectedItem, onChange: onChange, isActive: false, activeIndex: activeIndex, index: childIndex, isChild: true }));
172
172
  })))));
173
173
  return showTooltip ? (React.createElement(Tooltip, { label: ((_b = getComparisonValue(item, getOptionLabel)) === null || _b === void 0 ? void 0 : _b.toString()) || '', position: "bottom-left" }, itemContent)) : (itemContent);
174
174
  };
175
- export const Dropdown = ({ options, id, label, placeholder, required = false, value, defaultValue, onChange, getOptionLabel, variant = 'text', size = 'lg', style, className, isLeftLabel = false, isDivider = false, disabled = false, readOnly = false, isOpened = false, error = false, helperText, onClick, onBlur, onFocus, onClose, clearable = true, enableAutocomplete = false, noOptionsText = 'Нет вариантов для выбора', lng = 'ru', }) => {
175
+ export const Dropdown = ({ options, id, label, placeholder, required = false, value, defaultValue, onChange, showLoadMore = false, loadMore, getOptionLabel, variant = 'text', size = 'lg', style, className, isLeftLabel = false, isDivider = false, disabled = false, readOnly = false, isOpened = false, error = false, helperText, onClick, onBlur, onFocus, onClose, clearable = true, enableAutocomplete = false, noOptionsText = 'Нет вариантов для выбора', lng = 'ru', }) => {
176
176
  const [isOpen, setIsOpen] = useState(isOpened);
177
177
  const [modifiedOptions, setModifiedOptions] = useState([]);
178
178
  const [selectedItem, setSelectedItem] = useState(null);
@@ -373,12 +373,18 @@ export const Dropdown = ({ options, id, label, placeholder, required = false, va
373
373
  };
374
374
  const getDropdownMenu = () => {
375
375
  const optionsToRender = enableAutocomplete && searchValue ? filteredOptions : modifiedOptions;
376
- const menu = isOpen && (React.createElement("div", { className: dropdownClassess }, optionsToRender && optionsToRender.length > 0 ? (optionsToRender.map((optionsToRender, index) => {
377
- var _a;
378
- return (React.createElement(DropdownListItem, { key: (_a = optionsToRender === null || optionsToRender === void 0 ? void 0 : optionsToRender.key) !== null && _a !== void 0 ? _a : index, item: optionsToRender, getOptionLabel: getOptionLabel, size: size, selectedItem: selectedItem, variant: variant, onChange: onChangeHandler, isActive: activeIndex === index, activeIndex: activeIndex, index: index }));
379
- })) : (React.createElement("div", { className: `${styles['item-container']} ${styles['item-block']}`, style: { paddingLeft: '15px' } }, lng === 'ru' || lng.includes('ru')
380
- ? noOptionsText || 'Нет вариантов для выбора'
381
- : noOptionsText || 'No options to select'))));
376
+ const menu = isOpen && (React.createElement("div", { className: dropdownClassess },
377
+ optionsToRender && optionsToRender.length > 0 ? (optionsToRender.map((optionsToRender, index) => {
378
+ var _a;
379
+ return (React.createElement(DropdownListItem, { key: (_a = optionsToRender === null || optionsToRender === void 0 ? void 0 : optionsToRender.id) !== null && _a !== void 0 ? _a : index, item: optionsToRender, getOptionLabel: getOptionLabel, size: size, selectedItem: selectedItem, variant: variant, onChange: onChangeHandler, isActive: activeIndex === index, activeIndex: activeIndex, index: index }));
380
+ })) : (React.createElement("div", { className: `${styles['item-container']} ${styles['item-block']}`, style: { paddingLeft: '15px' } }, lng === 'ru' || lng.includes('ru')
381
+ ? noOptionsText || 'Нет вариантов для выбора'
382
+ : noOptionsText || 'No options to select')),
383
+ showLoadMore && loadMore && React.createElement("div", { className: styles[`loadMore`], onClick: (e) => {
384
+ e.stopPropagation();
385
+ e.preventDefault();
386
+ loadMore();
387
+ } }, 'Загрузить еще')));
382
388
  return isOpen ? menu : null;
383
389
  };
384
390
  useEffect(() => {
@@ -337,3 +337,16 @@
337
337
  grid-template-columns: 1fr 20px;
338
338
  justify-content: space-between;
339
339
  }
340
+ /* Кнопка Загрузить еще */
341
+ .loadMore {
342
+ /* align-self: flex-end; */
343
+ padding: 6px;
344
+ margin-left: 10px;
345
+ font-size: 12px;
346
+ color: var(--blue-secondary);
347
+ cursor: pointer;
348
+
349
+ }
350
+ .loadMore:hover {
351
+ color: var(--blue-dark);
352
+ }
@@ -3,15 +3,18 @@ import styles from './FileAttach.module.css';
3
3
  import classNames from 'classnames';
4
4
  import { FileLoader } from '../FileLoader/FileLoader';
5
5
  import { FileListAttaсhed } from '../FileListAttached/FileListAttaсhed';
6
- export const FileAttach = ({ filesList = [], maxFileSize = 2, maxFileCount = 10, acceptedFormats = {
6
+ export const FileAttach = ({ filesList = [], maxFileSize = 2, maxFileCount = 10, maxFileName = 0, acceptedFormats = {
7
7
  'image/*': ['.png', '.gif', '.jpeg', '.jpg'],
8
8
  'application/pdf': ['.pdf'],
9
9
  'application/msword': ['.doc', '.docx'],
10
+ 'model/gltf-binary': ['.glb'],
11
+ 'application/octet-stream': ['.prt', '.step', '.stp'],
12
+ 'text/plain': ['.syslog'],
10
13
  }, addedFiles, setAddedFiles, onDownload, onDelete, canAdd = true, canDelete = true, canDownload = true, position = 'bottom', lng = 'ru', className, style, fileValidator, }) => {
11
14
  const fileAttachClasses = classNames(styles['fileAttach'], className, {
12
15
  [styles[`fileAttach_position_${position}`]]: position,
13
16
  });
14
17
  return (React.createElement("div", { className: fileAttachClasses, style: style },
15
- React.createElement(FileLoader, { maxFileSize: maxFileSize, maxFileCount: maxFileCount, acceptedFormats: acceptedFormats, addedFiles: addedFiles, setAddedFiles: setAddedFiles, filesList: filesList, canAdd: canAdd, lng: lng, fileValidator: fileValidator }),
18
+ React.createElement(FileLoader, { maxFileSize: maxFileSize, maxFileCount: maxFileCount, maxFileName: maxFileName, acceptedFormats: acceptedFormats, addedFiles: addedFiles, setAddedFiles: setAddedFiles, filesList: filesList, canAdd: canAdd, lng: lng, fileValidator: fileValidator }),
16
19
  React.createElement(FileListAttaсhed, { filesList: filesList, onDelete: onDelete, onDownload: onDownload, canDelete: canDelete, canDownload: canDownload, lng: lng })));
17
20
  };
@@ -7,7 +7,7 @@ import { IconClose, IconDownload, IconFile } from '../../Icons';
7
7
  import { Tooltip } from '../Tooltip/Tooltip';
8
8
  import classNames from 'classnames';
9
9
  import { formatFileSize } from '../AttachedFilesPreview/AttachedFilesPreview';
10
- export const FileItem = ({ file, loading = false, error = '', onDownload, onDelete, canDelete = true, canDownload = true, style, isAddedFile, isRejectedFile, progressBarWidth }) => {
10
+ export const FileItem = ({ file, loading = false, error = '', onDownload, onDelete, canDelete = true, canDownload = true, style, isAddedFile, isRejectedFile, progressBarWidth, lng }) => {
11
11
  const [isLoadingFinished, setIsLoadingFinished] = useState(false);
12
12
  const [animationDuration, setAnimationDuration] = useState(0);
13
13
  const [maxLength, setMaxLength] = useState(30);
@@ -103,7 +103,7 @@ export const FileItem = ({ file, loading = false, error = '', onDownload, onDele
103
103
  React.createElement("div", { className: styles['fileItemName'], ref: fileNameRef },
104
104
  file.filename.length > maxLength ? (React.createElement(Tooltip, { label: file.filename, position: "bottom-center", displayDelay: 300 },
105
105
  React.createElement(Typography, { variant: "Body1", color: "var(--text-dark)" }, croppedName(file.filename)))) : (React.createElement(Typography, { variant: "Body1", color: "var(--text-dark)" }, croppedName(file.filename))),
106
- file.size !== 0 && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, formatFileSize(file.size))))),
106
+ file.size !== 0 && (React.createElement(Typography, { variant: "Caption", color: "var(--grey-medium)" }, formatFileSize(file.size, lng))))),
107
107
  React.createElement("div", { className: styles['fileItemActions'] },
108
108
  !(isAddedFile || isRejectedFile) && canDownload && (React.createElement(IconButton, { className: styles.fileIcon, icon: React.createElement(IconDownload, null), onClick: (e) => handleDownloadClick(e, file), color: "var(--icons-grey)", size: "sm" })),
109
109
  canDelete && (React.createElement(IconButton, { className: styles.fileIcon, icon: React.createElement(IconClose, null), onClick: (e) => handleDeleteClick(e, file.id || ''), color: "var(--icons-grey)", size: "sm" })))),
@@ -10,5 +10,5 @@ export const FileListAttaсhed = ({ filesList, onDelete, onDownload, canDelete,
10
10
  return (React.createElement("div", { className: classNames(styles['fileList'], className), style: style },
11
11
  isInfoShown &&
12
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 }))))));
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, lng: lng }))))));
14
14
  };
@@ -1,3 +1,3 @@
1
- import { FC } from 'react';
2
- import { FileLoaderProps } from '../../types';
3
- export declare const FileLoader: FC<FileLoaderProps>;
1
+ import React from 'react';
2
+ import { FileLoaderHandle, FileLoaderProps } from '../../types';
3
+ export declare const FileLoader: React.ForwardRefExoticComponent<FileLoaderProps & React.RefAttributes<FileLoaderHandle>>;
@@ -1,23 +1,34 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
2
2
  import { useDropzone } from 'react-dropzone';
3
3
  import styles from './FileLoader.module.css';
4
4
  import { Typography } from '../Typography/Typography';
5
5
  import { IconUpload } from '../../Icons';
6
6
  import { FileItem } from '../FileItem/FileItem';
7
7
  import classNames from 'classnames';
8
- export const FileLoader = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats = {
8
+ export const FileLoader = forwardRef(({ maxFileSize = 2, maxFileCount = 10, maxFileName = 0, acceptedFormats = {
9
9
  'image/*': ['.png', '.gif', '.jpeg', '.jpg'],
10
10
  'application/pdf': ['.pdf'],
11
- 'application/msword': ['.doc', '.docx'],
12
- }, addedFiles, setAddedFiles, filesList = [], canAdd = true, lng = 'ru', className, style, fileValidator, progressBarWidth }) => {
11
+ 'application/msword': ['.doc', '.docx', '.log', '.syslog', '.txt'],
12
+ }, rejectedFormats, addedFiles, setAddedFiles, filesList = [], canAdd = true, lng = 'ru', className, style, fileValidator, progressBarWidth }, ref) => {
13
13
  const [isLoadingFiles, setIsLoadingFiles] = useState(false);
14
14
  const [loadingFilesNames, setLoadingFilesNames] = useState([]);
15
15
  const [errorFiles, setErrorFiles] = useState([]);
16
16
  const [addedFilesFormated, setAddedFilesFormatted] = useState([]);
17
+ useImperativeHandle(ref, () => ({
18
+ clearErrorFiles: () => {
19
+ setErrorFiles([]);
20
+ },
21
+ clearAllFiles: () => {
22
+ setErrorFiles([]);
23
+ setAddedFiles([]);
24
+ setAddedFilesFormatted([]);
25
+ setLoadingFilesNames([]);
26
+ }
27
+ }));
17
28
  const fileValidatorInner = (file) => {
18
29
  if (file.size > maxFileSize * 1024 * 1024 * 1024) {
19
30
  return {
20
- code: 'name-too-large',
31
+ code: 'size-too-large',
21
32
  message: lng === 'ru' || lng.includes('ru')
22
33
  ? `Максимальный размер файла ${maxFileSize.toFixed(0)} ГБ`
23
34
  : `Maximum file size ${maxFileSize.toFixed(0)} GB`,
@@ -43,7 +54,13 @@ export const FileLoader = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats
43
54
  message: lng === 'ru' || lng.includes('ru') ? `Максимальное количество файлов ${maxFileCount}` : `Maximum number of files ${maxFileCount}`,
44
55
  };
45
56
  }
46
- if (acceptedFormats) {
57
+ if (maxFileName && file.name.length > maxFileName) {
58
+ return {
59
+ code: 'name-too-large',
60
+ message: lng === 'ru' || lng.includes('ru') ? `Имя файла не может превышать ${maxFileName} символов` : `File name must be under ${maxFileName} symbols`,
61
+ };
62
+ }
63
+ if (acceptedFormats && !rejectedFormats) {
47
64
  const acceptedExtensions = Object.values(acceptedFormats)
48
65
  .reduce((acc, val) => acc.concat(val), []);
49
66
  const fileParts = file.name.split('.');
@@ -59,6 +76,22 @@ export const FileLoader = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats
59
76
  };
60
77
  }
61
78
  }
79
+ if (rejectedFormats) {
80
+ const rejectedExtensions = Object.values(rejectedFormats)
81
+ .reduce((acc, val) => acc.concat(val), []);
82
+ const fileParts = file.name.split('.');
83
+ const fileExtension = fileParts.length > 1
84
+ ? `.${fileParts.pop().toLowerCase()}`
85
+ : '';
86
+ if (rejectedExtensions.includes(fileExtension)) {
87
+ return {
88
+ code: 'file-invalid-type',
89
+ message: lng === 'ru' || lng.includes('ru')
90
+ ? `Файл не должен быть одного из следующих типов: ${getAcceptedFormatsString(rejectedFormats)}`
91
+ : `File must not be one of: ${getAcceptedFormatsString(rejectedFormats)}`,
92
+ };
93
+ }
94
+ }
62
95
  if (fileValidator) {
63
96
  const customValidationResult = fileValidator(file);
64
97
  if (customValidationResult) {
@@ -141,21 +174,23 @@ export const FileLoader = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats
141
174
  setLoadingFilesNames(loadingFilesNames.filter((id) => id !== id));
142
175
  };
143
176
  const acceptedFileItems = addedFilesFormated.map((file) => {
144
- return (React.createElement(FileItem, { key: file.id, file: file, loading: loadingFilesNames.includes(file.filename), onDelete: handleDeleteFiles, isAddedFile: true, progressBarWidth: progressBarWidth }));
177
+ return (React.createElement(FileItem, { key: file.id, file: file, loading: loadingFilesNames.includes(file.filename), onDelete: handleDeleteFiles, isAddedFile: true, progressBarWidth: progressBarWidth, lng: lng }));
145
178
  });
146
179
  const handleDeleteRejectedFile = (id) => {
147
180
  setErrorFiles(errorFiles.filter((rejection) => rejection.file.id !== id));
148
181
  };
149
- const fileRejectionItems = errorFiles.map(({ file, errors }) => (React.createElement(FileItem, { key: file.id, file: file, error: errors[0].message, onDelete: handleDeleteRejectedFile, isRejectedFile: true })));
182
+ const fileRejectionItems = errorFiles.map(({ file, errors }) => (React.createElement(FileItem, { key: file.id, file: file, error: errors[0].message, onDelete: handleDeleteRejectedFile, isRejectedFile: true, lng: lng })));
150
183
  // Функция для получения всех доступных форматов в виде строки
151
184
  const getAcceptedFormatsString = (acceptedFormats) => {
152
- const formats = [];
185
+ const uniqueFormats = new Set();
153
186
  for (const key in acceptedFormats) {
154
187
  if (acceptedFormats.hasOwnProperty(key)) {
155
- formats.push(...acceptedFormats[key].map((format) => format.replace('.', '')));
188
+ acceptedFormats[key].forEach((format) => {
189
+ uniqueFormats.add(format.replace('.', ''));
190
+ });
156
191
  }
157
192
  }
158
- return formats.join(', ');
193
+ return Array.from(uniqueFormats).join(', ');
159
194
  };
160
195
  useEffect(() => {
161
196
  if (addedFiles.length === 0) {
@@ -189,9 +224,9 @@ export const FileLoader = ({ maxFileSize = 2, maxFileCount = 10, acceptedFormats
189
224
  React.createElement("br", null)))),
190
225
  maxFileCount &&
191
226
  (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`}`))))),
192
- acceptedFormats &&
193
- (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)}`))),
227
+ acceptedFormats && !rejectedFormats && (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" }, `${lng === 'ru' || lng.includes('ru') ? 'Поддерживаемые форматы:' : 'Supported formats:'} ${getAcceptedFormatsString(acceptedFormats)}`)),
228
+ rejectedFormats && (React.createElement(Typography, { variant: "Body2", color: "var(--grey-medium)" }, `${lng === 'ru' || lng.includes('ru') ? 'Неподдерживаемые форматы:' : 'Unsupported formats:'} ${getAcceptedFormatsString(rejectedFormats)}`)),
194
229
  (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'] },
195
230
  acceptedFileItems,
196
231
  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"))));
197
- };
232
+ });
@@ -7,6 +7,7 @@
7
7
  align-items: center;
8
8
  justify-content: center;
9
9
  aspect-ratio: 1 / 1;
10
+ border-radius: var(--default-border-radius, 50%);
10
11
  }
11
12
 
12
13
  .iconButton--sm svg {
@@ -23,7 +23,7 @@ const getElementFromRange = (range) => {
23
23
  const container = range.commonAncestorContainer;
24
24
  return container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
25
25
  };
26
- export const TextEditor = ({ label, onSubmit, onChange, defaultValue, error, helperText, canAttachFiles = false, files, required, className, isButtonDisabled, }) => {
26
+ export const TextEditor = ({ label, onSubmit, onChange, defaultValue, error, helperText, canAttachFiles = false, files, required, className, isButtonDisabled, lng = 'ru', }) => {
27
27
  const editorRef = useRef(null);
28
28
  const uploaderRef = useRef(null);
29
29
  const submitButtonRef = useRef(null);
@@ -293,6 +293,7 @@ export const TextEditor = ({ label, onSubmit, onChange, defaultValue, error, hel
293
293
  file,
294
294
  id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
295
295
  preview: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,
296
+ lng,
296
297
  }));
297
298
  setAttachedFiles((prev) => [...prev, ...newAttachedFiles]);
298
299
  event.target.value = '';
@@ -535,7 +536,7 @@ export const TextEditor = ({ label, onSubmit, onChange, defaultValue, error, hel
535
536
  return (React.createElement("div", { className: wrapperClassess },
536
537
  label && (React.createElement(Typography, { variant: "Caption", className: labelClasses }, label)),
537
538
  React.createElement("div", { className: inputClassess },
538
- attachedFiles.length > 0 && (React.createElement(AttachedFilesPreview, { files: attachedFiles, onDelete: (id) => removeAttachedFile(id), className: styles.attachedFilesContainer, isEdit: true })),
539
+ attachedFiles.length > 0 && (React.createElement(AttachedFilesPreview, { files: attachedFiles, onDelete: (id) => removeAttachedFile(id), className: styles.attachedFilesContainer, isEdit: true, lng: lng })),
539
540
  React.createElement("div", { ref: editorRef }),
540
541
  canAttachFiles && (React.createElement("input", { ref: uploaderRef, type: "file", style: { display: 'none' }, multiple: true, onChange: handleUploadFiles, accept: ACCEPTED_FILE_TYPES }))),
541
542
  error && helperText && (React.createElement(Typography, { variant: "Caption", className: classNames(styles.helperText) }, helperText))));
@@ -186,6 +186,10 @@ export interface DropdownProps {
186
186
  defaultValue?: string | number | TOptions | null;
187
187
  /** Callback, который будет вызван при изменении значения */
188
188
  onChange?: (event: any, value: string | number | TOptions | null) => void;
189
+ /** Флаг, является ли выпадающий список пагинированным */
190
+ showLoadMore?: boolean;
191
+ /** Функция для загрузки списка при пагинированных данных */
192
+ loadMore?: () => void;
189
193
  /** Функция для получения текста опции */
190
194
  getOptionLabel?: (option: TOptions) => string;
191
195
  /** Вариaнты выпадающего списка(текст + иконка, текст)' */
@@ -383,6 +387,8 @@ export interface FileAttachProps {
383
387
  maxFileSize?: number;
384
388
  /** Максимальное количество файлов */
385
389
  maxFileCount?: number;
390
+ /** Максимальное количество символов в названии файла */
391
+ maxFileName?: number;
386
392
  /**Поддерживаемые форматы файлов */
387
393
  acceptedFormats?: Accept;
388
394
  /**Добавленные файлы */
@@ -453,14 +459,20 @@ export interface FileItemProps {
453
459
  isRejectedFile?: boolean;
454
460
  /** Ширина прогресс бара */
455
461
  progressBarWidth?: string;
462
+ /** Язык интерфейса для типов данных*/
463
+ lng?: string;
456
464
  }
457
465
  export interface FileLoaderProps {
458
466
  /** Максимальный размер файла */
459
467
  maxFileSize?: number;
460
468
  /** Максимальное количество файлов */
461
469
  maxFileCount?: number;
470
+ /** Максимальное количество символов в названии файла */
471
+ maxFileName?: number;
462
472
  /**Поддерживаемые форматы файлов */
463
473
  acceptedFormats?: Accept;
474
+ /** Неподдерживаемые форматы файлов */
475
+ rejectedFormats?: Accept;
464
476
  /**Добавленные файлы */
465
477
  addedFiles: File[];
466
478
  /**Сосотояние для добавления файлов */
@@ -480,6 +492,10 @@ export interface FileLoaderProps {
480
492
  /** Ширина прогресс бара */
481
493
  progressBarWidth?: string;
482
494
  }
495
+ export interface FileLoaderHandle {
496
+ clearErrorFiles: () => void;
497
+ clearAllFiles: () => void;
498
+ }
483
499
  export interface DialogProps {
484
500
  /** Флаг открытия окна */
485
501
  open: boolean;
@@ -606,6 +622,8 @@ export interface TextEditorProps {
606
622
  required?: boolean;
607
623
  className?: string;
608
624
  isButtonDisabled?: boolean;
625
+ /** Язык */
626
+ lng?: string;
609
627
  }
610
628
  export interface CommentProps {
611
629
  /** Идентификатор элемента */
@@ -635,6 +653,8 @@ export interface CommentProps {
635
653
  onChange?: (value: string, files: FilePreview[]) => void;
636
654
  onSubmit?: (value: string, files: FilePreview[]) => void;
637
655
  onDelete?: (id: string) => void;
656
+ /** Язык */
657
+ lng?: string;
638
658
  }
639
659
  export interface LinkProps {
640
660
  /**Гипертекстовая ссылка */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kamotive_ui",
3
- "version": "2.10.5",
3
+ "version": "8.12.25",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -27,7 +27,7 @@
27
27
  "react": "^18.3.1",
28
28
  "react-datepicker": "^8.2.1",
29
29
  "react-dom": "^18.3.1",
30
- "react-dropzone": "^14.3.5"
30
+ "react-dropzone": "^14.3.8"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@chromatic-com/storybook": "^3.2.2",