✨ drag and drop
This commit is contained in:
parent
a813a4e57e
commit
b6565833b1
@ -0,0 +1,39 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
max-width: 357px;
|
||||
width: 100%;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
align-items: center;
|
||||
|
||||
.file-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 277px;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
min-width: var(--spacing-3, 24px);
|
||||
min-height: var(--spacing-3, 24px);
|
||||
width: var(--spacing-3, 24px);
|
||||
height: var(--spacing-3, 24px);
|
||||
stroke: var(--color-primary-500);
|
||||
}
|
||||
|
||||
.error{
|
||||
min-width: var(--spacing-3, 24px);
|
||||
min-height: var(--spacing-3, 24px);
|
||||
width: var(--spacing-3, 24px);
|
||||
height: var(--spacing-3, 24px);
|
||||
stroke: var(--color-error-500);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import { CheckCircleIcon, XCircleIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import React from "react";
|
||||
|
||||
import IconButton, { EIconButtonVariant } from "../../IconButton";
|
||||
import Loader from "../../Loader";
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
type IProps = {
|
||||
isLoading: boolean;
|
||||
file: File | null;
|
||||
onRemove: () => void;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export default function DocumentElement(props: IProps) {
|
||||
const { isLoading, onRemove, file, error } = props;
|
||||
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<div className={classes["content"]}>
|
||||
{isLoading ? <Loader /> : !error ? <CheckCircleIcon /> : <XCircleIcon className={classes["error"]} />}
|
||||
{error && (
|
||||
<Typography typo={ETypo.TEXT_MD_LIGHT} color={ETypoColor.COLOR_ERROR_500} className={classes["file-name"]}>
|
||||
{error}
|
||||
</Typography>
|
||||
)}
|
||||
{file && !error && (
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.TEXT_SECONDARY} className={classes["file-name"]}>
|
||||
{file.name}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
<IconButton onClick={onRemove} icon={<XMarkIcon />} variant={error ? EIconButtonVariant.ERROR : EIconButtonVariant.NEUTRAL} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -2,22 +2,57 @@
|
||||
|
||||
.root {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-3, 24px);
|
||||
|
||||
width: fit-content;
|
||||
padding: var(--spacing-2, 16px) var(--Radius-2xl, 32px) var(--spacing-2, 16px) var(--spacing-xl, 32px);
|
||||
align-items: center;
|
||||
gap: var(--spacing-4, 32px);
|
||||
|
||||
border-radius: var(--Radius-md, 8px);
|
||||
border: 1px dashed var(--dropdown-input-border-hovered, #b4bec5);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--dropdown-input-border-expanded);
|
||||
background: var(--primary-weak-higlight, #e5eefa);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4, 32px);
|
||||
|
||||
.deposit-document {
|
||||
.browse-document-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
|
||||
.browse-document {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
|
||||
&.desktop {
|
||||
@media screen and (max-width: $screen-s) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.mobile {
|
||||
display: none;
|
||||
@media screen and (max-width: $screen-s) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg{
|
||||
.documents {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
}
|
||||
|
||||
svg {
|
||||
min-width: var(--spacing-3, 24px);
|
||||
min-height: var(--spacing-3, 24px);
|
||||
width: var(--spacing-3, 24px);
|
||||
|
@ -1,35 +1,161 @@
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import React from "react";
|
||||
import { DocumentPlusIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import classNames from "classnames";
|
||||
import Separator, { ESeperatorColor, ESeperatorDirection } from "../Separator";
|
||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "../Button";
|
||||
import React, { useCallback, useRef, useState } from "react";
|
||||
|
||||
type IProps = {};
|
||||
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "../Button";
|
||||
import Separator, { ESeperatorColor, ESeperatorDirection } from "../Separator";
|
||||
import classes from "./classes.module.scss";
|
||||
import DocumentElement from "./DocumentElement";
|
||||
|
||||
type IProps = {
|
||||
name?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
type IMimeTypes = {
|
||||
extension: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
const mimeTypesAccepted: { [key: string]: IMimeTypes } = {
|
||||
"application/pdf": {
|
||||
extension: "pdf",
|
||||
size: 41943040,
|
||||
},
|
||||
"image/jpeg": {
|
||||
extension: "jpeg",
|
||||
size: 41943040,
|
||||
},
|
||||
"image/png": {
|
||||
extension: "png",
|
||||
size: 41943040,
|
||||
},
|
||||
"image/jpg": {
|
||||
extension: "jpg",
|
||||
size: 41943040,
|
||||
},
|
||||
};
|
||||
|
||||
type IDocument = {
|
||||
id: string;
|
||||
file: File | null;
|
||||
isLoading: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export default function DragAndDrop(props: IProps) {
|
||||
const {} = props;
|
||||
const { name, title, description } = props;
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [documents, setDocuments] = useState<IDocument[]>([]);
|
||||
|
||||
const handleFiles = useCallback((files: File[]) => {
|
||||
files.forEach((file) => {
|
||||
setDocuments((prevDocs) => [...prevDocs, { id: file.name, file: file, isLoading: true }]);
|
||||
setTimeout(() => {
|
||||
if (mimeTypesAccepted[file.type]) {
|
||||
const newDoc: IDocument = { id: file.name, file, isLoading: false };
|
||||
setDocuments((prevDocs) => prevDocs.map((doc) => (doc.id === newDoc.id ? newDoc : doc)));
|
||||
} else {
|
||||
const errorDoc: IDocument = { id: file.name, file: null, isLoading: false, error: "Type de fichier non accepté" };
|
||||
setDocuments((prevDocs) => prevDocs.map((doc) => (doc.id === errorDoc.id ? errorDoc : doc)));
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const files = Array.from(event.dataTransfer.files);
|
||||
handleFiles(files);
|
||||
},
|
||||
[handleFiles],
|
||||
);
|
||||
|
||||
const handleRemove = useCallback((id: string) => {
|
||||
setDocuments((prevDocs) => prevDocs.filter((doc) => doc.id !== id));
|
||||
}, []);
|
||||
|
||||
const handleBrowse = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = Array.from(event.target.files || []);
|
||||
handleFiles(files);
|
||||
},
|
||||
[handleFiles],
|
||||
);
|
||||
|
||||
const triggerFileInput = () => {
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.click();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(classes["root"])}>
|
||||
<DocumentPlusIcon />
|
||||
<Separator direction={ESeperatorDirection.VERTICAL} color={ESeperatorColor.STRONG} size={64} />
|
||||
<div className={classNames(classes["root"])} onDrop={handleDrop} onDragOver={(e) => e.preventDefault()}>
|
||||
<div className={classes["content"]}>
|
||||
<div className={classes["deposit-document"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_SEMIBOLD} color={ETypoColor.TEXT_PRIMARY}>
|
||||
Drag and Drop ou
|
||||
</Typography>
|
||||
<Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.TEXT} size={EButtonSize.SM}>
|
||||
parcourir
|
||||
</Button>
|
||||
</div>
|
||||
<DocumentPlusIcon />
|
||||
<Separator direction={ESeperatorDirection.VERTICAL} color={ESeperatorColor.STRONG} size={64} />
|
||||
<div className={classes["browse-document-container"]}>
|
||||
<div className={classNames(classes["browse-document"], classes["desktop"])}>
|
||||
<Typography typo={ETypo.TEXT_LG_SEMIBOLD} color={ETypoColor.TEXT_PRIMARY}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Button
|
||||
variant={EButtonVariant.PRIMARY}
|
||||
styletype={EButtonstyletype.TEXT}
|
||||
size={EButtonSize.SM}
|
||||
onClick={triggerFileInput}>
|
||||
{inputFile()}
|
||||
parcourir
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Typography typo={ETypo.TEXT_MD_LIGHT} color={ETypoColor.TEXT_SECONDARY}>
|
||||
Description
|
||||
</Typography>
|
||||
<div className={classNames(classes["browse-document"], classes["mobile"])}>
|
||||
<Button
|
||||
variant={EButtonVariant.PRIMARY}
|
||||
styletype={EButtonstyletype.TEXT}
|
||||
size={EButtonSize.SM}
|
||||
onClick={triggerFileInput}>
|
||||
{inputFile()}
|
||||
Ajouter un document
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Typography typo={ETypo.TEXT_MD_LIGHT} color={ETypoColor.TEXT_SECONDARY}>
|
||||
{description}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
{documents.length > 0 && (
|
||||
<div className={classes["documents"]}>
|
||||
{documents.map((doc) => (
|
||||
<DocumentElement
|
||||
key={doc.id}
|
||||
isLoading={doc.isLoading}
|
||||
file={doc.file}
|
||||
onRemove={() => handleRemove(doc.id)}
|
||||
error={doc.error}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
function inputFile() {
|
||||
return (
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
name={name}
|
||||
type="file"
|
||||
multiple
|
||||
accept={Object.keys(mimeTypesAccepted).join(",")}
|
||||
onChange={handleBrowse}
|
||||
hidden
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ export default function DesignSystem() {
|
||||
<Typography typo={ETypo.DISPLAY_LARGE}>DesignSystem</Typography>
|
||||
<div className={classes["components"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Drag and Drop</Typography>
|
||||
<DragAndDrop />
|
||||
<DragAndDrop name="test" title="Upload de document" description="Description" />
|
||||
<Typography typo={ETypo.TEXT_LG_BOLD}>Separators</Typography>
|
||||
<div className={classes["rows"]}>
|
||||
<Separator color={ESeperatorColor.DEFAULT} direction={ESeperatorDirection.HORIZONTAL} />
|
||||
|
Loading…
x
Reference in New Issue
Block a user