@true-engineering/true-react-common-ui-kit 3.23.0 → 3.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +6 -0
  2. package/dist/components/FileInput/FileInput.d.ts +25 -0
  3. package/dist/components/FileInput/FileInput.stories.d.ts +7 -0
  4. package/dist/components/FileInput/FileInput.styles.d.ts +3 -0
  5. package/dist/components/FileInput/helpers.d.ts +2 -0
  6. package/dist/components/FileInput/index.d.ts +2 -0
  7. package/dist/components/FileItem/FileItem.d.ts +31 -0
  8. package/dist/components/FileItem/FileItem.stories.d.ts +8 -0
  9. package/dist/components/FileItem/FileItem.styles.d.ts +11 -0
  10. package/dist/components/FileItem/constants.d.ts +5 -0
  11. package/dist/components/FileItem/helpers.d.ts +4 -0
  12. package/dist/components/FileItem/index.d.ts +4 -0
  13. package/dist/components/FileItem/types.d.ts +8 -0
  14. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.styles.d.ts +1 -1
  15. package/dist/components/Icon/complexIcons/icons.d.ts +7 -0
  16. package/dist/components/Icon/helpers.d.ts +1 -1
  17. package/dist/components/Icon/icons-list.d.ts +1 -1
  18. package/dist/components/Input/Input.styles.d.ts +1 -1
  19. package/dist/components/MultiSelectList/MultiSelectList.styles.d.ts +1 -1
  20. package/dist/components/Notification/Notification.styles.d.ts +1 -1
  21. package/dist/components/SearchInput/SearchInput.stories.d.ts +1 -1
  22. package/dist/components/Select/Select.styles.d.ts +3 -3
  23. package/dist/components/TextArea/TextArea.styles.d.ts +1 -1
  24. package/dist/components/Toaster/Toaster.styles.d.ts +1 -1
  25. package/dist/components/index.d.ts +2 -0
  26. package/dist/constants/index.d.ts +1 -0
  27. package/dist/constants/mime-types.d.ts +76 -0
  28. package/dist/theme/types.d.ts +3 -1
  29. package/dist/true-react-common-ui-kit.js +16131 -15468
  30. package/dist/true-react-common-ui-kit.js.map +1 -1
  31. package/dist/true-react-common-ui-kit.umd.cjs +16132 -15470
  32. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  33. package/package.json +3 -2
  34. package/src/components/FileInput/FileInput.stories.tsx +75 -0
  35. package/src/components/FileInput/FileInput.styles.ts +80 -0
  36. package/src/components/FileInput/FileInput.tsx +147 -0
  37. package/src/components/FileInput/helpers.ts +6 -0
  38. package/src/components/FileInput/index.ts +2 -0
  39. package/src/components/FileItem/FileItem.stories.tsx +63 -0
  40. package/src/components/FileItem/FileItem.styles.ts +122 -0
  41. package/src/components/FileItem/FileItem.tsx +157 -0
  42. package/src/components/FileItem/constants.ts +29 -0
  43. package/src/components/FileItem/helpers.ts +27 -0
  44. package/src/components/FileItem/index.ts +4 -0
  45. package/src/components/FileItem/types.ts +11 -0
  46. package/src/components/Icon/complexIcons/fileExcel.svg +11 -0
  47. package/src/components/Icon/complexIcons/fileImage.svg +13 -0
  48. package/src/components/Icon/complexIcons/fileOther.svg +10 -0
  49. package/src/components/Icon/complexIcons/filePdf.svg +11 -0
  50. package/src/components/Icon/complexIcons/fileWord.svg +11 -0
  51. package/src/components/Icon/complexIcons/fileXml.svg +13 -0
  52. package/src/components/Icon/complexIcons/fileZip.svg +16 -0
  53. package/src/components/Icon/complexIcons/icons.ts +14 -0
  54. package/src/components/Icon/icons-list.ts +8 -0
  55. package/src/components/ThemedPreloader/components/DotsPreloader/DotsPreloader.styles.ts +3 -2
  56. package/src/components/index.ts +2 -0
  57. package/src/constants/index.ts +1 -0
  58. package/src/constants/mime-types.ts +77 -0
  59. package/src/theme/types.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true-engineering/true-react-common-ui-kit",
3
- "version": "3.23.0",
3
+ "version": "3.24.0",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -43,10 +43,11 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@floating-ui/react": "0.26.12",
46
- "@true-engineering/true-react-platform-helpers": "0.0.6",
46
+ "@true-engineering/true-react-platform-helpers": "0.1.0",
47
47
  "clsx": "1.2.1",
48
48
  "country-flag-icons": "1.5.5",
49
49
  "date-fns": "2.29.3",
50
+ "filesize": "10.1.1",
50
51
  "hex-to-rgba": "2.0.1",
51
52
  "jss": "10.9.2",
52
53
  "lodash-es": "4.17.21",
@@ -0,0 +1,75 @@
1
+ import { format } from 'date-fns';
2
+ import { ru } from 'date-fns/locale';
3
+ import { ComponentStory } from '@storybook/react';
4
+ import { mimeTypes } from '../../constants';
5
+ import { FileItem } from '../FileItem';
6
+ import { Icon } from '../Icon';
7
+ import { FileInput } from './FileInput';
8
+
9
+ export default {
10
+ title: 'Inputs/FileInput',
11
+ component: FileInput,
12
+ };
13
+
14
+ const description = 'Форматы файла: xlsx, xlsm. Размер — до 1 МБ';
15
+ const text = (
16
+ <div style={{ display: 'flex', alignItems: 'center' }}>
17
+ <span style={{ display: 'inline-block', width: 20, height: 20, marginRight: 8 }}>
18
+ <Icon type="upload" />
19
+ </span>
20
+ Перетащите или выберите файл
21
+ </div>
22
+ );
23
+
24
+ const handleAdd = (newFiles: File[]) =>
25
+ console.log(
26
+ 'added:',
27
+ newFiles.map(({ name }) => name),
28
+ );
29
+
30
+ const Template: ComponentStory<typeof FileInput> = (args) => (
31
+ <div style={{ width: 652 }}>
32
+ <FileInput {...args} />
33
+ </div>
34
+ );
35
+
36
+ export const Default = Template.bind({});
37
+
38
+ Default.args = {
39
+ label: 'Скан документа',
40
+ isDragAndDropDisabled: false,
41
+ isRequired: true,
42
+ isReadOnly: false,
43
+ isMultipleDisabled: false,
44
+ isDisabled: false,
45
+ isActive: false,
46
+ onAdd: handleAdd,
47
+ tweakStyles: {
48
+ fileList: {
49
+ '& > div': {
50
+ width: '270px',
51
+ },
52
+ },
53
+ },
54
+ description,
55
+ text,
56
+ fileList: (
57
+ <>
58
+ <FileItem
59
+ fileInfo={{ name: 'file1', type: mimeTypes.jpeg, size: 10000 }}
60
+ metadata={<span>Обновлен {format(new Date(), 'dd MMM, HH:mm', { locale: ru })}</span>}
61
+ />
62
+ <FileItem
63
+ fileInfo={{ name: 'file 2', type: mimeTypes.xls }}
64
+ metadata={<span>Обновлен {format(new Date(), 'dd MMM, HH:mm', { locale: ru })}</span>}
65
+ isDisabled
66
+ />
67
+ </>
68
+ ),
69
+ };
70
+
71
+ Default.parameters = {
72
+ controls: {
73
+ exclude: ['onAdd', 'tweakStyles'],
74
+ },
75
+ };
@@ -0,0 +1,80 @@
1
+ import { ITweakStyles, animations, createThemedStyles } from '../../theme';
2
+
3
+ export const useStyles = createThemedStyles('FileInput', {
4
+ root: {
5
+ display: 'flex',
6
+ flexDirection: 'column',
7
+ width: '100%',
8
+ },
9
+
10
+ label: {
11
+ position: 'relative',
12
+ width: 'fit-content',
13
+ fontSize: 16,
14
+ marginBottom: 12,
15
+ },
16
+
17
+ requiredLabel: {
18
+ '&:after': {
19
+ content: '""',
20
+ position: 'absolute',
21
+ right: -8,
22
+ top: 4,
23
+ transform: 'translate(0, -50%)',
24
+ width: 6,
25
+ height: 6,
26
+ borderRadius: '50%',
27
+ },
28
+ },
29
+
30
+ description: {
31
+ marginBottom: 8,
32
+ fontSize: 12,
33
+ },
34
+
35
+ text: {
36
+ padding: [16, 0],
37
+ },
38
+
39
+ inputWrapper: {
40
+ position: 'relative',
41
+ display: 'flex',
42
+ flexDirection: 'column',
43
+ alignItems: 'center',
44
+ boxSizing: 'border-box',
45
+ marginBottom: 8,
46
+ transition: animations.defaultTransition,
47
+ transitionProperty: 'background-color, color',
48
+ },
49
+
50
+ inputLabel: {
51
+ position: 'absolute',
52
+ inset: 0,
53
+ cursor: 'pointer',
54
+
55
+ '& input': {
56
+ opacity: 0,
57
+ width: 1,
58
+ height: 1,
59
+ zIndex: -1,
60
+ },
61
+ },
62
+
63
+ dragged: {},
64
+
65
+ active: {},
66
+
67
+ disabled: {
68
+ cursor: 'not-allowed',
69
+ pointerEvents: 'none',
70
+ },
71
+
72
+ fileList: {
73
+ display: 'flex',
74
+ flexWrap: 'wrap',
75
+ columnGap: 6,
76
+ rowGap: 12,
77
+ },
78
+ });
79
+
80
+ export type IFileInputStyles = ITweakStyles<typeof useStyles>;
@@ -0,0 +1,147 @@
1
+ import {
2
+ ReactNode,
3
+ useState,
4
+ InputHTMLAttributes,
5
+ forwardRef,
6
+ ChangeEventHandler,
7
+ DragEventHandler,
8
+ } from 'react';
9
+ import clsx from 'clsx';
10
+ import {
11
+ addDataTestId,
12
+ isEmpty,
13
+ isReactNodeNotEmpty,
14
+ } from '@true-engineering/true-react-platform-helpers';
15
+ import { addDataAttributes } from '../../helpers';
16
+ import { ICommonProps } from '../../types';
17
+ import { blockEvent } from './helpers';
18
+ import { IFileInputStyles, useStyles } from './FileInput.styles';
19
+
20
+ export interface IFileInputProps extends ICommonProps<IFileInputStyles> {
21
+ fileList?: ReactNode;
22
+ label?: ReactNode;
23
+ text: ReactNode;
24
+ description?: ReactNode;
25
+ /** @default false */
26
+ isDragAndDropDisabled?: boolean;
27
+ /** @default false */
28
+ isRequired?: boolean;
29
+ /** @default false */
30
+ isReadOnly?: boolean;
31
+ /** @default false */
32
+ isMultipleDisabled?: boolean;
33
+ /** @default false */
34
+ isDisabled?: boolean;
35
+ /** @default false */
36
+ isActive?: boolean;
37
+ /** @default undefined */
38
+ accept?: InputHTMLAttributes<HTMLInputElement>['accept'];
39
+ onAdd?: (files: File[]) => void;
40
+ }
41
+
42
+ export const FileInput = forwardRef<HTMLInputElement, IFileInputProps>(
43
+ (
44
+ {
45
+ fileList,
46
+ label,
47
+ text,
48
+ description,
49
+ isDragAndDropDisabled = false,
50
+ isRequired = false,
51
+ isReadOnly = false,
52
+ isMultipleDisabled = false,
53
+ isDisabled = false,
54
+ isActive = false,
55
+ accept,
56
+ onAdd,
57
+ testId,
58
+ tweakStyles,
59
+ data,
60
+ },
61
+ ref,
62
+ ) => {
63
+ const classes = useStyles({ theme: tweakStyles });
64
+
65
+ const [isDragOver, setIsDragOver] = useState(false);
66
+
67
+ const handleAdd = (files: FileList | null) => {
68
+ if (isDisabled || isEmpty(files) || files?.length === 0) {
69
+ return;
70
+ }
71
+
72
+ onAdd?.([...files]);
73
+ };
74
+
75
+ const handleDragStart: DragEventHandler<HTMLDivElement> = (event) => {
76
+ blockEvent(event);
77
+ setIsDragOver(true);
78
+ };
79
+
80
+ const handleDragStop: DragEventHandler<HTMLDivElement> = (event) => {
81
+ blockEvent(event);
82
+ setIsDragOver(false);
83
+ };
84
+
85
+ const handleDrop: DragEventHandler<HTMLDivElement> = (event) => {
86
+ handleDragStop(event);
87
+
88
+ handleAdd(event.dataTransfer.files);
89
+ };
90
+
91
+ const handleFileChange: ChangeEventHandler<HTMLInputElement> = (event) => {
92
+ handleAdd(event.target.files);
93
+ };
94
+
95
+ const dndHandlers =
96
+ !isDragAndDropDisabled && !isDisabled
97
+ ? {
98
+ onDragOver: handleDragStart,
99
+ onDragEnter: handleDragStart,
100
+ onDragLeave: handleDragStop,
101
+ onDrop: handleDrop,
102
+ }
103
+ : undefined;
104
+
105
+ return (
106
+ <div className={classes.root} {...addDataAttributes(data)} {...addDataTestId(testId)}>
107
+ {isReactNodeNotEmpty(label) && (
108
+ <div className={clsx(classes.label, { [classes.requiredLabel]: isRequired })}>
109
+ {label}
110
+ </div>
111
+ )}
112
+ {!isReadOnly && (
113
+ <div
114
+ className={clsx(classes.inputWrapper, {
115
+ [classes.dragged]: isDragOver,
116
+ [classes.disabled]: isDisabled,
117
+ [classes.active]: isActive,
118
+ })}
119
+ {...dndHandlers}
120
+ >
121
+ {isReactNodeNotEmpty(text) && <div className={classes.text}>{text}</div>}
122
+ <label className={classes.inputLabel}>
123
+ <input
124
+ ref={ref}
125
+ type="file"
126
+ value=""
127
+ onChange={handleFileChange}
128
+ multiple={!isMultipleDisabled}
129
+ disabled={isDisabled}
130
+ accept={accept}
131
+ {...addDataTestId(testId, 'input')}
132
+ />
133
+ </label>
134
+ </div>
135
+ )}
136
+
137
+ {isReactNodeNotEmpty(description) && (
138
+ <div className={classes.description}>{description}</div>
139
+ )}
140
+
141
+ <div className={classes.fileList}>{fileList}</div>
142
+ </div>
143
+ );
144
+ },
145
+ );
146
+
147
+ FileInput.displayName = 'FileInput';
@@ -0,0 +1,6 @@
1
+ import { SyntheticEvent } from 'react';
2
+
3
+ export const blockEvent = (event: SyntheticEvent): void => {
4
+ event.preventDefault();
5
+ event.stopPropagation();
6
+ };
@@ -0,0 +1,2 @@
1
+ export * from './FileInput';
2
+ export type { IFileInputStyles } from './FileInput.styles';
@@ -0,0 +1,63 @@
1
+ import { format } from 'date-fns';
2
+ import { ru } from 'date-fns/locale';
3
+ import { ComponentStory } from '@storybook/react';
4
+ import { FILE_ITEM_SIZES } from '../FileItem/constants';
5
+ import { IIconType, iconsList } from '../Icon';
6
+ import { complexIcons } from '../Icon/complexIcons';
7
+ import { FileItem, IFileItemProps } from './FileItem';
8
+
9
+ export default {
10
+ title: 'Data Display/FileItem',
11
+ component: FileItem,
12
+ };
13
+
14
+ const iconTypes = [
15
+ undefined,
16
+ ...Object.keys(iconsList),
17
+ ...Object.keys(complexIcons),
18
+ ] as IIconType[];
19
+
20
+ const preloaderTypes: Array<IFileItemProps['preloaderType']> = ['dots', 'default', 'logo'];
21
+
22
+ const onRemove = async () => console.log('remove file');
23
+
24
+ const onClick = (): Promise<void> => new Promise(() => console.log('download file'));
25
+
26
+ export const Default: ComponentStory<typeof FileItem> = (args) => <FileItem {...args} />;
27
+
28
+ Default.args = {
29
+ // actions: <IconButton icon="download" />,
30
+ icon: undefined,
31
+ error: 'Страшная ошибка',
32
+ itemSize: 'm',
33
+ preloaderType: 'default',
34
+ removeIcon: 'trash-can',
35
+ isDisabled: false,
36
+ areActionsDisabled: false,
37
+ isLoading: false,
38
+ shouldShowSize: true,
39
+ onClick,
40
+ onRemove,
41
+ fileInfo: {
42
+ name: 'Document_Filename.docx',
43
+ type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
44
+ size: 1000000,
45
+ },
46
+ metadata: (
47
+ <span>Обновлен {format(new Date(1712298796990), 'dd MMM, HH:mm', { locale: ru })}</span>
48
+ ),
49
+ info: 'Нет, Вы это видели вообще?',
50
+ };
51
+
52
+ Default.argTypes = {
53
+ icon: { options: iconTypes, control: 'select' },
54
+ removeIcon: { options: iconTypes, control: 'select' },
55
+ itemSize: { options: FILE_ITEM_SIZES, control: 'inline-radio' },
56
+ preloaderType: { options: preloaderTypes, control: 'inline-radio' },
57
+ };
58
+
59
+ Default.parameters = {
60
+ controls: {
61
+ exclude: ['data', 'testId', 'tweakStyles', 'onRemove', 'onClick'],
62
+ },
63
+ };
@@ -0,0 +1,122 @@
1
+ import { ITweakStyles, animations, createThemedStyles } from '../../theme';
2
+ import { IIconButtonStyles } from '../IconButton';
3
+ import { IThemedPreloaderStyles } from '../ThemedPreloader';
4
+ import { ITooltipStyles } from '../Tooltip';
5
+
6
+ export const useStyles = createThemedStyles('FileItem', {
7
+ root: {
8
+ display: 'flex',
9
+ flexDirection: 'column',
10
+ gap: 4,
11
+ },
12
+
13
+ fileItemWrapper: {
14
+ display: 'flex',
15
+ gap: 12,
16
+ width: '100%',
17
+ cursor: 'pointer',
18
+ boxSizing: 'border-box',
19
+ transition: animations.defaultTransition,
20
+ transitionProperty: 'background-color, color, box-shadow, border-color',
21
+ },
22
+
23
+ m: {
24
+ '&$fileItemWrapper': {
25
+ padding: [10, 10, 10, 12],
26
+ },
27
+
28
+ '--icon-size': '20px',
29
+ },
30
+
31
+ l: {
32
+ '&$fileItemWrapper': {
33
+ padding: [12, 16],
34
+ },
35
+
36
+ '--icon-size': '32px',
37
+ },
38
+
39
+ fileIcon: {
40
+ maxWidth: 'var(--icon-size)',
41
+ maxHeight: 'var(--icon-size)',
42
+ },
43
+
44
+ fileName: {
45
+ whiteSpace: 'nowrap',
46
+ overflow: 'hidden',
47
+ textOverflow: 'ellipsis',
48
+ fontSize: 16,
49
+ },
50
+
51
+ fileNameContainer: {
52
+ overflow: 'hidden',
53
+ marginRight: 'auto',
54
+ },
55
+
56
+ preloader: {
57
+ display: 'flex',
58
+ margin: 6,
59
+ },
60
+
61
+ 'preloader-dots': {
62
+ width: 26,
63
+ marginTop: 12,
64
+ },
65
+
66
+ 'preloader-default': {
67
+ width: 20,
68
+ height: 20,
69
+ },
70
+
71
+ 'preloader-logo': {
72
+ width: 20,
73
+ height: 20,
74
+ },
75
+
76
+ metadata: {
77
+ display: 'flex',
78
+ flexDirection: 'column',
79
+ gap: 2,
80
+ fontSize: 12,
81
+ },
82
+
83
+ actions: {
84
+ display: 'flex',
85
+ flexShrink: 0,
86
+ margin: -6,
87
+ },
88
+
89
+ fileSize: {},
90
+
91
+ footer: {},
92
+
93
+ error: {
94
+ fontSize: 12,
95
+ },
96
+
97
+ info: {
98
+ fontSize: 12,
99
+ },
100
+
101
+ disabled: {
102
+ cursor: 'not-allowed',
103
+ },
104
+ });
105
+
106
+ export const themedPreloaderStyles: IThemedPreloaderStyles = {
107
+ tweakDotsPreloader: {
108
+ root: {
109
+ gap: 4,
110
+ '--dot-size': '6px',
111
+ },
112
+ },
113
+ };
114
+
115
+ export type IFileItemStyles = ITweakStyles<
116
+ typeof useStyles,
117
+ {
118
+ tweakRemoveIconButton: IIconButtonStyles;
119
+ tweakPreloader: IThemedPreloaderStyles;
120
+ tweakTooltip: ITooltipStyles;
121
+ }
122
+ >;
@@ -0,0 +1,157 @@
1
+ import { FC, MouseEvent, KeyboardEvent, ReactNode } from 'react';
2
+ import clsx from 'clsx';
3
+ import { filesize } from 'filesize';
4
+ import {
5
+ addClickHandler,
6
+ addDataTestId,
7
+ isFunction,
8
+ isNotEmpty,
9
+ isReactNodeNotEmpty,
10
+ } from '@true-engineering/true-react-platform-helpers';
11
+ import { addDataAttributes } from '../../helpers';
12
+ import { useTweakStyles } from '../../hooks';
13
+ import { ICommonProps } from '../../types';
14
+ import { IIcon, renderIcon } from '../Icon';
15
+ import { IconButton } from '../IconButton';
16
+ import { TextWithTooltip } from '../TextWithTooltip';
17
+ import { IThemedPreloaderProps, ThemedPreloader } from '../ThemedPreloader';
18
+ import { getFileIcon } from './helpers';
19
+ import { IFileInfo, IFileItemSize } from './types';
20
+ import { IFileItemStyles, themedPreloaderStyles, useStyles } from './FileItem.styles';
21
+
22
+ export interface IFileItemProps extends ICommonProps<IFileItemStyles> {
23
+ fileInfo: IFileInfo;
24
+ icon?: IIcon;
25
+ /** @default trash-can */
26
+ removeIcon?: IIcon;
27
+ metadata?: ReactNode;
28
+ actions?: ReactNode;
29
+ error?: ReactNode;
30
+ info?: ReactNode;
31
+ /** @default m */
32
+ itemSize?: IFileItemSize;
33
+ /** @default default */
34
+ preloaderType?: IThemedPreloaderProps['type'];
35
+ /** @default false */
36
+ isDisabled?: boolean;
37
+ /** @default false */
38
+ areActionsDisabled?: boolean;
39
+ /** @default false */
40
+ isLoading?: boolean;
41
+ /** @default false */
42
+ shouldShowSize?: boolean;
43
+ onClick?: (event: MouseEvent | KeyboardEvent) => void;
44
+ onRemove?: (event: MouseEvent | KeyboardEvent) => void;
45
+ }
46
+
47
+ export const FileItem: FC<IFileItemProps> = ({
48
+ fileInfo,
49
+ icon = getFileIcon(fileInfo),
50
+ metadata,
51
+ actions,
52
+ error,
53
+ info,
54
+ isDisabled = false,
55
+ areActionsDisabled = false,
56
+ isLoading = false,
57
+ shouldShowSize = false,
58
+ itemSize = 'm',
59
+ preloaderType = 'default',
60
+ removeIcon = 'trash-can',
61
+ onRemove,
62
+ onClick,
63
+ testId,
64
+ data,
65
+ tweakStyles,
66
+ }) => {
67
+ const classes = useStyles({ theme: tweakStyles });
68
+
69
+ const tweakIconButtonStyles = useTweakStyles({
70
+ tweakStyles,
71
+ className: 'tweakRemoveIconButton',
72
+ currentComponentName: 'FileItem',
73
+ });
74
+
75
+ const tweakPreloaderStyles = useTweakStyles({
76
+ innerStyles: themedPreloaderStyles,
77
+ tweakStyles,
78
+ className: 'tweakPreloader',
79
+ currentComponentName: 'FileItem',
80
+ });
81
+
82
+ const tweakTooltipStyles = useTweakStyles({
83
+ tweakStyles,
84
+ className: 'tweakTooltip',
85
+ currentComponentName: 'FileItem',
86
+ });
87
+
88
+ const handleClick = (event: MouseEvent | KeyboardEvent) => {
89
+ event.stopPropagation();
90
+ onClick?.(event);
91
+ };
92
+
93
+ const handleRemove = (event: MouseEvent | KeyboardEvent) => {
94
+ event.stopPropagation();
95
+ onRemove?.(event);
96
+ };
97
+
98
+ const { name, size } = fileInfo;
99
+ const hasRemoveButton = isFunction(onRemove);
100
+
101
+ return (
102
+ <div className={classes.root}>
103
+ <div
104
+ className={clsx(classes.fileItemWrapper, classes[itemSize], {
105
+ [classes.disabled]: isDisabled,
106
+ })}
107
+ {...addDataTestId(testId)}
108
+ {...addDataAttributes(data)}
109
+ {...addClickHandler(handleClick, !isDisabled)}
110
+ >
111
+ <div className={classes.fileIcon}>{renderIcon(icon)}</div>
112
+
113
+ <div className={classes.fileNameContainer}>
114
+ <TextWithTooltip tooltipText={name} tooltipView="hint" tweakStyles={tweakTooltipStyles}>
115
+ <div className={classes.fileName}>{name}</div>
116
+ </TextWithTooltip>
117
+
118
+ <div className={classes.metadata}>
119
+ {metadata}
120
+ {shouldShowSize && isNotEmpty(size) && (
121
+ <div className={classes.fileSize}>{filesize(size)}</div>
122
+ )}
123
+ </div>
124
+ </div>
125
+
126
+ <div className={classes.actions}>
127
+ {isLoading ? (
128
+ <div className={clsx(classes.preloader, classes[`preloader-${preloaderType}`])}>
129
+ <ThemedPreloader
130
+ type={preloaderType}
131
+ tweakStyles={tweakPreloaderStyles}
132
+ useCurrentColor
133
+ />
134
+ </div>
135
+ ) : (
136
+ hasRemoveButton && (
137
+ <IconButton
138
+ icon={removeIcon}
139
+ onClick={handleRemove}
140
+ isDisabled={areActionsDisabled}
141
+ tweakStyles={tweakIconButtonStyles}
142
+ />
143
+ )
144
+ )}
145
+
146
+ {actions}
147
+ </div>
148
+ </div>
149
+
150
+ <div className={classes.footer}>
151
+ {isReactNodeNotEmpty(error) && <div className={classes.error}>{error}</div>}
152
+
153
+ {isReactNodeNotEmpty(info) && <div className={classes.info}>{info}</div>}
154
+ </div>
155
+ </div>
156
+ );
157
+ };
@@ -0,0 +1,29 @@
1
+ import { mimeTypes } from '../../constants';
2
+ import { IComplexIcon } from '../Icon';
3
+ import { type IFileIcon } from './types';
4
+
5
+ export const FILE_ICONS = [
6
+ 'file-excel',
7
+ 'file-image',
8
+ 'file-pdf',
9
+ 'file-word',
10
+ 'file-xml',
11
+ 'file-zip',
12
+ 'file-other',
13
+ ] satisfies IComplexIcon[];
14
+
15
+ export const FILE_ICON_DEFAULT: IFileIcon = 'file-other';
16
+
17
+ export const iconTypeMap: Record<string, IFileIcon> = {
18
+ [mimeTypes.xls]: 'file-excel',
19
+ [mimeTypes.xlsx]: 'file-excel',
20
+ [mimeTypes.jpeg]: 'file-image',
21
+ [mimeTypes.png]: 'file-image',
22
+ [mimeTypes.pdf]: 'file-pdf',
23
+ [mimeTypes.doc]: 'file-word',
24
+ [mimeTypes.docx]: 'file-word',
25
+ [mimeTypes.xml]: 'file-xml',
26
+ [mimeTypes.zip]: 'file-zip',
27
+ };
28
+
29
+ export const FILE_ITEM_SIZES = ['m', 'l'] as const;