mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: wip UI attachments
This commit is contained in:
@@ -96,7 +96,7 @@ export default class ManualJournal extends mixin(TenantModel, [
|
|||||||
static get relationMappings() {
|
static get relationMappings() {
|
||||||
const AccountTransaction = require('models/AccountTransaction');
|
const AccountTransaction = require('models/AccountTransaction');
|
||||||
const ManualJournalEntry = require('models/ManualJournalEntry');
|
const ManualJournalEntry = require('models/ManualJournalEntry');
|
||||||
const ManualJournal = require('models/ManualJournal');
|
const Document = require('models/Document');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entries: {
|
entries: {
|
||||||
@@ -127,7 +127,7 @@ export default class ManualJournal extends mixin(TenantModel, [
|
|||||||
*/
|
*/
|
||||||
attachments: {
|
attachments: {
|
||||||
relation: Model.ManyToManyRelation,
|
relation: Model.ManyToManyRelation,
|
||||||
modelClass: ManualJournal.default,
|
modelClass: Document.default,
|
||||||
join: {
|
join: {
|
||||||
from: 'manual_journals.id',
|
from: 'manual_journals.id',
|
||||||
through: {
|
through: {
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { Button, Intent } from '@blueprintjs/core';
|
import { Button, Intent } from '@blueprintjs/core';
|
||||||
|
import clsx from 'classnames';
|
||||||
import { Box, Icon, Stack } from '@/components';
|
import { Box, Icon, Stack } from '@/components';
|
||||||
import { Dropzone, DropzoneProps } from '@/components/Dropzone';
|
import { Dropzone, DropzoneProps } from '@/components/Dropzone';
|
||||||
import { MIME_TYPES } from '@/components/Dropzone/mine-types';
|
import { MIME_TYPES } from '@/components/Dropzone/mine-types';
|
||||||
import styles from './ImportDropzone.module.css';
|
|
||||||
import { useUncontrolled } from '@/hooks/useUncontrolled';
|
import { useUncontrolled } from '@/hooks/useUncontrolled';
|
||||||
|
import styles from './ImportDropzone.module.css';
|
||||||
|
|
||||||
interface ImportDropzoneFieldProps {
|
interface ImportDropzoneFieldProps {
|
||||||
initialValue?: File;
|
initialValue?: File;
|
||||||
value?: File;
|
value?: File;
|
||||||
onChange?: (file: File) => void;
|
onChange?: (file: File) => void;
|
||||||
dropzoneProps?: DropzoneProps;
|
dropzoneProps?: DropzoneProps;
|
||||||
|
uploadIcon?: JSX.Element;
|
||||||
|
title?: string;
|
||||||
|
subtitle?: string;
|
||||||
|
classNames?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ImportDropzoneField({
|
export function ImportDropzoneField({
|
||||||
@@ -19,6 +24,10 @@ export function ImportDropzoneField({
|
|||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
dropzoneProps,
|
dropzoneProps,
|
||||||
|
uploadIcon = <Icon icon="download" iconSize={26} />,
|
||||||
|
title = 'Drag images here or click to select files',
|
||||||
|
subtitle = 'Drag and Drop file here or Choose file',
|
||||||
|
classNames,
|
||||||
}: ImportDropzoneFieldProps) {
|
}: ImportDropzoneFieldProps) {
|
||||||
const [localValue, handleChange] = useUncontrolled({
|
const [localValue, handleChange] = useUncontrolled({
|
||||||
value,
|
value,
|
||||||
@@ -38,15 +47,18 @@ export function ImportDropzoneField({
|
|||||||
onReject={(files) => console.log('rejected files', files)}
|
onReject={(files) => console.log('rejected files', files)}
|
||||||
maxSize={5 * 1024 ** 2}
|
maxSize={5 * 1024 ** 2}
|
||||||
accept={[MIME_TYPES.csv, MIME_TYPES.xls, MIME_TYPES.xlsx]}
|
accept={[MIME_TYPES.csv, MIME_TYPES.xls, MIME_TYPES.xlsx]}
|
||||||
classNames={{ content: styles.dropzoneContent }}
|
classNames={{ root: classNames?.root, content: styles.dropzoneContent }}
|
||||||
activateOnClick={false}
|
activateOnClick={false}
|
||||||
openRef={openRef}
|
openRef={openRef}
|
||||||
{...dropzoneProps}
|
{...dropzoneProps}
|
||||||
>
|
>
|
||||||
<Stack spacing={12} align="center" className={styles.content}>
|
<Stack
|
||||||
<Box className={styles.iconWrap}>
|
spacing={12}
|
||||||
<Icon icon="download" iconSize={26} />
|
align="center"
|
||||||
</Box>
|
className={clsx(styles.content, classNames?.content)}
|
||||||
|
>
|
||||||
|
{uploadIcon && <Box className={styles.iconWrap}>{uploadIcon}</Box>}
|
||||||
|
|
||||||
{localValue ? (
|
{localValue ? (
|
||||||
<Stack spacing={6} justify="center" align="center">
|
<Stack spacing={6} justify="center" align="center">
|
||||||
<h4 className={styles.title}>{localValue.name}</h4>
|
<h4 className={styles.title}>{localValue.name}</h4>
|
||||||
@@ -56,15 +68,10 @@ export function ImportDropzoneField({
|
|||||||
</Stack>
|
</Stack>
|
||||||
) : (
|
) : (
|
||||||
<Stack spacing={4} align="center">
|
<Stack spacing={4} align="center">
|
||||||
<h4 className={styles.title}>
|
{title && <h4 className={styles.title}>{title}</h4>}
|
||||||
Drag images here or click to select files
|
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
|
||||||
</h4>
|
|
||||||
<span className={styles.subtitle}>
|
|
||||||
Drag and Drop file here or Choose file
|
|
||||||
</span>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
intent="none"
|
intent="none"
|
||||||
onClick={() => openRef.current?.()}
|
onClick={() => openRef.current?.()}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { CLASSES } from '@/constants/classes';
|
|||||||
import { Paper, Row, Col } from '@/components';
|
import { Paper, Row, Col } from '@/components';
|
||||||
import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft';
|
import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft';
|
||||||
import { InvoiceFormFooterRight } from './InvoiceFormFooterRight';
|
import { InvoiceFormFooterRight } from './InvoiceFormFooterRight';
|
||||||
|
import { UploadAttachmentButton } from './UploadAttachmentButton';
|
||||||
|
|
||||||
export default function InvoiceFormFooter() {
|
export default function InvoiceFormFooter() {
|
||||||
return (
|
return (
|
||||||
@@ -15,6 +16,7 @@ export default function InvoiceFormFooter() {
|
|||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<InvoiceFormFooterLeft />
|
<InvoiceFormFooterLeft />
|
||||||
|
<UploadAttachmentButton />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
.popover :global .bp4-popover-content{
|
||||||
|
min-width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.attachmentButton:not([class*=bp4-intent-]) {
|
||||||
|
&,
|
||||||
|
&:hover{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
border: 1px solid rgb(206, 212, 218);
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentField :global .bp4-label{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import clsx from 'classnames';
|
||||||
|
import { Field, useFormikContext } from 'formik';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Classes,
|
||||||
|
Popover,
|
||||||
|
PopoverInteractionKind,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { FFormGroup } from '@/components';
|
||||||
|
import { UploadAttachmentsPopoverContent } from './UploadAttachmentsPopoverContent';
|
||||||
|
import styles from './UploadAttachmentButton.module.scss';
|
||||||
|
|
||||||
|
function UploadAttachmentButtonButtonContentField() {
|
||||||
|
return (
|
||||||
|
<Field name={'attachments'}>
|
||||||
|
{({ form: { setFieldValue }, field: { value } }) => (
|
||||||
|
<UploadAttachmentsPopoverContent
|
||||||
|
value={value}
|
||||||
|
onChange={(value) => {
|
||||||
|
setFieldValue('attachments', value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UploadAttachmentButton() {
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
const uploadedFiles = values?.attachments?.length || 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
name={'attachments'}
|
||||||
|
label={'Attachments'}
|
||||||
|
className={styles.attachmentField}
|
||||||
|
>
|
||||||
|
<Popover
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
popoverClassName={clsx(styles.popover, Classes.POPOVER_CONTENT_SIZING)}
|
||||||
|
placement={'top-start'}
|
||||||
|
content={<UploadAttachmentButtonButtonContentField />}
|
||||||
|
>
|
||||||
|
<Button className={styles.attachmentButton}>
|
||||||
|
{uploadedFiles > 0 ? (
|
||||||
|
<>Upload attachments ({uploadedFiles})</>
|
||||||
|
) : (
|
||||||
|
<>Upload attachments</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
.content {
|
||||||
|
}
|
||||||
|
|
||||||
|
.hintText{
|
||||||
|
display: flex;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #8F99A8;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments{
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentItem{
|
||||||
|
border: 1px solid #D3D8DE;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentFilenameText{
|
||||||
|
color: #404854;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentSizeText{
|
||||||
|
font-size: 12px;
|
||||||
|
color: #738091;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentContent{
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachmentIcon{
|
||||||
|
color: #8F99A8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label{
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropzoneRoot{
|
||||||
|
min-height: 140px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import { Button, Intent, Text, Spinner } from '@blueprintjs/core';
|
||||||
|
import { Box, Group, Icon, Stack } from '@/components';
|
||||||
|
import { ImportDropzoneField } from '@/containers/Import/ImportDropzoneFile';
|
||||||
|
import { useUncontrolled } from '@/hooks/useUncontrolled';
|
||||||
|
import {
|
||||||
|
useDeleteAttachment,
|
||||||
|
useUploadAttachments,
|
||||||
|
} from '@/hooks/query/attachments';
|
||||||
|
import { formatBytes } from './utils';
|
||||||
|
import styles from './UploadAttachmentPopoverContent.module.scss';
|
||||||
|
|
||||||
|
interface AttachmentFileCommon {
|
||||||
|
originName: string;
|
||||||
|
key: string;
|
||||||
|
size: number;
|
||||||
|
mimeType: string;
|
||||||
|
}
|
||||||
|
interface AttachmentFileLoaded extends AttachmentFileCommon {}
|
||||||
|
interface AttachmentFileLoading extends AttachmentFileCommon {
|
||||||
|
_loading: boolean;
|
||||||
|
}
|
||||||
|
type AttachmentFile = AttachmentFileLoaded | AttachmentFileLoading;
|
||||||
|
|
||||||
|
interface UploadAttachmentsPopoverContentProps {
|
||||||
|
initialValue?: AttachmentFile[];
|
||||||
|
value?: AttachmentFile[];
|
||||||
|
onChange?: (value: AttachmentFile[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads and list the attachments with ability to delete particular attachment.
|
||||||
|
* @param {UploadAttachmentsPopoverContentProps}
|
||||||
|
*/
|
||||||
|
export function UploadAttachmentsPopoverContent({
|
||||||
|
initialValue,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: UploadAttachmentsPopoverContentProps) {
|
||||||
|
// Controlled/uncontrolled value state.
|
||||||
|
const [localFiles, handleFilesChange] = useUncontrolled<AttachmentFile[]>({
|
||||||
|
finalValue: [],
|
||||||
|
initialValue,
|
||||||
|
value,
|
||||||
|
onChange: onChange,
|
||||||
|
});
|
||||||
|
// Stops loading of the given attachment key and updates it to new key,
|
||||||
|
// that came from the server-side after uploading is done.
|
||||||
|
const stopLoadingAttachment = (
|
||||||
|
localFiles: AttachmentFile[],
|
||||||
|
internalKey: string,
|
||||||
|
newKey: string,
|
||||||
|
) => {
|
||||||
|
return localFiles.map((localFile) => {
|
||||||
|
if (localFile.key === internalKey) {
|
||||||
|
return {
|
||||||
|
...localFile,
|
||||||
|
key: newKey,
|
||||||
|
_loading: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return localFile;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Uploads the attachments.
|
||||||
|
const { mutateAsync: uploadAttachments } = useUploadAttachments({
|
||||||
|
onSuccess: (data, variables, context) => {
|
||||||
|
const newLocalFiles = stopLoadingAttachment(
|
||||||
|
localFiles,
|
||||||
|
data.config.data.get('internalKey'),
|
||||||
|
data.data.data.key,
|
||||||
|
);
|
||||||
|
handleFilesChange(newLocalFiles);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// Deletes the attachment.
|
||||||
|
const { mutateAsync: deleteAttachment } = useDeleteAttachment();
|
||||||
|
|
||||||
|
// Deletes the attachment of the given file key.
|
||||||
|
const DeleteButton = ({ fileKey }: { fileKey: string }) => {
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setLoading(true);
|
||||||
|
deleteAttachment(fileKey).then(() => {
|
||||||
|
const updatedFiles = localFiles.filter(
|
||||||
|
(file, i) => file.key !== fileKey,
|
||||||
|
);
|
||||||
|
handleFilesChange(updatedFiles);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
small
|
||||||
|
minimal
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
loading={loading}
|
||||||
|
disabled={loading}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<Icon icon={'trash-16'} iconSize={16} />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
// Handle change dropzone.
|
||||||
|
const handleChangeDropzone = (file: File) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
const key = Date.now().toString();
|
||||||
|
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('internalKey', key);
|
||||||
|
|
||||||
|
handleFilesChange([
|
||||||
|
{
|
||||||
|
originName: file.name,
|
||||||
|
size: file.size,
|
||||||
|
key,
|
||||||
|
_loading: true,
|
||||||
|
},
|
||||||
|
...localFiles,
|
||||||
|
]);
|
||||||
|
uploadAttachments(formData);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.content}>
|
||||||
|
<div>
|
||||||
|
<Text className={styles.label}>Attach documents</Text>
|
||||||
|
<Stack spacing={0}>
|
||||||
|
<ImportDropzoneField
|
||||||
|
uploadIcon={null}
|
||||||
|
value={null}
|
||||||
|
title={''}
|
||||||
|
classNames={{ root: styles.dropzoneRoot }}
|
||||||
|
onChange={handleChangeDropzone}
|
||||||
|
/>
|
||||||
|
<Group className={styles.hintText}>
|
||||||
|
<Box>Formats: CSV, XLSX</Box>
|
||||||
|
<Box>Maximum: 25MB</Box>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{!isEmpty(localFiles) && (
|
||||||
|
<Stack spacing={8} className={styles.attachments}>
|
||||||
|
{localFiles.map((localFile: AttachmentFile, index: number) => (
|
||||||
|
<Group
|
||||||
|
position={'space-between'}
|
||||||
|
className={styles.attachmentItem}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<Group spacing={16} className={styles.attachmentContent}>
|
||||||
|
{localFile._loading ? (
|
||||||
|
<Spinner size={20} />
|
||||||
|
) : (
|
||||||
|
<Icon
|
||||||
|
icon={'media'}
|
||||||
|
iconSize={16}
|
||||||
|
className={styles.attachmentIcon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Stack spacing={2}>
|
||||||
|
<Text className={styles.attachmentFilenameText}>
|
||||||
|
{localFile.originName}
|
||||||
|
</Text>
|
||||||
|
{localFile._loading ? (
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
) : (
|
||||||
|
<Text className={styles.attachmentSizeText}>
|
||||||
|
{formatBytes(localFile.size)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{!localFile._loading && (
|
||||||
|
<Group spacing={0}>
|
||||||
|
<Button small minimal>
|
||||||
|
View
|
||||||
|
</Button>
|
||||||
|
<DeleteButton fileKey={localFile.key} />
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
packages/webapp/src/hooks/query/attachments.ts
Normal file
31
packages/webapp/src/hooks/query/attachments.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
|
import useApiRequest from '../useRequest';
|
||||||
|
|
||||||
|
const commonInvalidateQueries = (query) => {
|
||||||
|
// Invalidate accounts.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the given attachments.
|
||||||
|
*/
|
||||||
|
export function useUploadAttachments(props) {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(values) => apiRequest.post('/attachments', values),
|
||||||
|
props,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given attachment key.
|
||||||
|
*/
|
||||||
|
export function useDeleteAttachment(props) {
|
||||||
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(key: string) => apiRequest.delete(`/attachments/${key}`),
|
||||||
|
props,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -578,26 +578,30 @@ export default {
|
|||||||
viewBox: '0 0 20 20',
|
viewBox: '0 0 20 20',
|
||||||
},
|
},
|
||||||
done: {
|
done: {
|
||||||
path: [
|
path: ['M395-285 226-455l50-50 119 118 289-288 50 51-339 339Z'],
|
||||||
'M395-285 226-455l50-50 119 118 289-288 50 51-339 339Z',
|
viewBox: '0 -960 960 960',
|
||||||
],
|
|
||||||
viewBox: '0 -960 960 960'
|
|
||||||
},
|
},
|
||||||
download: {
|
download: {
|
||||||
path: [
|
path: [
|
||||||
'M480-336 288-528l51-51 105 105v-342h72v342l105-105 51 51-192 192ZM263.717-192Q234-192 213-213.15T192-264v-72h72v72h432v-72h72v72q0 29.7-21.162 50.85Q725.676-192 695.96-192H263.717Z'
|
'M480-336 288-528l51-51 105 105v-342h72v342l105-105 51 51-192 192ZM263.717-192Q234-192 213-213.15T192-264v-72h72v72h432v-72h72v72q0 29.7-21.162 50.85Q725.676-192 695.96-192H263.717Z',
|
||||||
],
|
],
|
||||||
viewBox: '0 -960 960 960'
|
viewBox: '0 -960 960 960',
|
||||||
},
|
},
|
||||||
'chevron-up': {
|
'chevron-up': {
|
||||||
path: [
|
path: [
|
||||||
'M12.71,9.29l-4-4C8.53,5.11,8.28,5,8,5S7.47,5.11,7.29,5.29l-4,4C3.11,9.47,3,9.72,3,10c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29L8,7.41l3.29,3.29C11.47,10.89,11.72,11,12,11c0.55,0,1-0.45,1-1C13,9.72,12.89,9.47,12.71,9.29'
|
'M12.71,9.29l-4-4C8.53,5.11,8.28,5,8,5S7.47,5.11,7.29,5.29l-4,4C3.11,9.47,3,9.72,3,10c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29L8,7.41l3.29,3.29C11.47,10.89,11.72,11,12,11c0.55,0,1-0.45,1-1C13,9.72,12.89,9.47,12.71,9.29',
|
||||||
],
|
],
|
||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
},
|
},
|
||||||
'chevron-down': {
|
'chevron-down': {
|
||||||
path: [
|
path: [
|
||||||
'M12,5c-0.28,0-0.53,0.11-0.71,0.29L8,8.59L4.71,5.29C4.53,5.11,4.28,5,4,5C3.45,5,3,5.45,3,6c0,0.28,0.11,0.53,0.29,0.71l4,4C7.47,10.89,7.72,11,8,11s0.53-0.11,0.71-0.29l4-4C12.89,6.53,13,6.28,13,6C13,5.45,12.55,5,12,5z'
|
'M12,5c-0.28,0-0.53,0.11-0.71,0.29L8,8.59L4.71,5.29C4.53,5.11,4.28,5,4,5C3.45,5,3,5.45,3,6c0,0.28,0.11,0.53,0.29,0.71l4,4C7.47,10.89,7.72,11,8,11s0.53-0.11,0.71-0.29l4-4C12.89,6.53,13,6.28,13,6C13,5.45,12.55,5,12,5z',
|
||||||
|
],
|
||||||
|
viewBox: '0 0 16 16',
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
path: [
|
||||||
|
'M11.99,6.99c0.55,0,1-0.45,1-1s-0.45-1-1-1s-1,0.45-1,1S11.44,6.99,11.99,6.99zM14.99,1.99h-14c-0.55,0-1,0.45-1,1v10c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1v-10C15.99,2.44,15.54,1.99,14.99,1.99zM13.99,10.99l-5-3l-1,2l-3-4l-3,5v-7h12V10.99z',
|
||||||
],
|
],
|
||||||
viewBox: '0 0 16 16',
|
viewBox: '0 0 16 16',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user