@snack-uikit/attachment 0.0.1-preview-d3c8ef06.0 → 0.0.1-preview-bfae2d03.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.
- package/README.md +5 -4
- package/dist/components/Attachment/Attachment.d.ts +1 -1
- package/dist/components/Attachment/Attachment.js +2 -2
- package/dist/components/Attachment/components/Emblem/Emblem.d.ts +2 -2
- package/dist/components/Attachment/components/Emblem/Emblem.js +6 -9
- package/dist/components/AttachmentSquare/AttachmentSquare.d.ts +1 -1
- package/dist/components/AttachmentSquare/AttachmentSquare.js +3 -3
- package/dist/components/AttachmentSquare/components/HoverContent/HoverContent.js +1 -1
- package/dist/components/AttachmentSquare/components/LoadingContent/LoadingContent.js +2 -1
- package/dist/components/AttachmentSquare/components/MainContent/MainContent.d.ts +2 -2
- package/dist/components/AttachmentSquare/components/MainContent/MainContent.js +5 -7
- package/dist/contexts.d.ts +1 -1
- package/dist/helperComponents/Actions/Actions.d.ts +2 -1
- package/dist/helperComponents/Actions/Actions.js +10 -4
- package/dist/helperComponents/Actions/styles.module.css +8 -0
- package/dist/helperComponents/Text/Text.d.ts +3 -1
- package/dist/helperComponents/Text/Text.js +15 -2
- package/dist/helperComponents/TextBlock/TextBlock.js +3 -2
- package/dist/testIds.d.ts +11 -0
- package/dist/testIds.js +12 -0
- package/dist/types.d.ts +25 -5
- package/package.json +3 -3
- package/src/components/Attachment/Attachment.tsx +10 -7
- package/src/components/Attachment/components/Emblem/Emblem.tsx +15 -10
- package/src/components/AttachmentSquare/AttachmentSquare.tsx +6 -11
- package/src/components/AttachmentSquare/components/HoverContent/HoverContent.tsx +1 -1
- package/src/components/AttachmentSquare/components/LoadingContent/LoadingContent.tsx +10 -2
- package/src/components/AttachmentSquare/components/MainContent/MainContent.tsx +12 -8
- package/src/contexts.ts +1 -1
- package/src/helperComponents/Actions/Actions.tsx +26 -3
- package/src/helperComponents/Actions/styles.module.scss +14 -0
- package/src/helperComponents/Text/Text.tsx +5 -2
- package/src/helperComponents/TextBlock/TextBlock.tsx +10 -4
- package/src/testIds.ts +13 -0
- package/src/types.ts +26 -5
package/README.md
CHANGED
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
| file | `File` | - | Файл |
|
|
16
16
|
| onDownload | `(file?: File) => void` | - | Колбек на клик по кнопке скачивания |
|
|
17
17
|
| onDelete | `(file?: File) => void` | - | Колбек на клик по кнопке удаления |
|
|
18
|
+
| onRetry | `() => void` | - | Колбек на клик по кнопке повторения |
|
|
18
19
|
| title | `string` | fileName | Заголовок |
|
|
19
20
|
| description | `string` | fileExtension | Описание |
|
|
20
21
|
| error | `string` | - | Сообщение об ошибке |
|
|
21
22
|
| truncateVariant | "end" \| "middle" | - | Вариант обрезания строки: <br> - `End` - с конца; <br> - `Middle` - по середине |
|
|
22
23
|
| loading | `boolean` | - | Управление состоянием загрузки |
|
|
23
|
-
|
|
|
24
|
+
| icon | `JSXElementConstructor<{ size?: number; className?: string; }>` | - | Иконка для файла @defaultIcon FileSVG |
|
|
24
25
|
| size | enum Size: `"s"`, `"m"` | s | Размер |
|
|
25
26
|
| checked | `boolean` | - | Управление состоянием выбран/не выбран |
|
|
26
27
|
| disabled | `boolean` | - | Управление состоянием интерактивности |
|
|
27
|
-
| multipleSelection | `boolean` | - | Отображение галочки для режима массового выделения карточек |
|
|
28
28
|
| onClick | `(e: MouseEvent<HTMLDivElement \| HTMLAnchorElement, MouseEvent>) => void` | - | Колбек на клик по карточке |
|
|
29
29
|
| className | `string` | - | CSS-класс для элемента с контентом |
|
|
30
30
|
## Attachment
|
|
@@ -34,18 +34,19 @@
|
|
|
34
34
|
| file | `File` | - | Файл |
|
|
35
35
|
| onDownload | `(file?: File) => void` | - | Колбек на клик по кнопке скачивания |
|
|
36
36
|
| onDelete | `(file?: File) => void` | - | Колбек на клик по кнопке удаления |
|
|
37
|
+
| onRetry | `() => void` | - | Колбек на клик по кнопке повторения |
|
|
37
38
|
| title | `string` | fileName | Заголовок |
|
|
38
39
|
| description | `string` | fileExtension | Описание |
|
|
39
40
|
| error | `string` | - | Сообщение об ошибке |
|
|
40
41
|
| truncateVariant | "end" \| "middle" | - | Вариант обрезания строки: <br> - `End` - с конца; <br> - `Middle` - по середине |
|
|
41
42
|
| loading | `boolean` | - | Управление состоянием загрузки |
|
|
42
|
-
|
|
|
43
|
+
| icon | `JSXElementConstructor<{ size?: number; className?: string; }>` | - | Иконка для файла @defaultIcon FileSVG |
|
|
43
44
|
| size | enum Size: `"s"`, `"m"` | s | Размер |
|
|
44
45
|
| checked | `boolean` | - | Управление состоянием выбран/не выбран |
|
|
45
46
|
| disabled | `boolean` | - | Управление состоянием интерактивности |
|
|
46
|
-
| multipleSelection | `boolean` | - | Отображение галочки для режима массового выделения карточек |
|
|
47
47
|
| onClick | `(e: MouseEvent<HTMLDivElement \| HTMLAnchorElement, MouseEvent>) => void` | - | Колбек на клик по карточке |
|
|
48
48
|
| className | `string` | - | CSS-класс для элемента с контентом |
|
|
49
|
+
| truncate | `{ title?: number; description?: number; error?: number; }` | - | |
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
[//]: DOCUMENTATION_SECTION_END
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AttachmentProps } from '../../types';
|
|
2
|
-
export declare function Attachment({ file, loading,
|
|
2
|
+
export declare function Attachment({ file, loading, icon, title: titleProp, description: descriptionProp, error, disabled, truncateVariant, checked, onDownload, onDelete, onClick, onRetry, size, truncate, ...rest }: AttachmentProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -19,10 +19,10 @@ import { useImage } from '../../hooks';
|
|
|
19
19
|
import { Emblem } from './components';
|
|
20
20
|
import styles from './styles.module.css';
|
|
21
21
|
export function Attachment(_a) {
|
|
22
|
-
var { file, loading,
|
|
22
|
+
var { file, loading, icon, title: titleProp, description: descriptionProp, error, disabled, truncateVariant, checked, onDownload, onDelete, onClick, onRetry, size = 's', truncate } = _a, rest = __rest(_a, ["file", "loading", "icon", "title", "description", "error", "disabled", "truncateVariant", "checked", "onDownload", "onDelete", "onClick", "onRetry", "size", "truncate"]);
|
|
23
23
|
const { loading: loadingImage, imageData } = useImage(file);
|
|
24
24
|
const isLoading = loading || loadingImage;
|
|
25
25
|
const title = titleProp || getBaseFileName(file === null || file === void 0 ? void 0 : file.name);
|
|
26
26
|
const description = descriptionProp || getFileExtension(file === null || file === void 0 ? void 0 : file.name);
|
|
27
|
-
return (_jsx(Card, Object.assign({}, extractSupportProps(rest), { outline: true, disabled: disabled, checked: checked, multipleSelection:
|
|
27
|
+
return (_jsx(Card, Object.assign({}, extractSupportProps(rest), { outline: true, disabled: disabled, checked: !loading && checked, multipleSelection: true, size: size, onClick: !isLoading && !disabled ? onClick : undefined, header: _jsx(AttachmentContext.Provider, { value: { file, truncateVariant, onDelete, onDownload, onRetry, disabled, size, truncate }, children: _jsxs("div", { className: styles.composition, "data-size": size, children: [_jsx(Emblem, { title: title, loading: isLoading, icon: icon, imageData: imageData }), _jsx(TextBlock, { title: title, description: description, error: error }), _jsx(Actions, { hideDownload: isLoading || Boolean(error), hideRetry: isLoading || !Boolean(error) })] }) }) })));
|
|
28
28
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AttachmentProps } from '../../../../types';
|
|
2
2
|
export type EmblemProps = {
|
|
3
3
|
imageData?: string;
|
|
4
|
-
} & Pick<AttachmentProps, '
|
|
5
|
-
export declare function Emblem({ loading,
|
|
4
|
+
} & Pick<AttachmentProps, 'icon' | 'loading' | 'title'>;
|
|
5
|
+
export declare function Emblem({ loading, icon, imageData, title }: EmblemProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
2
|
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
4
3
|
import { FileSVG } from '@snack-uikit/icons';
|
|
5
4
|
import { Spinner } from '@snack-uikit/loaders';
|
|
6
5
|
import { useAttachmentContext } from '../../../../contexts';
|
|
7
|
-
import {
|
|
6
|
+
import { TEST_IDS } from '../../../../testIds';
|
|
8
7
|
import styles from './styled.module.css';
|
|
9
|
-
export function Emblem({ loading,
|
|
10
|
-
|
|
11
|
-
const { file, size } = useAttachmentContext();
|
|
12
|
-
const extension = useMemo(() => { var _a; return (_a = getFileExtension(file === null || file === void 0 ? void 0 : file.name)) !== null && _a !== void 0 ? _a : ''; }, [file === null || file === void 0 ? void 0 : file.name]);
|
|
8
|
+
export function Emblem({ loading, icon, imageData, title }) {
|
|
9
|
+
const { size } = useAttachmentContext();
|
|
13
10
|
if (loading) {
|
|
14
|
-
return (_jsx(IconPredefined, { size: size, icon: () => _jsx(Spinner, { size: 's' }), decor: false, appearance: 'neutral', shape: 'square' }));
|
|
11
|
+
return (_jsx(IconPredefined, { size: size, icon: () => _jsx(Spinner, { size: 's' }), decor: false, appearance: 'neutral', shape: 'square', "data-test-id": TEST_IDS.loading }));
|
|
15
12
|
}
|
|
16
13
|
if (imageData) {
|
|
17
|
-
return (_jsx(IconPredefined, { size: size, icon: () => _jsx("img", { src: imageData, alt: title, "data-size": size || undefined, className: styles.img }), decor: false, shape: 'square' }));
|
|
14
|
+
return (_jsx(IconPredefined, { size: size, icon: () => _jsx("img", { src: imageData, alt: title, "data-size": size || undefined, className: styles.img }), decor: false, shape: 'square', "data-test-id": TEST_IDS.image }));
|
|
18
15
|
}
|
|
19
|
-
return (_jsx(IconPredefined, { size: size, icon:
|
|
16
|
+
return (_jsx(IconPredefined, { size: size, icon: icon !== null && icon !== void 0 ? icon : FileSVG, decor: true, appearance: 'neutral', shape: 'square', "data-test-id": TEST_IDS.icon }));
|
|
20
17
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { AttachmentSquareProps } from '../../types';
|
|
2
|
-
export declare function AttachmentSquare({ file, loading,
|
|
2
|
+
export declare function AttachmentSquare({ file, loading, icon, title: titleProp, description: descriptionProp, error, disabled, truncateVariant, checked, onDownload, onDelete, onClick, onRetry, size, ...rest }: AttachmentSquareProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -20,7 +20,7 @@ import { useImage } from '../../hooks';
|
|
|
20
20
|
import { HoverContent, LoadingContent, MainContent } from './components';
|
|
21
21
|
import styles from './styles.module.css';
|
|
22
22
|
export function AttachmentSquare(_a) {
|
|
23
|
-
var { file, loading,
|
|
23
|
+
var { file, loading, icon, title: titleProp, description: descriptionProp, error, disabled, truncateVariant, checked, onDownload, onDelete, onClick, onRetry, size = 's' } = _a, rest = __rest(_a, ["file", "loading", "icon", "title", "description", "error", "disabled", "truncateVariant", "checked", "onDownload", "onDelete", "onClick", "onRetry", "size"]);
|
|
24
24
|
const { loading: loadingImage, imageData } = useImage(file);
|
|
25
25
|
const isLoading = loading || loadingImage;
|
|
26
26
|
const title = titleProp || getBaseFileName(file === null || file === void 0 ? void 0 : file.name);
|
|
@@ -30,8 +30,8 @@ export function AttachmentSquare(_a) {
|
|
|
30
30
|
[styles.sizeS]: size === 's',
|
|
31
31
|
[styles.sizeM]: size === 'm',
|
|
32
32
|
[styles.hasFocusedActions]: hasFocusedActions,
|
|
33
|
-
}), checked: checked, multipleSelection:
|
|
33
|
+
}), checked: !loading && checked, multipleSelection: true, onClick: !isLoading && !disabled ? onClick : undefined, header: _jsx(AttachmentContext.Provider, { value: { file, truncateVariant, onDelete, onDownload, onRetry, disabled, size }, children: _jsxs(AttachmentFocusActionsContext.Provider, { value: {
|
|
34
34
|
focused: hasFocusedActions,
|
|
35
35
|
setFocused: setHasFocusedActions,
|
|
36
|
-
}, children: [!isLoading && !disabled && !error && (_jsx(MainContent, { title: title,
|
|
36
|
+
}, children: [!isLoading && !disabled && !error && (_jsx(MainContent, { title: title, icon: icon, imageData: imageData, className: styles.main })), !isLoading && (_jsx(HoverContent, { title: title, description: description, error: error, className: styles.hover })), isLoading && _jsx(LoadingContent, {})] }) }) })));
|
|
37
37
|
}
|
|
@@ -7,5 +7,5 @@ export function HoverContent({ title, description, error, className = '' }) {
|
|
|
7
7
|
const { disabled, size } = useAttachmentContext();
|
|
8
8
|
return (_jsxs("div", { className: cn(styles.composition, {
|
|
9
9
|
[className]: !disabled && !error,
|
|
10
|
-
}), "data-size": size, children: [_jsx(TextBlock, { title: title, error: error, description: !error ? description : undefined, align: 'center' }), _jsx(Actions, { hideDownload: Boolean(error) })] }));
|
|
10
|
+
}), "data-size": size, children: [_jsx(TextBlock, { title: title, error: error, description: !error ? description : undefined, align: 'center' }), _jsx(Actions, { hideDownload: Boolean(error), hideRetry: !Boolean(error) })] }));
|
|
11
11
|
}
|
|
@@ -3,8 +3,9 @@ import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
|
3
3
|
import { Spinner } from '@snack-uikit/loaders';
|
|
4
4
|
import { useAttachmentContext } from '../../../../contexts';
|
|
5
5
|
import { Actions } from '../../../../helperComponents';
|
|
6
|
+
import { TEST_IDS } from '../../../../testIds';
|
|
6
7
|
import styles from '../styles.module.css';
|
|
7
8
|
export function LoadingContent() {
|
|
8
9
|
const { size } = useAttachmentContext();
|
|
9
|
-
return (_jsxs("div", { className: styles.composition, "data-size": size, children: [_jsx(IconPredefined, { size:
|
|
10
|
+
return (_jsxs("div", { className: styles.composition, "data-size": size, children: [_jsx(IconPredefined, { size: size, icon: () => _jsx(Spinner, { size: 's' }), decor: false, appearance: 'neutral', shape: 'square', "data-test-id": TEST_IDS.loading }), _jsx(Actions, { hideDownload: true, hideRetry: true })] }));
|
|
10
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AttachmentProps } from '../../../../types';
|
|
2
2
|
type MainContentProps = {
|
|
3
3
|
imageData?: string;
|
|
4
|
-
} & Pick<AttachmentProps, 'title' | '
|
|
5
|
-
export declare function MainContent({ title,
|
|
4
|
+
} & Pick<AttachmentProps, 'title' | 'icon' | 'className'>;
|
|
5
|
+
export declare function MainContent({ title, icon, imageData, className }: MainContentProps): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
export {};
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
-
import { useMemo } from 'react';
|
|
4
3
|
import { Card } from '@snack-uikit/card';
|
|
5
4
|
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
6
5
|
import { FileSVG } from '@snack-uikit/icons';
|
|
7
6
|
import { useAttachmentContext } from '../../../../contexts';
|
|
8
7
|
import { TextBlock } from '../../../../helperComponents';
|
|
9
|
-
import {
|
|
8
|
+
import { TEST_IDS } from '../../../../testIds';
|
|
10
9
|
import styles from '../styles.module.css';
|
|
11
|
-
export function MainContent({ title,
|
|
12
|
-
var _a
|
|
10
|
+
export function MainContent({ title, icon, imageData, className }) {
|
|
11
|
+
var _a;
|
|
13
12
|
const { file, size } = useAttachmentContext();
|
|
14
|
-
const extension = useMemo(() => { var _a; return (_a = getFileExtension(file === null || file === void 0 ? void 0 : file.name)) !== null && _a !== void 0 ? _a : ''; }, [file === null || file === void 0 ? void 0 : file.name]);
|
|
15
13
|
if (imageData) {
|
|
16
|
-
return (_jsx("div", { className: cn(styles.composition, className), children: _jsx(Card.Image, { src: imageData, alt: (_a = file === null || file === void 0 ? void 0 : file.name) !== null && _a !== void 0 ? _a : 'file', mode: 'background', hideFading: true }) }));
|
|
14
|
+
return (_jsx("div", { className: cn(styles.composition, className), children: _jsx(Card.Image, { src: imageData, alt: (_a = file === null || file === void 0 ? void 0 : file.name) !== null && _a !== void 0 ? _a : 'file', mode: 'background', hideFading: true, "data-test-id": TEST_IDS.image }) }));
|
|
17
15
|
}
|
|
18
|
-
return (_jsxs("div", { className: cn(styles.composition, className), "data-size": size, children: [_jsx(TextBlock, { title: title, align: 'center' }), _jsx(IconPredefined, { size: size, icon:
|
|
16
|
+
return (_jsxs("div", { className: cn(styles.composition, className), "data-size": size, children: [_jsx(TextBlock, { title: title, align: 'center' }), _jsx(IconPredefined, { size: size, icon: icon !== null && icon !== void 0 ? icon : FileSVG, decor: true, appearance: 'neutral', shape: 'square', "data-test-id": TEST_IDS.icon })] }));
|
|
19
17
|
}
|
package/dist/contexts.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { AttachmentProps } from './types';
|
|
3
|
-
export type AttachmentContextType = Pick<AttachmentProps, 'size' | 'file' | 'truncateVariant' | 'onDownload' | 'onDelete' | 'disabled'>;
|
|
3
|
+
export type AttachmentContextType = Pick<AttachmentProps, 'size' | 'file' | 'truncateVariant' | 'onDownload' | 'onDelete' | 'onRetry' | 'disabled' | 'truncate'>;
|
|
4
4
|
export declare const AttachmentContext: import("react").Context<AttachmentContextType>;
|
|
5
5
|
export declare function useAttachmentContext(): AttachmentContextType;
|
|
6
6
|
export type AttachmentFocusActionsContextType = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type ActionsProps = {
|
|
2
2
|
hideDownload?: boolean;
|
|
3
3
|
hideDelete?: boolean;
|
|
4
|
+
hideRetry?: boolean;
|
|
4
5
|
};
|
|
5
|
-
export declare function Actions({ hideDelete, hideDownload }: ActionsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
6
|
+
export declare function Actions({ hideDelete, hideDownload, hideRetry }: ActionsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
6
7
|
export {};
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
3
|
-
import { DownloadSVG, TrashSVG } from '@snack-uikit/icons';
|
|
3
|
+
import { DownloadSVG, TrashSVG, UpdateSVG } from '@snack-uikit/icons';
|
|
4
4
|
import { useAttachmentContext, useAttachmentFocusActionsContext } from '../../contexts';
|
|
5
|
+
import { TEST_IDS } from '../../testIds';
|
|
5
6
|
import styles from './styles.module.css';
|
|
6
7
|
const noop = () => { };
|
|
7
|
-
export function Actions({ hideDelete, hideDownload }) {
|
|
8
|
-
const { onDelete, onDownload, file, disabled } = useAttachmentContext();
|
|
8
|
+
export function Actions({ hideDelete, hideDownload, hideRetry }) {
|
|
9
|
+
const { onDelete, onDownload, file, disabled, onRetry } = useAttachmentContext();
|
|
9
10
|
const { setFocused = noop } = useAttachmentFocusActionsContext();
|
|
10
11
|
const showDownload = onDownload && !hideDownload;
|
|
11
12
|
const showDelete = onDelete && !hideDelete;
|
|
13
|
+
const showRetry = onRetry && !hideRetry;
|
|
12
14
|
const handleFocus = () => {
|
|
13
15
|
setFocused(true);
|
|
14
16
|
};
|
|
15
17
|
const handleBlur = () => {
|
|
16
18
|
setFocused(false);
|
|
17
19
|
};
|
|
20
|
+
const handleRetry = (e) => {
|
|
21
|
+
onRetry === null || onRetry === void 0 ? void 0 : onRetry();
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
};
|
|
18
24
|
const handleDownload = (e) => {
|
|
19
25
|
onDownload === null || onDownload === void 0 ? void 0 : onDownload(file);
|
|
20
26
|
e.stopPropagation();
|
|
@@ -26,5 +32,5 @@ export function Actions({ hideDelete, hideDownload }) {
|
|
|
26
32
|
if (!showDownload && !showDelete) {
|
|
27
33
|
return null;
|
|
28
34
|
}
|
|
29
|
-
return (_jsxs("div", { className: styles.actions, children: [showDownload && (_jsx(ButtonFunction, { disabled: disabled, size: 'xs', icon: _jsx(DownloadSVG, {}), onClick: handleDownload, onFocus: handleFocus, onBlur: handleBlur })), showDelete && (_jsx(ButtonFunction, { disabled: disabled, size: 'xs', icon: _jsx(TrashSVG, {}), onClick: handleDelete, onFocus: handleFocus, onBlur: handleBlur }))] }));
|
|
35
|
+
return (_jsxs("div", { className: styles.actions, children: [showDownload && (_jsx(ButtonFunction, { disabled: disabled, size: 'xs', icon: _jsx(DownloadSVG, {}), onClick: handleDownload, onFocus: handleFocus, onBlur: handleBlur, "data-test-id": TEST_IDS.downloadAction })), showRetry && (_jsx(ButtonFunction, { disabled: disabled, size: 'xs', icon: _jsx(UpdateSVG, {}), onClick: handleRetry, onFocus: handleFocus, onBlur: handleBlur, className: styles.retryAction, "data-test-id": TEST_IDS.retryAction })), showDelete && (_jsx(ButtonFunction, { disabled: disabled, size: 'xs', icon: _jsx(TrashSVG, {}), onClick: handleDelete, onFocus: handleFocus, onBlur: handleBlur, "data-test-id": TEST_IDS.deleteAction }))] }));
|
|
30
36
|
}
|
|
@@ -2,4 +2,12 @@
|
|
|
2
2
|
gap:var(--space-attachment-composition-end-item-gap, 4px);
|
|
3
3
|
display:flex;
|
|
4
4
|
flex-shrink:0;
|
|
5
|
+
}
|
|
6
|
+
.actions :not([data-disabled]).retryAction svg{
|
|
7
|
+
transform:rotate(360deg);
|
|
8
|
+
transition:transform 0.5s;
|
|
9
|
+
}
|
|
10
|
+
.actions :not([data-disabled]).retryAction:active svg{
|
|
11
|
+
transform:rotate(0deg);
|
|
12
|
+
transition-duration:0s;
|
|
5
13
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type TextProps = {
|
|
2
2
|
text?: string;
|
|
3
3
|
className: string;
|
|
4
|
+
maxLines?: number;
|
|
5
|
+
'data-test-id'?: string;
|
|
4
6
|
};
|
|
5
|
-
export declare function Text({ text, className }: TextProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export declare function Text({ text, className, maxLines, ...rest }: TextProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
13
|
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
14
|
+
import { extractSupportProps } from '@snack-uikit/utils';
|
|
3
15
|
import { useAttachmentContext } from '../../contexts';
|
|
4
|
-
export function Text(
|
|
16
|
+
export function Text(_a) {
|
|
17
|
+
var { text, className, maxLines = 1 } = _a, rest = __rest(_a, ["text", "className", "maxLines"]);
|
|
5
18
|
const { truncateVariant } = useAttachmentContext();
|
|
6
19
|
if (!text) {
|
|
7
20
|
return null;
|
|
8
21
|
}
|
|
9
|
-
return (_jsx("div", { className: className, children: _jsx(TruncateString, { text: text, maxLines:
|
|
22
|
+
return (_jsx("div", { className: className, children: _jsx(TruncateString, Object.assign({ text: text, maxLines: maxLines, variant: truncateVariant }, extractSupportProps(rest))) }));
|
|
10
23
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
import { useAttachmentContext } from '../../contexts';
|
|
4
|
+
import { TEST_IDS } from '../../testIds';
|
|
4
5
|
import { Text } from '../Text';
|
|
5
6
|
import styles from './styles.module.css';
|
|
6
7
|
export function TextBlock({ title, description, error, className, align = 'left' }) {
|
|
7
|
-
const { size } = useAttachmentContext();
|
|
8
|
-
return (_jsxs("div", { className: cn(styles.content, className), "data-size": size, "data-align": align, children: [_jsx(Text, { className: styles.title, text: title }), _jsx(Text, { className: styles.description, text: description }), _jsx(Text, { className: styles.error, text: error })] }));
|
|
8
|
+
const { size, truncate = {} } = useAttachmentContext();
|
|
9
|
+
return (_jsxs("div", { className: cn(styles.content, className), "data-size": size, "data-align": align, children: [_jsx(Text, { className: styles.title, text: title, "data-test-id": TEST_IDS.title, maxLines: truncate === null || truncate === void 0 ? void 0 : truncate.title }), _jsx(Text, { className: styles.description, text: description, "data-test-id": TEST_IDS.description, maxLines: truncate === null || truncate === void 0 ? void 0 : truncate.description }), _jsx(Text, { className: styles.error, text: error, "data-test-id": TEST_IDS.error, maxLines: truncate === null || truncate === void 0 ? void 0 : truncate.error })] }));
|
|
9
10
|
}
|
package/dist/testIds.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const PREFIX = 'attachment';
|
|
2
|
+
export const TEST_IDS = {
|
|
3
|
+
icon: `${PREFIX}__icon`,
|
|
4
|
+
image: `${PREFIX}__image`,
|
|
5
|
+
loading: `${PREFIX}__loading`,
|
|
6
|
+
retryAction: `${PREFIX}__retry-action`,
|
|
7
|
+
deleteAction: `${PREFIX}__delete-action`,
|
|
8
|
+
downloadAction: `${PREFIX}__download-action`,
|
|
9
|
+
title: `${PREFIX}__title`,
|
|
10
|
+
description: `${PREFIX}__description`,
|
|
11
|
+
error: `${PREFIX}__error`,
|
|
12
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -3,13 +3,15 @@ import { IconPredefinedProps } from '@snack-uikit/icon-predefined';
|
|
|
3
3
|
import { TruncateStringProps } from '@snack-uikit/truncate-string';
|
|
4
4
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
5
5
|
export type Size = 's' | 'm';
|
|
6
|
-
export type
|
|
6
|
+
export type AttachmentSquareProps = WithSupportProps<{
|
|
7
7
|
/** Файл */
|
|
8
8
|
file?: File;
|
|
9
9
|
/** Колбек на клик по кнопке скачивания */
|
|
10
10
|
onDownload?(file?: File | undefined): void;
|
|
11
11
|
/** Колбек на клик по кнопке удаления */
|
|
12
12
|
onDelete?(file?: File | undefined): void;
|
|
13
|
+
/** Колбек на клик по кнопке повторения */
|
|
14
|
+
onRetry?(): void;
|
|
13
15
|
/**
|
|
14
16
|
* Заголовок
|
|
15
17
|
* @default fileName
|
|
@@ -31,11 +33,29 @@ export type AttachmentProps = WithSupportProps<{
|
|
|
31
33
|
/** Управление состоянием загрузки */
|
|
32
34
|
loading?: boolean;
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
36
|
+
* Иконка для файла
|
|
35
37
|
* @defaultIcon FileSVG
|
|
36
38
|
*/
|
|
37
|
-
|
|
39
|
+
icon?: IconPredefinedProps['icon'];
|
|
38
40
|
/** Размер */
|
|
39
41
|
size?: Size;
|
|
40
|
-
}> & Pick<CardProps, 'checked' | 'disabled' | '
|
|
41
|
-
export type
|
|
42
|
+
}> & Pick<CardProps, 'checked' | 'disabled' | 'onClick' | 'className'>;
|
|
43
|
+
export type AttachmentProps = AttachmentSquareProps & {
|
|
44
|
+
truncate?: {
|
|
45
|
+
/**
|
|
46
|
+
* Максимальное кол-во строк заголовка
|
|
47
|
+
* @default 1
|
|
48
|
+
*/
|
|
49
|
+
title?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Максимальное кол-во строк описания
|
|
52
|
+
* @default 1
|
|
53
|
+
*/
|
|
54
|
+
description?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Максимальное кол-во строк ошибки
|
|
57
|
+
* @default 1
|
|
58
|
+
*/
|
|
59
|
+
error?: number;
|
|
60
|
+
};
|
|
61
|
+
};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Attachment",
|
|
7
|
-
"version": "0.0.1-preview-
|
|
7
|
+
"version": "0.0.1-preview-bfae2d03.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"scripts": {},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@snack-uikit/button": "0.17.1",
|
|
36
|
-
"@snack-uikit/card": "0.12.14-preview-
|
|
36
|
+
"@snack-uikit/card": "0.12.14-preview-bfae2d03.0",
|
|
37
37
|
"@snack-uikit/icon-predefined": "0.5.1",
|
|
38
38
|
"@snack-uikit/icons": "0.21.0",
|
|
39
39
|
"@snack-uikit/loaders": "0.5.2",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"@snack-uikit/utils": "3.3.0",
|
|
42
42
|
"classnames": "2.3.2"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "7b2a733a770a8bdd14adb9951c8fc552ad192578"
|
|
45
45
|
}
|
|
@@ -12,18 +12,19 @@ import styles from './styles.module.scss';
|
|
|
12
12
|
export function Attachment({
|
|
13
13
|
file,
|
|
14
14
|
loading,
|
|
15
|
-
|
|
15
|
+
icon,
|
|
16
16
|
title: titleProp,
|
|
17
17
|
description: descriptionProp,
|
|
18
18
|
error,
|
|
19
19
|
disabled,
|
|
20
20
|
truncateVariant,
|
|
21
21
|
checked,
|
|
22
|
-
multipleSelection,
|
|
23
22
|
onDownload,
|
|
24
23
|
onDelete,
|
|
25
24
|
onClick,
|
|
25
|
+
onRetry,
|
|
26
26
|
size = 's',
|
|
27
|
+
truncate,
|
|
27
28
|
...rest
|
|
28
29
|
}: AttachmentProps) {
|
|
29
30
|
const { loading: loadingImage, imageData } = useImage(file);
|
|
@@ -37,16 +38,18 @@ export function Attachment({
|
|
|
37
38
|
{...extractSupportProps(rest)}
|
|
38
39
|
outline
|
|
39
40
|
disabled={disabled}
|
|
40
|
-
checked={checked}
|
|
41
|
-
multipleSelection
|
|
41
|
+
checked={!loading && checked}
|
|
42
|
+
multipleSelection
|
|
42
43
|
size={size}
|
|
43
44
|
onClick={!isLoading && !disabled ? onClick : undefined}
|
|
44
45
|
header={
|
|
45
|
-
<AttachmentContext.Provider
|
|
46
|
+
<AttachmentContext.Provider
|
|
47
|
+
value={{ file, truncateVariant, onDelete, onDownload, onRetry, disabled, size, truncate }}
|
|
48
|
+
>
|
|
46
49
|
<div className={styles.composition} data-size={size}>
|
|
47
|
-
<Emblem title={title} loading={isLoading}
|
|
50
|
+
<Emblem title={title} loading={isLoading} icon={icon} imageData={imageData} />
|
|
48
51
|
<TextBlock title={title} description={description} error={error} />
|
|
49
|
-
<Actions hideDownload={isLoading || Boolean(error)} />
|
|
52
|
+
<Actions hideDownload={isLoading || Boolean(error)} hideRetry={isLoading || !Boolean(error)} />
|
|
50
53
|
</div>
|
|
51
54
|
</AttachmentContext.Provider>
|
|
52
55
|
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
|
-
|
|
3
1
|
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
4
2
|
import { FileSVG } from '@snack-uikit/icons';
|
|
5
3
|
import { Spinner } from '@snack-uikit/loaders';
|
|
6
4
|
|
|
7
5
|
import { useAttachmentContext } from '../../../../contexts';
|
|
8
|
-
import {
|
|
6
|
+
import { TEST_IDS } from '../../../../testIds';
|
|
9
7
|
import { AttachmentProps } from '../../../../types';
|
|
10
8
|
import styles from './styled.module.scss';
|
|
11
9
|
|
|
12
10
|
export type EmblemProps = {
|
|
13
11
|
imageData?: string;
|
|
14
|
-
} & Pick<AttachmentProps, '
|
|
15
|
-
|
|
16
|
-
export function Emblem({ loading, extensionToIcon, imageData, title }: EmblemProps) {
|
|
17
|
-
const { file, size } = useAttachmentContext();
|
|
12
|
+
} & Pick<AttachmentProps, 'icon' | 'loading' | 'title'>;
|
|
18
13
|
|
|
19
|
-
|
|
14
|
+
export function Emblem({ loading, icon, imageData, title }: EmblemProps) {
|
|
15
|
+
const { size } = useAttachmentContext();
|
|
20
16
|
|
|
21
17
|
if (loading) {
|
|
22
18
|
return (
|
|
23
|
-
<IconPredefined
|
|
19
|
+
<IconPredefined
|
|
20
|
+
size={size}
|
|
21
|
+
icon={() => <Spinner size='s' />}
|
|
22
|
+
decor={false}
|
|
23
|
+
appearance='neutral'
|
|
24
|
+
shape='square'
|
|
25
|
+
data-test-id={TEST_IDS.loading}
|
|
26
|
+
/>
|
|
24
27
|
);
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -31,6 +34,7 @@ export function Emblem({ loading, extensionToIcon, imageData, title }: EmblemPro
|
|
|
31
34
|
icon={() => <img src={imageData} alt={title} data-size={size || undefined} className={styles.img} />}
|
|
32
35
|
decor={false}
|
|
33
36
|
shape='square'
|
|
37
|
+
data-test-id={TEST_IDS.image}
|
|
34
38
|
/>
|
|
35
39
|
);
|
|
36
40
|
}
|
|
@@ -38,10 +42,11 @@ export function Emblem({ loading, extensionToIcon, imageData, title }: EmblemPro
|
|
|
38
42
|
return (
|
|
39
43
|
<IconPredefined
|
|
40
44
|
size={size}
|
|
41
|
-
icon={
|
|
45
|
+
icon={icon ?? FileSVG}
|
|
42
46
|
decor
|
|
43
47
|
appearance='neutral'
|
|
44
48
|
shape='square'
|
|
49
|
+
data-test-id={TEST_IDS.icon}
|
|
45
50
|
/>
|
|
46
51
|
);
|
|
47
52
|
}
|
|
@@ -14,17 +14,17 @@ import styles from './styles.module.scss';
|
|
|
14
14
|
export function AttachmentSquare({
|
|
15
15
|
file,
|
|
16
16
|
loading,
|
|
17
|
-
|
|
17
|
+
icon,
|
|
18
18
|
title: titleProp,
|
|
19
19
|
description: descriptionProp,
|
|
20
20
|
error,
|
|
21
21
|
disabled,
|
|
22
22
|
truncateVariant,
|
|
23
23
|
checked,
|
|
24
|
-
multipleSelection,
|
|
25
24
|
onDownload,
|
|
26
25
|
onDelete,
|
|
27
26
|
onClick,
|
|
27
|
+
onRetry,
|
|
28
28
|
size = 's',
|
|
29
29
|
...rest
|
|
30
30
|
}: AttachmentSquareProps) {
|
|
@@ -47,11 +47,11 @@ export function AttachmentSquare({
|
|
|
47
47
|
[styles.sizeM]: size === 'm',
|
|
48
48
|
[styles.hasFocusedActions]: hasFocusedActions,
|
|
49
49
|
})}
|
|
50
|
-
checked={checked}
|
|
51
|
-
multipleSelection
|
|
50
|
+
checked={!loading && checked}
|
|
51
|
+
multipleSelection
|
|
52
52
|
onClick={!isLoading && !disabled ? onClick : undefined}
|
|
53
53
|
header={
|
|
54
|
-
<AttachmentContext.Provider value={{ file, truncateVariant, onDelete, onDownload, disabled, size }}>
|
|
54
|
+
<AttachmentContext.Provider value={{ file, truncateVariant, onDelete, onDownload, onRetry, disabled, size }}>
|
|
55
55
|
<AttachmentFocusActionsContext.Provider
|
|
56
56
|
value={{
|
|
57
57
|
focused: hasFocusedActions,
|
|
@@ -59,12 +59,7 @@ export function AttachmentSquare({
|
|
|
59
59
|
}}
|
|
60
60
|
>
|
|
61
61
|
{!isLoading && !disabled && !error && (
|
|
62
|
-
<MainContent
|
|
63
|
-
title={title}
|
|
64
|
-
extensionToIcon={extensionToIcon}
|
|
65
|
-
imageData={imageData}
|
|
66
|
-
className={styles.main}
|
|
67
|
-
/>
|
|
62
|
+
<MainContent title={title} icon={icon} imageData={imageData} className={styles.main} />
|
|
68
63
|
)}
|
|
69
64
|
|
|
70
65
|
{!isLoading && (
|
|
@@ -19,7 +19,7 @@ export function HoverContent({ title, description, error, className = '' }: Hove
|
|
|
19
19
|
>
|
|
20
20
|
<TextBlock title={title} error={error} description={!error ? description : undefined} align='center' />
|
|
21
21
|
|
|
22
|
-
<Actions hideDownload={Boolean(error)} />
|
|
22
|
+
<Actions hideDownload={Boolean(error)} hideRetry={!Boolean(error)} />
|
|
23
23
|
</div>
|
|
24
24
|
);
|
|
25
25
|
}
|
|
@@ -3,6 +3,7 @@ import { Spinner } from '@snack-uikit/loaders';
|
|
|
3
3
|
|
|
4
4
|
import { useAttachmentContext } from '../../../../contexts';
|
|
5
5
|
import { Actions } from '../../../../helperComponents';
|
|
6
|
+
import { TEST_IDS } from '../../../../testIds';
|
|
6
7
|
import styles from '../styles.module.scss';
|
|
7
8
|
|
|
8
9
|
export function LoadingContent() {
|
|
@@ -10,8 +11,15 @@ export function LoadingContent() {
|
|
|
10
11
|
|
|
11
12
|
return (
|
|
12
13
|
<div className={styles.composition} data-size={size}>
|
|
13
|
-
<IconPredefined
|
|
14
|
-
|
|
14
|
+
<IconPredefined
|
|
15
|
+
size={size}
|
|
16
|
+
icon={() => <Spinner size='s' />}
|
|
17
|
+
decor={false}
|
|
18
|
+
appearance='neutral'
|
|
19
|
+
shape='square'
|
|
20
|
+
data-test-id={TEST_IDS.loading}
|
|
21
|
+
/>
|
|
22
|
+
<Actions hideDownload hideRetry />
|
|
15
23
|
</div>
|
|
16
24
|
);
|
|
17
25
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
2
|
|
|
4
3
|
import { Card } from '@snack-uikit/card';
|
|
5
4
|
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
@@ -7,23 +6,27 @@ import { FileSVG } from '@snack-uikit/icons';
|
|
|
7
6
|
|
|
8
7
|
import { useAttachmentContext } from '../../../../contexts';
|
|
9
8
|
import { TextBlock } from '../../../../helperComponents';
|
|
10
|
-
import {
|
|
9
|
+
import { TEST_IDS } from '../../../../testIds';
|
|
11
10
|
import { AttachmentProps } from '../../../../types';
|
|
12
11
|
import styles from '../styles.module.scss';
|
|
13
12
|
|
|
14
13
|
type MainContentProps = {
|
|
15
14
|
imageData?: string;
|
|
16
|
-
} & Pick<AttachmentProps, 'title' | '
|
|
15
|
+
} & Pick<AttachmentProps, 'title' | 'icon' | 'className'>;
|
|
17
16
|
|
|
18
|
-
export function MainContent({ title,
|
|
17
|
+
export function MainContent({ title, icon, imageData, className }: MainContentProps) {
|
|
19
18
|
const { file, size } = useAttachmentContext();
|
|
20
19
|
|
|
21
|
-
const extension = useMemo(() => getFileExtension(file?.name) ?? '', [file?.name]);
|
|
22
|
-
|
|
23
20
|
if (imageData) {
|
|
24
21
|
return (
|
|
25
22
|
<div className={cn(styles.composition, className)}>
|
|
26
|
-
<Card.Image
|
|
23
|
+
<Card.Image
|
|
24
|
+
src={imageData}
|
|
25
|
+
alt={file?.name ?? 'file'}
|
|
26
|
+
mode='background'
|
|
27
|
+
hideFading
|
|
28
|
+
data-test-id={TEST_IDS.image}
|
|
29
|
+
/>
|
|
27
30
|
</div>
|
|
28
31
|
);
|
|
29
32
|
}
|
|
@@ -34,10 +37,11 @@ export function MainContent({ title, extensionToIcon, imageData, className }: Ma
|
|
|
34
37
|
|
|
35
38
|
<IconPredefined
|
|
36
39
|
size={size}
|
|
37
|
-
icon={
|
|
40
|
+
icon={icon ?? FileSVG}
|
|
38
41
|
decor
|
|
39
42
|
appearance='neutral'
|
|
40
43
|
shape='square'
|
|
44
|
+
data-test-id={TEST_IDS.icon}
|
|
41
45
|
/>
|
|
42
46
|
</div>
|
|
43
47
|
);
|
package/src/contexts.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { AttachmentProps } from './types';
|
|
|
4
4
|
|
|
5
5
|
export type AttachmentContextType = Pick<
|
|
6
6
|
AttachmentProps,
|
|
7
|
-
'size' | 'file' | 'truncateVariant' | 'onDownload' | 'onDelete' | 'disabled'
|
|
7
|
+
'size' | 'file' | 'truncateVariant' | 'onDownload' | 'onDelete' | 'onRetry' | 'disabled' | 'truncate'
|
|
8
8
|
>;
|
|
9
9
|
|
|
10
10
|
export const AttachmentContext = createContext<AttachmentContextType>({});
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import { MouseEvent } from 'react';
|
|
2
2
|
|
|
3
3
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
4
|
-
import { DownloadSVG, TrashSVG } from '@snack-uikit/icons';
|
|
4
|
+
import { DownloadSVG, TrashSVG, UpdateSVG } from '@snack-uikit/icons';
|
|
5
5
|
|
|
6
6
|
import { useAttachmentContext, useAttachmentFocusActionsContext } from '../../contexts';
|
|
7
|
+
import { TEST_IDS } from '../../testIds';
|
|
7
8
|
import styles from './styles.module.scss';
|
|
8
9
|
|
|
9
10
|
type ActionsProps = {
|
|
10
11
|
hideDownload?: boolean;
|
|
11
12
|
hideDelete?: boolean;
|
|
13
|
+
hideRetry?: boolean;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
const noop = () => {};
|
|
15
17
|
|
|
16
|
-
export function Actions({ hideDelete, hideDownload }: ActionsProps) {
|
|
17
|
-
const { onDelete, onDownload, file, disabled } = useAttachmentContext();
|
|
18
|
+
export function Actions({ hideDelete, hideDownload, hideRetry }: ActionsProps) {
|
|
19
|
+
const { onDelete, onDownload, file, disabled, onRetry } = useAttachmentContext();
|
|
18
20
|
const { setFocused = noop } = useAttachmentFocusActionsContext();
|
|
19
21
|
|
|
20
22
|
const showDownload = onDownload && !hideDownload;
|
|
21
23
|
const showDelete = onDelete && !hideDelete;
|
|
24
|
+
const showRetry = onRetry && !hideRetry;
|
|
22
25
|
|
|
23
26
|
const handleFocus = () => {
|
|
24
27
|
setFocused(true);
|
|
@@ -26,6 +29,12 @@ export function Actions({ hideDelete, hideDownload }: ActionsProps) {
|
|
|
26
29
|
const handleBlur = () => {
|
|
27
30
|
setFocused(false);
|
|
28
31
|
};
|
|
32
|
+
|
|
33
|
+
const handleRetry = (e: MouseEvent<HTMLButtonElement>) => {
|
|
34
|
+
onRetry?.();
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
};
|
|
37
|
+
|
|
29
38
|
const handleDownload = (e: MouseEvent<HTMLButtonElement>) => {
|
|
30
39
|
onDownload?.(file);
|
|
31
40
|
e.stopPropagation();
|
|
@@ -50,6 +59,19 @@ export function Actions({ hideDelete, hideDownload }: ActionsProps) {
|
|
|
50
59
|
onClick={handleDownload}
|
|
51
60
|
onFocus={handleFocus}
|
|
52
61
|
onBlur={handleBlur}
|
|
62
|
+
data-test-id={TEST_IDS.downloadAction}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
{showRetry && (
|
|
66
|
+
<ButtonFunction
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
size='xs'
|
|
69
|
+
icon={<UpdateSVG />}
|
|
70
|
+
onClick={handleRetry}
|
|
71
|
+
onFocus={handleFocus}
|
|
72
|
+
onBlur={handleBlur}
|
|
73
|
+
className={styles.retryAction}
|
|
74
|
+
data-test-id={TEST_IDS.retryAction}
|
|
53
75
|
/>
|
|
54
76
|
)}
|
|
55
77
|
{showDelete && (
|
|
@@ -60,6 +82,7 @@ export function Actions({ hideDelete, hideDownload }: ActionsProps) {
|
|
|
60
82
|
onClick={handleDelete}
|
|
61
83
|
onFocus={handleFocus}
|
|
62
84
|
onBlur={handleBlur}
|
|
85
|
+
data-test-id={TEST_IDS.deleteAction}
|
|
63
86
|
/>
|
|
64
87
|
)}
|
|
65
88
|
</div>
|
|
@@ -5,4 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
display: flex;
|
|
7
7
|
flex-shrink: 0;
|
|
8
|
+
|
|
9
|
+
:not([data-disabled]).retryAction {
|
|
10
|
+
svg {
|
|
11
|
+
transform: rotate(360deg);
|
|
12
|
+
transition: transform 0.5s;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&:active {
|
|
16
|
+
svg {
|
|
17
|
+
transform: rotate(0deg);
|
|
18
|
+
transition-duration: 0s;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
8
22
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
2
|
+
import { extractSupportProps } from '@snack-uikit/utils';
|
|
2
3
|
|
|
3
4
|
import { useAttachmentContext } from '../../contexts';
|
|
4
5
|
|
|
5
6
|
export type TextProps = {
|
|
6
7
|
text?: string;
|
|
7
8
|
className: string;
|
|
9
|
+
maxLines?: number;
|
|
10
|
+
'data-test-id'?: string;
|
|
8
11
|
};
|
|
9
12
|
|
|
10
|
-
export function Text({ text, className }: TextProps) {
|
|
13
|
+
export function Text({ text, className, maxLines = 1, ...rest }: TextProps) {
|
|
11
14
|
const { truncateVariant } = useAttachmentContext();
|
|
12
15
|
|
|
13
16
|
if (!text) {
|
|
@@ -16,7 +19,7 @@ export function Text({ text, className }: TextProps) {
|
|
|
16
19
|
|
|
17
20
|
return (
|
|
18
21
|
<div className={className}>
|
|
19
|
-
<TruncateString text={text} maxLines={
|
|
22
|
+
<TruncateString text={text} maxLines={maxLines} variant={truncateVariant} {...extractSupportProps(rest)} />
|
|
20
23
|
</div>
|
|
21
24
|
);
|
|
22
25
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
2
|
|
|
3
3
|
import { useAttachmentContext } from '../../contexts';
|
|
4
|
+
import { TEST_IDS } from '../../testIds';
|
|
4
5
|
import { AttachmentProps } from '../../types';
|
|
5
6
|
import { Text } from '../Text';
|
|
6
7
|
import styles from './styles.module.scss';
|
|
@@ -10,13 +11,18 @@ export type TextBlockProps = Pick<AttachmentProps, 'title' | 'description' | 'er
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export function TextBlock({ title, description, error, className, align = 'left' }: TextBlockProps) {
|
|
13
|
-
const { size } = useAttachmentContext();
|
|
14
|
+
const { size, truncate = {} } = useAttachmentContext();
|
|
14
15
|
|
|
15
16
|
return (
|
|
16
17
|
<div className={cn(styles.content, className)} data-size={size} data-align={align}>
|
|
17
|
-
<Text className={styles.title} text={title} />
|
|
18
|
-
<Text
|
|
19
|
-
|
|
18
|
+
<Text className={styles.title} text={title} data-test-id={TEST_IDS.title} maxLines={truncate?.title} />
|
|
19
|
+
<Text
|
|
20
|
+
className={styles.description}
|
|
21
|
+
text={description}
|
|
22
|
+
data-test-id={TEST_IDS.description}
|
|
23
|
+
maxLines={truncate?.description}
|
|
24
|
+
/>
|
|
25
|
+
<Text className={styles.error} text={error} data-test-id={TEST_IDS.error} maxLines={truncate?.error} />
|
|
20
26
|
</div>
|
|
21
27
|
);
|
|
22
28
|
}
|
package/src/testIds.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const PREFIX = 'attachment';
|
|
2
|
+
|
|
3
|
+
export const TEST_IDS = {
|
|
4
|
+
icon: `${PREFIX}__icon`,
|
|
5
|
+
image: `${PREFIX}__image`,
|
|
6
|
+
loading: `${PREFIX}__loading`,
|
|
7
|
+
retryAction: `${PREFIX}__retry-action`,
|
|
8
|
+
deleteAction: `${PREFIX}__delete-action`,
|
|
9
|
+
downloadAction: `${PREFIX}__download-action`,
|
|
10
|
+
title: `${PREFIX}__title`,
|
|
11
|
+
description: `${PREFIX}__description`,
|
|
12
|
+
error: `${PREFIX}__error`,
|
|
13
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -5,13 +5,15 @@ import { WithSupportProps } from '@snack-uikit/utils';
|
|
|
5
5
|
|
|
6
6
|
export type Size = 's' | 'm';
|
|
7
7
|
|
|
8
|
-
export type
|
|
8
|
+
export type AttachmentSquareProps = WithSupportProps<{
|
|
9
9
|
/** Файл */
|
|
10
10
|
file?: File;
|
|
11
11
|
/** Колбек на клик по кнопке скачивания */
|
|
12
12
|
onDownload?(file?: File | undefined): void;
|
|
13
13
|
/** Колбек на клик по кнопке удаления */
|
|
14
14
|
onDelete?(file?: File | undefined): void;
|
|
15
|
+
/** Колбек на клик по кнопке повторения */
|
|
16
|
+
onRetry?(): void;
|
|
15
17
|
/**
|
|
16
18
|
* Заголовок
|
|
17
19
|
* @default fileName
|
|
@@ -30,16 +32,35 @@ export type AttachmentProps = WithSupportProps<{
|
|
|
30
32
|
* <br> - `Middle` - по середине
|
|
31
33
|
* */
|
|
32
34
|
truncateVariant?: TruncateStringProps['variant'];
|
|
35
|
+
|
|
33
36
|
/** Управление состоянием загрузки */
|
|
34
37
|
loading?: boolean;
|
|
35
38
|
/**
|
|
36
|
-
*
|
|
39
|
+
* Иконка для файла
|
|
37
40
|
* @defaultIcon FileSVG
|
|
38
41
|
*/
|
|
39
|
-
|
|
42
|
+
icon?: IconPredefinedProps['icon'];
|
|
40
43
|
/** Размер */
|
|
41
44
|
size?: Size;
|
|
42
45
|
}> &
|
|
43
|
-
Pick<CardProps, 'checked' | 'disabled' | '
|
|
46
|
+
Pick<CardProps, 'checked' | 'disabled' | 'onClick' | 'className'>;
|
|
44
47
|
|
|
45
|
-
export type
|
|
48
|
+
export type AttachmentProps = AttachmentSquareProps & {
|
|
49
|
+
truncate?: {
|
|
50
|
+
/**
|
|
51
|
+
* Максимальное кол-во строк заголовка
|
|
52
|
+
* @default 1
|
|
53
|
+
*/
|
|
54
|
+
title?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Максимальное кол-во строк описания
|
|
57
|
+
* @default 1
|
|
58
|
+
*/
|
|
59
|
+
description?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Максимальное кол-во строк ошибки
|
|
62
|
+
* @default 1
|
|
63
|
+
*/
|
|
64
|
+
error?: number;
|
|
65
|
+
};
|
|
66
|
+
};
|