diff --git a/src/front/Components/DesignSystem/DragAndDrop/DocumentElement/classes.module.scss b/src/front/Components/DesignSystem/DragAndDrop/DocumentElement/classes.module.scss new file mode 100644 index 00000000..cdba1ed0 --- /dev/null +++ b/src/front/Components/DesignSystem/DragAndDrop/DocumentElement/classes.module.scss @@ -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); + } +} \ No newline at end of file diff --git a/src/front/Components/DesignSystem/DragAndDrop/DocumentElement/index.tsx b/src/front/Components/DesignSystem/DragAndDrop/DocumentElement/index.tsx new file mode 100644 index 00000000..efb8aa20 --- /dev/null +++ b/src/front/Components/DesignSystem/DragAndDrop/DocumentElement/index.tsx @@ -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 ( +
+
+ {isLoading ? : !error ? : } + {error && ( + + {error} + + )} + {file && !error && ( + + {file.name} + + )} +
+ } variant={error ? EIconButtonVariant.ERROR : EIconButtonVariant.NEUTRAL} /> +
+ ); +} diff --git a/src/front/Components/DesignSystem/DragAndDrop/classes.module.scss b/src/front/Components/DesignSystem/DragAndDrop/classes.module.scss new file mode 100644 index 00000000..0c3fae28 --- /dev/null +++ b/src/front/Components/DesignSystem/DragAndDrop/classes.module.scss @@ -0,0 +1,62 @@ +@import "@Themes/constants.scss"; + +.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); + + 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: inline-flex; + align-items: center; + gap: var(--spacing-4, 32px); + + .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; + } + } + } + } + } + + .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); + height: var(--spacing-3, 24px); + stroke: var(--color-primary-500); + } +} diff --git a/src/front/Components/DesignSystem/DragAndDrop/index.tsx b/src/front/Components/DesignSystem/DragAndDrop/index.tsx new file mode 100644 index 00000000..16ce9619 --- /dev/null +++ b/src/front/Components/DesignSystem/DragAndDrop/index.tsx @@ -0,0 +1,161 @@ +import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography"; +import { DocumentPlusIcon } from "@heroicons/react/24/outline"; +import classNames from "classnames"; +import React, { useCallback, useRef, useState } from "react"; + +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 { name, title, description } = props; + const fileInputRef = useRef(null); + + const [documents, setDocuments] = useState([]); + + 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) => { + 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) => { + const files = Array.from(event.target.files || []); + handleFiles(files); + }, + [handleFiles], + ); + + const triggerFileInput = () => { + if (fileInputRef.current) { + fileInputRef.current.click(); + } + }; + + return ( +
e.preventDefault()}> +
+ + +
+
+ + {title} + + +
+ +
+ +
+ + + {description} + +
+
+ {documents.length > 0 && ( +
+ {documents.map((doc) => ( + handleRemove(doc.id)} + error={doc.error} + /> + ))} +
+ )} +
+ ); + + function inputFile() { + return ( + + ); + } +} diff --git a/src/front/Components/DesignSystem/Separator/classes.module.scss b/src/front/Components/DesignSystem/Separator/classes.module.scss new file mode 100644 index 00000000..51b570fd --- /dev/null +++ b/src/front/Components/DesignSystem/Separator/classes.module.scss @@ -0,0 +1,24 @@ +@import "@Themes/constants.scss"; + +.root { + height: 2px; + width: 100%; + background-color: var(--separator-stroke-default); + + &.vertical { + width: 2px; + height: 100%; + } + + &.light { + background-color: var(--separator-stroke-light); + } + + &.strong { + background-color: var(--separator-stroke-strong); + } + + &.contrast { + background-color: var(--separator-stroke-contrast); + } +} diff --git a/src/front/Components/DesignSystem/Separator/index.tsx b/src/front/Components/DesignSystem/Separator/index.tsx new file mode 100644 index 00000000..21ccbc1e --- /dev/null +++ b/src/front/Components/DesignSystem/Separator/index.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +import classes from "./classes.module.scss"; +import classNames from "classnames"; + +export enum ESeperatorColor { + LIGHT = "light", + DEFAULT = "default", + STRONG = "strong", + CONTRAST = "contrast", +} + +export enum ESeperatorDirection { + HORIZONTAL = "horizontal", + VERTICAL = "vertical", +} + +type IProps = { + color?: ESeperatorColor; + direction?: ESeperatorDirection; + size?: number; +}; + +export default function Separator(props: IProps) { + const { color = ESeperatorColor.DEFAULT, direction = ESeperatorDirection.HORIZONTAL, size } = props; + + return ( +
+ ); +} diff --git a/src/front/Components/DesignSystem/Typography/index.tsx b/src/front/Components/DesignSystem/Typography/index.tsx index 584397a6..248cad65 100644 --- a/src/front/Components/DesignSystem/Typography/index.tsx +++ b/src/front/Components/DesignSystem/Typography/index.tsx @@ -149,6 +149,8 @@ export enum ETypoColor { INPUT_ERROR = "--input-error", TEXT_ACCENT = "--text-accent", + TEXT_PRIMARY = "--text-primary", + TEXT_SECONDARY = "--text-secondary", CONTRAST_DEFAULT = "--contrast-default", CONTRAST_HOVERED = "--contrast-hovered", diff --git a/src/front/Components/Layouts/DesignSystem/classes.module.scss b/src/front/Components/Layouts/DesignSystem/classes.module.scss index c0f72ca1..22111a86 100644 --- a/src/front/Components/Layouts/DesignSystem/classes.module.scss +++ b/src/front/Components/Layouts/DesignSystem/classes.module.scss @@ -3,15 +3,16 @@ flex-direction: column; gap: 32px; .components { + display: flex; + flex-direction: column; + gap: 32px; max-width: 600px; + .inputs { display: flex; flex-direction: column; gap: 24px; } - display: flex; - flex-direction: column; - gap: 24px; .rows { display: flex; diff --git a/src/front/Components/Layouts/DesignSystem/index.tsx b/src/front/Components/Layouts/DesignSystem/index.tsx index 0eab5b90..187dc05f 100644 --- a/src/front/Components/Layouts/DesignSystem/index.tsx +++ b/src/front/Components/Layouts/DesignSystem/index.tsx @@ -36,6 +36,8 @@ import classes from "./classes.module.scss"; import CheckboxesInputElement from "@Front/Components/DesignSystem/CheckBox"; import RadioBox from "@Front/Components/DesignSystem/RadioBox"; import Toggle, { EToggleSize } from "@Front/Components/DesignSystem/Toggle"; +import Separator, { ESeperatorColor, ESeperatorDirection } from "@Front/Components/DesignSystem/Separator"; +import DragAndDrop from "@Front/Components/DesignSystem/DragAndDrop"; export default function DesignSystem() { const { isOpen, open, close } = useOpenable(); @@ -83,78 +85,95 @@ export default function DesignSystem() { return ( - DesignSystem
-
-
- - - - -
- -
- - - - -
+ DesignSystem
+ Drag and Drop + + Separators +
+ + + + +
+
+ + + + +
+ + Checkboxes +
+ + + + +
+ Radio boxes +
+ + + + +
+ Toggle