@snack-uikit/attachment 0.0.1-preview-8b6f4393.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/CHANGELOG.md +5 -0
- package/LICENSE +201 -0
- package/README.md +51 -0
- package/dist/components/Attachment/Attachment.d.ts +2 -0
- package/dist/components/Attachment/Attachment.js +28 -0
- package/dist/components/Attachment/components/Emblem/Emblem.d.ts +5 -0
- package/dist/components/Attachment/components/Emblem/Emblem.js +20 -0
- package/dist/components/Attachment/components/Emblem/index.d.ts +1 -0
- package/dist/components/Attachment/components/Emblem/index.js +1 -0
- package/dist/components/Attachment/components/Emblem/styled.module.css +15 -0
- package/dist/components/Attachment/components/index.d.ts +1 -0
- package/dist/components/Attachment/components/index.js +1 -0
- package/dist/components/Attachment/index.d.ts +1 -0
- package/dist/components/Attachment/index.js +1 -0
- package/dist/components/Attachment/styles.module.css +15 -0
- package/dist/components/AttachmentSquare/AttachmentSquare.d.ts +2 -0
- package/dist/components/AttachmentSquare/AttachmentSquare.js +27 -0
- package/dist/components/AttachmentSquare/components/HoverContent/HoverContent.d.ts +4 -0
- package/dist/components/AttachmentSquare/components/HoverContent/HoverContent.js +11 -0
- package/dist/components/AttachmentSquare/components/HoverContent/index.d.ts +1 -0
- package/dist/components/AttachmentSquare/components/HoverContent/index.js +1 -0
- package/dist/components/AttachmentSquare/components/LoadingContent/LoadingContent.d.ts +1 -0
- package/dist/components/AttachmentSquare/components/LoadingContent/LoadingContent.js +8 -0
- package/dist/components/AttachmentSquare/components/LoadingContent/index.d.ts +1 -0
- package/dist/components/AttachmentSquare/components/LoadingContent/index.js +1 -0
- package/dist/components/AttachmentSquare/components/MainContent/MainContent.d.ts +6 -0
- package/dist/components/AttachmentSquare/components/MainContent/MainContent.js +19 -0
- package/dist/components/AttachmentSquare/components/MainContent/index.d.ts +1 -0
- package/dist/components/AttachmentSquare/components/MainContent/index.js +1 -0
- package/dist/components/AttachmentSquare/components/index.d.ts +3 -0
- package/dist/components/AttachmentSquare/components/index.js +3 -0
- package/dist/components/AttachmentSquare/components/styles.module.css +12 -0
- package/dist/components/AttachmentSquare/index.d.ts +1 -0
- package/dist/components/AttachmentSquare/index.js +1 -0
- package/dist/components/AttachmentSquare/styles.module.css +16 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/dist/contexts.d.ts +5 -0
- package/dist/contexts.js +5 -0
- package/dist/helperComponents/Actions/Actions.d.ts +6 -0
- package/dist/helperComponents/Actions/Actions.js +20 -0
- package/dist/helperComponents/Actions/index.d.ts +1 -0
- package/dist/helperComponents/Actions/index.js +1 -0
- package/dist/helperComponents/Actions/styles.module.css +5 -0
- package/dist/helperComponents/Text/Text.d.ts +5 -0
- package/dist/helperComponents/Text/Text.js +10 -0
- package/dist/helperComponents/Text/index.d.ts +1 -0
- package/dist/helperComponents/Text/index.js +1 -0
- package/dist/helperComponents/Text/styles.module.css +16 -0
- package/dist/helperComponents/TextBlock/TextBlock.d.ts +5 -0
- package/dist/helperComponents/TextBlock/TextBlock.js +9 -0
- package/dist/helperComponents/TextBlock/index.d.ts +1 -0
- package/dist/helperComponents/TextBlock/index.js +1 -0
- package/dist/helperComponents/TextBlock/styles.module.css +68 -0
- package/dist/helperComponents/index.d.ts +2 -0
- package/dist/helperComponents/index.js +2 -0
- package/dist/helpers.d.ts +3 -0
- package/dist/helpers.js +18 -0
- package/dist/hooks.d.ts +4 -0
- package/dist/hooks.js +26 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +1 -0
- package/package.json +45 -0
- package/src/components/Attachment/Attachment.tsx +55 -0
- package/src/components/Attachment/components/Emblem/Emblem.tsx +47 -0
- package/src/components/Attachment/components/Emblem/index.ts +1 -0
- package/src/components/Attachment/components/Emblem/styled.module.scss +16 -0
- package/src/components/Attachment/components/index.ts +1 -0
- package/src/components/Attachment/index.ts +1 -0
- package/src/components/Attachment/styles.module.scss +20 -0
- package/src/components/AttachmentSquare/AttachmentSquare.tsx +56 -0
- package/src/components/AttachmentSquare/components/HoverContent/HoverContent.tsx +24 -0
- package/src/components/AttachmentSquare/components/HoverContent/index.ts +1 -0
- package/src/components/AttachmentSquare/components/LoadingContent/LoadingContent.tsx +14 -0
- package/src/components/AttachmentSquare/components/LoadingContent/index.ts +1 -0
- package/src/components/AttachmentSquare/components/MainContent/MainContent.tsx +44 -0
- package/src/components/AttachmentSquare/components/MainContent/index.ts +1 -0
- package/src/components/AttachmentSquare/components/index.ts +3 -0
- package/src/components/AttachmentSquare/components/styles.module.scss +17 -0
- package/src/components/AttachmentSquare/index.ts +1 -0
- package/src/components/AttachmentSquare/styles.module.scss +25 -0
- package/src/components/index.ts +2 -0
- package/src/contexts.ts +14 -0
- package/src/helperComponents/Actions/Actions.tsx +48 -0
- package/src/helperComponents/Actions/index.ts +1 -0
- package/src/helperComponents/Actions/styles.module.scss +8 -0
- package/src/helperComponents/Text/Text.tsx +22 -0
- package/src/helperComponents/Text/index.ts +1 -0
- package/src/helperComponents/Text/styles.module.scss +24 -0
- package/src/helperComponents/TextBlock/TextBlock.tsx +22 -0
- package/src/helperComponents/TextBlock/index.ts +1 -0
- package/src/helperComponents/TextBlock/styles.module.scss +63 -0
- package/src/helperComponents/index.ts +2 -0
- package/src/helpers.ts +21 -0
- package/src/hooks.ts +33 -0
- package/src/index.ts +3 -0
- package/src/types.ts +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Text';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.wrapper{
|
|
2
|
+
width:var(--size-attachment-container-square, 80px);
|
|
3
|
+
height:var(--size-attachment-container-square, 80px);
|
|
4
|
+
}
|
|
5
|
+
.wrapper .main{
|
|
6
|
+
display:flex;
|
|
7
|
+
}
|
|
8
|
+
.wrapper .hover{
|
|
9
|
+
display:none;
|
|
10
|
+
}
|
|
11
|
+
.wrapper:hover .hover, .wrapper:focus-visible .hover{
|
|
12
|
+
display:flex;
|
|
13
|
+
}
|
|
14
|
+
.wrapper:hover .main, .wrapper:focus-visible .main{
|
|
15
|
+
display:none;
|
|
16
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AttachmentProps } from '../../types';
|
|
2
|
+
export type TextBlockProps = Pick<AttachmentProps, 'title' | 'description' | 'error' | 'className'> & {
|
|
3
|
+
align?: 'center' | 'left';
|
|
4
|
+
};
|
|
5
|
+
export declare function TextBlock({ title, description, error, className, align }: TextBlockProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { useAttachmentContext } from '../../contexts';
|
|
4
|
+
import { Text } from '../Text';
|
|
5
|
+
import styles from './styles.module.css';
|
|
6
|
+
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 })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TextBlock';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TextBlock';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.content{
|
|
2
|
+
overflow:hidden;
|
|
3
|
+
display:flex;
|
|
4
|
+
flex:1;
|
|
5
|
+
flex-direction:column;
|
|
6
|
+
align-items:flex-start;
|
|
7
|
+
justify-content:flex-start;
|
|
8
|
+
}
|
|
9
|
+
.content[data-align=left]{
|
|
10
|
+
text-align:left;
|
|
11
|
+
}
|
|
12
|
+
.content[data-align=center]{
|
|
13
|
+
max-width:100%;
|
|
14
|
+
text-align:center;
|
|
15
|
+
}
|
|
16
|
+
.content[data-size=s] .title{
|
|
17
|
+
font-family:var(--sans-label-m-font-family, SB Sans Interface);
|
|
18
|
+
font-weight:var(--sans-label-m-font-weight, Semibold);
|
|
19
|
+
line-height:var(--sans-label-m-line-height, 16px);
|
|
20
|
+
font-size:var(--sans-label-m-font-size, 12px);
|
|
21
|
+
letter-spacing:var(--sans-label-m-letter-spacing, -0.1px);
|
|
22
|
+
paragraph-spacing:var(--sans-label-m-paragraph-spacing, 6.6px);
|
|
23
|
+
}
|
|
24
|
+
.content[data-size=s] .description,
|
|
25
|
+
.content[data-size=s] .error{
|
|
26
|
+
font-family:var(--sans-body-s-font-family, SB Sans Interface);
|
|
27
|
+
font-weight:var(--sans-body-s-font-weight, Regular);
|
|
28
|
+
line-height:var(--sans-body-s-line-height, 16px);
|
|
29
|
+
font-size:var(--sans-body-s-font-size, 12px);
|
|
30
|
+
letter-spacing:var(--sans-body-s-letter-spacing, 0.1px);
|
|
31
|
+
paragraph-spacing:var(--sans-body-s-paragraph-spacing, 6.6px);
|
|
32
|
+
}
|
|
33
|
+
.content[data-size=m] .title{
|
|
34
|
+
font-family:var(--sans-label-l-font-family, SB Sans Interface);
|
|
35
|
+
font-weight:var(--sans-label-l-font-weight, Semibold);
|
|
36
|
+
line-height:var(--sans-label-l-line-height, 20px);
|
|
37
|
+
font-size:var(--sans-label-l-font-size, 14px);
|
|
38
|
+
letter-spacing:var(--sans-label-l-letter-spacing, -0.1px);
|
|
39
|
+
paragraph-spacing:var(--sans-label-l-paragraph-spacing, 7.7px);
|
|
40
|
+
}
|
|
41
|
+
.content[data-size=m] .description,
|
|
42
|
+
.content[data-size=m] .error{
|
|
43
|
+
font-family:var(--sans-body-m-font-family, SB Sans Interface);
|
|
44
|
+
font-weight:var(--sans-body-m-font-weight, Regular);
|
|
45
|
+
line-height:var(--sans-body-m-line-height, 20px);
|
|
46
|
+
font-size:var(--sans-body-m-font-size, 14px);
|
|
47
|
+
letter-spacing:var(--sans-body-m-letter-spacing, 0.1px);
|
|
48
|
+
paragraph-spacing:var(--sans-body-m-paragraph-spacing, 7.7px);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.title,
|
|
52
|
+
.description,
|
|
53
|
+
.error{
|
|
54
|
+
overflow:hidden;
|
|
55
|
+
width:100%;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.title{
|
|
59
|
+
color:var(--sys-neutral-text-main, #33333b);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.description{
|
|
63
|
+
color:var(--sys-neutral-text-light, #868892);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.error{
|
|
67
|
+
color:var(--sys-red-text-support, #ab423e);
|
|
68
|
+
}
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function getBaseFileName(fileName) {
|
|
2
|
+
if (!fileName) {
|
|
3
|
+
return fileName;
|
|
4
|
+
}
|
|
5
|
+
const parts = fileName.split('.');
|
|
6
|
+
parts.pop();
|
|
7
|
+
// Для отображения названия файлов без имени (например, .bashrc, .gitignore)
|
|
8
|
+
if (parts.length === 1 && parts[0] === '')
|
|
9
|
+
return fileName;
|
|
10
|
+
return parts.join('.');
|
|
11
|
+
}
|
|
12
|
+
export function getFileExtension(fileName) {
|
|
13
|
+
var _a;
|
|
14
|
+
return (_a = fileName === null || fileName === void 0 ? void 0 : fileName.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLocaleUpperCase();
|
|
15
|
+
}
|
|
16
|
+
export function isFileImage(file) {
|
|
17
|
+
return file.type.match(/image\/(png|jpg|jpeg|gif|webp|bmp|svg)/i);
|
|
18
|
+
}
|
package/dist/hooks.d.ts
ADDED
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { isFileImage } from './helpers';
|
|
3
|
+
export function useImage(file) {
|
|
4
|
+
const [imageData, setImageData] = useState(undefined);
|
|
5
|
+
const [loading, setIsLoading] = useState(false);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (file && isFileImage(file)) {
|
|
8
|
+
const reader = new FileReader();
|
|
9
|
+
reader.onloadstart = () => {
|
|
10
|
+
setIsLoading(true);
|
|
11
|
+
};
|
|
12
|
+
reader.onloadend = () => {
|
|
13
|
+
setIsLoading(false);
|
|
14
|
+
const { result } = reader;
|
|
15
|
+
if (result) {
|
|
16
|
+
setImageData(result.toString());
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
reader.readAsDataURL(file);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
setImageData(undefined);
|
|
23
|
+
}
|
|
24
|
+
}, [file]);
|
|
25
|
+
return { imageData, loading };
|
|
26
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { CardProps } from '@snack-uikit/card';
|
|
2
|
+
import { IconPredefinedProps } from '@snack-uikit/icon-predefined';
|
|
3
|
+
import { TruncateStringProps } from '@snack-uikit/truncate-string';
|
|
4
|
+
import { WithSupportProps } from '@snack-uikit/utils';
|
|
5
|
+
export type Size = 's' | 'm';
|
|
6
|
+
export type AttachmentCommonProps = WithSupportProps<{
|
|
7
|
+
/** Файл */
|
|
8
|
+
file?: File;
|
|
9
|
+
/** Колбек на клик по кнопке скачивания */
|
|
10
|
+
onDownload?(file?: File | undefined): void;
|
|
11
|
+
/** Колбек на клик по кнопке удаления */
|
|
12
|
+
onDelete?(file?: File | undefined): void;
|
|
13
|
+
/**
|
|
14
|
+
* Заголовок
|
|
15
|
+
* @default file.name
|
|
16
|
+
*/
|
|
17
|
+
title?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Описание
|
|
20
|
+
* @default file.type
|
|
21
|
+
*/
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Сообщение об ошибке */
|
|
24
|
+
error?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Вариант обрезания строки:
|
|
27
|
+
* <br> - `End` - с конца;
|
|
28
|
+
* <br> - `Middle` - по середине
|
|
29
|
+
* */
|
|
30
|
+
truncateVariant?: TruncateStringProps['variant'];
|
|
31
|
+
/** Управление состоянием загрузки */
|
|
32
|
+
loading?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Объект сопоставления расширения файла и отображаемой иконки
|
|
35
|
+
* @defaultIcon FileSVG
|
|
36
|
+
*/
|
|
37
|
+
extension2Icon?: Record<string, IconPredefinedProps['icon']>;
|
|
38
|
+
}> & Pick<CardProps, 'checked' | 'disabled' | 'multipleSelection' | 'onClick' | 'className'>;
|
|
39
|
+
export type AttachmentSquareProps = AttachmentCommonProps;
|
|
40
|
+
export type AttachmentProps = AttachmentCommonProps & {
|
|
41
|
+
/** Размер */
|
|
42
|
+
size?: Size;
|
|
43
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@snack-uikit/attachment",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"title": "Attachment",
|
|
7
|
+
"version": "0.0.1-preview-8b6f4393.0",
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"*.css",
|
|
10
|
+
"*.woff",
|
|
11
|
+
"*.woff2"
|
|
12
|
+
],
|
|
13
|
+
"description": "",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"homepage": "https://cloud-ru-tech.github.io/snack-uikit/packages/attachment",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/cloud-ru-tech/snack-uikit/tree/master",
|
|
20
|
+
"directory": "packages/attachment"
|
|
21
|
+
},
|
|
22
|
+
"author": "Nikita Ershov <niershov@cloud.ru>",
|
|
23
|
+
"contributors": [
|
|
24
|
+
"Nikita Ershov <niershov@cloud.ru>"
|
|
25
|
+
],
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"src",
|
|
29
|
+
"./CHANGELOG.md",
|
|
30
|
+
"./LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"license": "Apache-2.0",
|
|
33
|
+
"scripts": {},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@snack-uikit/button": "0.17.1",
|
|
36
|
+
"@snack-uikit/card": "0.12.13",
|
|
37
|
+
"@snack-uikit/icon-predefined": "0.5.1",
|
|
38
|
+
"@snack-uikit/icons": "0.21.0",
|
|
39
|
+
"@snack-uikit/loaders": "0.5.2",
|
|
40
|
+
"@snack-uikit/truncate-string": "0.4.14",
|
|
41
|
+
"@snack-uikit/utils": "3.3.0",
|
|
42
|
+
"classnames": "2.3.2"
|
|
43
|
+
},
|
|
44
|
+
"gitHead": "22c140322d6f41f78990eaf8ae6a9b38ea039247"
|
|
45
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Card } from '@snack-uikit/card';
|
|
2
|
+
import { extractSupportProps } from '@snack-uikit/utils';
|
|
3
|
+
|
|
4
|
+
import { AttachmentContext } from '../../contexts';
|
|
5
|
+
import { Actions, TextBlock } from '../../helperComponents';
|
|
6
|
+
import { getBaseFileName, getFileExtension } from '../../helpers';
|
|
7
|
+
import { useImage } from '../../hooks';
|
|
8
|
+
import { AttachmentProps } from '../../types';
|
|
9
|
+
import { Emblem } from './components/Emblem';
|
|
10
|
+
import styles from './styles.module.scss';
|
|
11
|
+
|
|
12
|
+
export function Attachment({
|
|
13
|
+
file,
|
|
14
|
+
loading,
|
|
15
|
+
extension2Icon,
|
|
16
|
+
title: titleProp,
|
|
17
|
+
description: descriptionProp,
|
|
18
|
+
error,
|
|
19
|
+
disabled,
|
|
20
|
+
truncateVariant,
|
|
21
|
+
checked,
|
|
22
|
+
multipleSelection,
|
|
23
|
+
onDownload,
|
|
24
|
+
onDelete,
|
|
25
|
+
onClick,
|
|
26
|
+
size = 's',
|
|
27
|
+
...rest
|
|
28
|
+
}: AttachmentProps) {
|
|
29
|
+
const { loading: loadingImage, imageData } = useImage(file);
|
|
30
|
+
const isLoading = loading || loadingImage;
|
|
31
|
+
|
|
32
|
+
const title = titleProp || getBaseFileName(file?.name);
|
|
33
|
+
const description = descriptionProp || getFileExtension(file?.name);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Card
|
|
37
|
+
{...extractSupportProps(rest)}
|
|
38
|
+
outline
|
|
39
|
+
disabled={disabled}
|
|
40
|
+
checked={checked}
|
|
41
|
+
multipleSelection={multipleSelection}
|
|
42
|
+
size={size}
|
|
43
|
+
onClick={!isLoading && !disabled ? onClick : undefined}
|
|
44
|
+
header={
|
|
45
|
+
<AttachmentContext.Provider value={{ file, truncateVariant, onDelete, onDownload, disabled, size }}>
|
|
46
|
+
<div className={styles.composition} data-size={size}>
|
|
47
|
+
<Emblem title={title} loading={isLoading} extension2Icon={extension2Icon} imageData={imageData} />
|
|
48
|
+
<TextBlock title={title} description={description} error={error} />
|
|
49
|
+
<Actions hideDownload={isLoading} />
|
|
50
|
+
</div>
|
|
51
|
+
</AttachmentContext.Provider>
|
|
52
|
+
}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
4
|
+
import { FileSVG } from '@snack-uikit/icons';
|
|
5
|
+
import { Spinner } from '@snack-uikit/loaders';
|
|
6
|
+
|
|
7
|
+
import { useAttachmentContext } from '../../../../contexts';
|
|
8
|
+
import { getFileExtension } from '../../../../helpers';
|
|
9
|
+
import { AttachmentProps } from '../../../../types';
|
|
10
|
+
import styles from './styled.module.scss';
|
|
11
|
+
|
|
12
|
+
export type EmblemProps = {
|
|
13
|
+
imageData?: string;
|
|
14
|
+
} & Pick<AttachmentProps, 'extension2Icon' | 'loading' | 'title'>;
|
|
15
|
+
|
|
16
|
+
export function Emblem({ loading, extension2Icon, imageData, title }: EmblemProps) {
|
|
17
|
+
const { file, size } = useAttachmentContext();
|
|
18
|
+
|
|
19
|
+
const extension = useMemo(() => getFileExtension(file?.name) ?? '', [file?.name]);
|
|
20
|
+
|
|
21
|
+
if (loading) {
|
|
22
|
+
return (
|
|
23
|
+
<IconPredefined size={size} icon={() => <Spinner size='s' />} decor={false} appearance='neutral' shape='square' />
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (imageData) {
|
|
28
|
+
return (
|
|
29
|
+
<IconPredefined
|
|
30
|
+
size={size}
|
|
31
|
+
icon={() => <img src={imageData} alt={title} data-size={size || undefined} className={styles.img} />}
|
|
32
|
+
decor={false}
|
|
33
|
+
shape='square'
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<IconPredefined
|
|
40
|
+
size={size}
|
|
41
|
+
icon={extension2Icon?.[extension] ?? FileSVG}
|
|
42
|
+
decor
|
|
43
|
+
appearance='neutral'
|
|
44
|
+
shape='square'
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Emblem';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-attachment';
|
|
2
|
+
|
|
3
|
+
$sizes: 's', 'm';
|
|
4
|
+
|
|
5
|
+
.img {
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
flex-shrink: 0;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
object-fit: cover;
|
|
10
|
+
|
|
11
|
+
@each $size in $sizes {
|
|
12
|
+
&[data-size='#{$size}'] {
|
|
13
|
+
@include composite-var($attachment, 'composition', 'start-item', $size);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Emblem';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Attachment';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-attachment';
|
|
2
|
+
|
|
3
|
+
$sizes: 's', 'm';
|
|
4
|
+
|
|
5
|
+
.composition {
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
width: 100%;
|
|
12
|
+
margin: calc(0px - $dimension-050m);
|
|
13
|
+
padding: $dimension-050m;
|
|
14
|
+
|
|
15
|
+
@each $size in $sizes {
|
|
16
|
+
&[data-size='#{$size}'] {
|
|
17
|
+
@include composite-var($attachment, 'composition', $size);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Card } from '@snack-uikit/card';
|
|
2
|
+
import { extractSupportProps } from '@snack-uikit/utils';
|
|
3
|
+
|
|
4
|
+
import { AttachmentContext } from '../../contexts';
|
|
5
|
+
import { getBaseFileName, getFileExtension } from '../../helpers';
|
|
6
|
+
import { useImage } from '../../hooks';
|
|
7
|
+
import { AttachmentSquareProps } from '../../types';
|
|
8
|
+
import { HoverContent, LoadingContent, MainContent } from './components';
|
|
9
|
+
import styles from './styles.module.scss';
|
|
10
|
+
|
|
11
|
+
export function AttachmentSquare({
|
|
12
|
+
file,
|
|
13
|
+
loading,
|
|
14
|
+
extension2Icon,
|
|
15
|
+
title: titleProp,
|
|
16
|
+
description: descriptionProp,
|
|
17
|
+
error,
|
|
18
|
+
disabled,
|
|
19
|
+
truncateVariant,
|
|
20
|
+
checked,
|
|
21
|
+
multipleSelection,
|
|
22
|
+
onDownload,
|
|
23
|
+
onDelete,
|
|
24
|
+
onClick,
|
|
25
|
+
...rest
|
|
26
|
+
}: AttachmentSquareProps) {
|
|
27
|
+
const { loading: loadingImage, imageData } = useImage(file);
|
|
28
|
+
const isLoading = loading || loadingImage;
|
|
29
|
+
|
|
30
|
+
const title = titleProp || getBaseFileName(file?.name);
|
|
31
|
+
const description = descriptionProp || getFileExtension(file?.name);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Card
|
|
35
|
+
{...extractSupportProps(rest)}
|
|
36
|
+
outline
|
|
37
|
+
disabled={disabled}
|
|
38
|
+
size='s'
|
|
39
|
+
className={styles.wrapper}
|
|
40
|
+
checked={checked}
|
|
41
|
+
multipleSelection={multipleSelection}
|
|
42
|
+
onClick={!isLoading && !disabled ? onClick : undefined}
|
|
43
|
+
header={
|
|
44
|
+
<AttachmentContext.Provider value={{ file, truncateVariant, onDelete, onDownload, disabled, size: 's' }}>
|
|
45
|
+
{!isLoading && !disabled && (
|
|
46
|
+
<MainContent title={title} extension2Icon={extension2Icon} imageData={imageData} className={styles.main} />
|
|
47
|
+
)}
|
|
48
|
+
{!isLoading && (
|
|
49
|
+
<HoverContent title={title} description={description} error={error} className={styles.hover} />
|
|
50
|
+
)}
|
|
51
|
+
{isLoading && <LoadingContent />}
|
|
52
|
+
</AttachmentContext.Provider>
|
|
53
|
+
}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
|
|
3
|
+
import { useAttachmentContext } from '../../../../contexts';
|
|
4
|
+
import { Actions, TextBlock } from '../../../../helperComponents';
|
|
5
|
+
import { AttachmentProps } from '../../../../types';
|
|
6
|
+
import styles from '../styles.module.scss';
|
|
7
|
+
|
|
8
|
+
type HoverContentProps = Pick<AttachmentProps, 'title' | 'description' | 'error' | 'className'>;
|
|
9
|
+
|
|
10
|
+
export function HoverContent({ title, description, error, className = '' }: HoverContentProps) {
|
|
11
|
+
const { disabled } = useAttachmentContext();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(styles.composition, {
|
|
16
|
+
[className]: !disabled,
|
|
17
|
+
})}
|
|
18
|
+
>
|
|
19
|
+
<TextBlock title={title} error={error} description={!error ? description : undefined} align='center' />
|
|
20
|
+
|
|
21
|
+
<Actions />
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './HoverContent';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
2
|
+
import { Spinner } from '@snack-uikit/loaders';
|
|
3
|
+
|
|
4
|
+
import { Actions } from '../../../../helperComponents';
|
|
5
|
+
import styles from '../styles.module.scss';
|
|
6
|
+
|
|
7
|
+
export function LoadingContent() {
|
|
8
|
+
return (
|
|
9
|
+
<div className={styles.composition}>
|
|
10
|
+
<IconPredefined size='s' icon={() => <Spinner size='s' />} decor={false} appearance='neutral' shape='square' />
|
|
11
|
+
<Actions hideDownload />
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './LoadingContent';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Card } from '@snack-uikit/card';
|
|
5
|
+
import { IconPredefined } from '@snack-uikit/icon-predefined';
|
|
6
|
+
import { FileSVG } from '@snack-uikit/icons';
|
|
7
|
+
|
|
8
|
+
import { useAttachmentContext } from '../../../../contexts';
|
|
9
|
+
import { TextBlock } from '../../../../helperComponents';
|
|
10
|
+
import { getFileExtension } from '../../../../helpers';
|
|
11
|
+
import { AttachmentProps } from '../../../../types';
|
|
12
|
+
import styles from '../styles.module.scss';
|
|
13
|
+
|
|
14
|
+
type MainContentProps = {
|
|
15
|
+
imageData?: string;
|
|
16
|
+
} & Pick<AttachmentProps, 'title' | 'extension2Icon' | 'className'>;
|
|
17
|
+
|
|
18
|
+
export function MainContent({ title, extension2Icon, imageData, className }: MainContentProps) {
|
|
19
|
+
const { file } = useAttachmentContext();
|
|
20
|
+
|
|
21
|
+
const extension = useMemo(() => getFileExtension(file?.name) ?? '', [file?.name]);
|
|
22
|
+
|
|
23
|
+
if (imageData) {
|
|
24
|
+
return (
|
|
25
|
+
<div className={cn(styles.composition, className)}>
|
|
26
|
+
<Card.Image src={imageData} alt={file?.name ?? 'file'} mode='background' hideFading />
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className={cn(styles.composition, className)}>
|
|
33
|
+
<TextBlock title={title} align='center' />
|
|
34
|
+
|
|
35
|
+
<IconPredefined
|
|
36
|
+
size='s'
|
|
37
|
+
icon={extension2Icon?.[extension] ?? FileSVG}
|
|
38
|
+
decor
|
|
39
|
+
appearance='neutral'
|
|
40
|
+
shape='square'
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MainContent';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-attachment';
|
|
2
|
+
|
|
3
|
+
.composition {
|
|
4
|
+
@include composite-var($attachment-composition-s);
|
|
5
|
+
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
align-items: center;
|
|
10
|
+
|
|
11
|
+
box-sizing: content-box;
|
|
12
|
+
width: 100%;
|
|
13
|
+
margin: calc(0px - $dimension-050m);
|
|
14
|
+
padding: $dimension-050m;
|
|
15
|
+
|
|
16
|
+
text-align: center;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AttachmentSquare';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-attachment';
|
|
2
|
+
|
|
3
|
+
.wrapper {
|
|
4
|
+
@include composite-var($attachment-container-square);
|
|
5
|
+
|
|
6
|
+
.main {
|
|
7
|
+
display: flex;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.hover {
|
|
11
|
+
display: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&:hover,
|
|
15
|
+
&:focus-within,
|
|
16
|
+
&:focus-visible {
|
|
17
|
+
.hover {
|
|
18
|
+
display: flex;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.main {
|
|
22
|
+
display: none;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/contexts.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import { AttachmentProps } from './types';
|
|
4
|
+
|
|
5
|
+
export type AttachmentContextType = Pick<
|
|
6
|
+
AttachmentProps,
|
|
7
|
+
'size' | 'file' | 'truncateVariant' | 'onDownload' | 'onDelete' | 'disabled'
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
export const AttachmentContext = createContext<AttachmentContextType>({});
|
|
11
|
+
|
|
12
|
+
export function useAttachmentContext() {
|
|
13
|
+
return useContext(AttachmentContext);
|
|
14
|
+
}
|