Skip to content

Commit b0db1f8

Browse files
authored
Merge pull request #62 from Inist-CNRS/Form-Fix
Form fix
2 parents 8cf5f4c + 3d952ef commit b0db1f8

File tree

4 files changed

+85
-26
lines changed

4 files changed

+85
-26
lines changed

tdm-fe/src/app/components/form/ProcessingFormEmail.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type ProcessingFormEmailProps = {
1515
const ProcessingFormEmail = ({ value, onChange }: ProcessingFormEmailProps) => {
1616
const [email, setEmail] = useState<string>(value ?? '');
1717
const [isInvalid, setIsInvalid] = useState(false);
18+
const [hasAttemptedInput, setHasAttemptedInput] = useState(false);
1819

1920
useEffect(() => {
2021
let invalid = false;
@@ -24,26 +25,25 @@ const ProcessingFormEmail = ({ value, onChange }: ProcessingFormEmailProps) => {
2425
}
2526

2627
setIsInvalid(invalid);
27-
if (!invalid) {
28-
onChange(email);
29-
}
28+
onChange(invalid ? null : email);
3029
}, [email, onChange]);
3130

3231
const handleEmailChange = useCallback((event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
3332
setEmail(event.target.value);
33+
setHasAttemptedInput(true);
3434
}, []);
3535

3636
return (
3737
<div className="processing-form-field-group processing-form-field-with-label">
3838
<TextField
3939
value={email}
4040
onChange={handleEmailChange}
41-
error={isInvalid}
41+
error={isInvalid ? hasAttemptedInput : false}
4242
className="processing-form-field"
4343
label="Adresse électronique"
4444
fullWidth
4545
/>
46-
{isInvalid ? (
46+
{isInvalid && hasAttemptedInput ? (
4747
<div className="text processing-form-field-label error">
4848
L&apos;adresse électronique n&apos;est pas valide
4949
</div>

tdm-fe/src/app/components/form/ProcessingFormUpload.tsx

+53-18
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,92 @@ import mimeTypes from 'mime';
77
import { MuiFileInput } from 'mui-file-input';
88
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
99

10+
import type React from 'react';
11+
1012
type ProcessingFormUploadProps = {
1113
mimes: string[];
1214
value: File | null;
1315
isOnError: boolean;
1416
isPending: boolean;
15-
onChange: (value: File | null) => void;
17+
onChange: (value: File | null, isValid: boolean) => void;
1618
};
1719

1820
const ProcessingFormUpload = ({ mimes, value, isOnError, isPending, onChange }: ProcessingFormUploadProps) => {
1921
const [file, setFile] = useState<File | null>(value);
2022
const [isInvalid, setIsInvalid] = useState(false);
23+
const [isDragging, setIsDragging] = useState(false);
24+
const [hasAttemptedUpload, setHasAttemptedUpload] = useState(false);
2125

2226
const stringifiesMineTypes = useMemo(() => {
2327
return mimes.join(', ');
2428
}, [mimes]);
2529

30+
/**
31+
* Check file validity and update parent component
32+
*/
2633
useEffect(() => {
2734
let invalid = false;
2835

29-
if (!file || !mimes.includes(mimeTypes.getType(file.name) ?? '')) {
30-
invalid = true;
36+
if (file) {
37+
if (!mimes.includes(mimeTypes.getType(file.name) ?? '')) {
38+
invalid = true;
39+
}
40+
setHasAttemptedUpload(true);
3141
}
3242

3343
setIsInvalid(invalid);
34-
if (!invalid) {
35-
onChange(file);
36-
}
44+
onChange(file, file !== null && !invalid);
3745
}, [file, mimes, onChange]);
3846

3947
const handleFileChange = useCallback((newFile: File | null) => {
4048
setFile(newFile);
49+
if (newFile === null) {
50+
setHasAttemptedUpload(false);
51+
}
52+
}, []);
53+
54+
const handleDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {
55+
e.preventDefault();
56+
e.stopPropagation();
57+
setIsDragging(true);
4158
}, []);
4259

60+
const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
61+
e.preventDefault();
62+
e.stopPropagation();
63+
setIsDragging(false);
64+
}, []);
65+
66+
const handleDrop = useCallback(
67+
(e: React.DragEvent<HTMLDivElement>) => {
68+
e.preventDefault();
69+
e.stopPropagation();
70+
setIsDragging(false);
71+
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
72+
handleFileChange(e.dataTransfer.files[0]);
73+
}
74+
},
75+
[handleFileChange],
76+
);
77+
4378
if (isPending || isOnError) {
4479
return <FileUpload showError={isOnError} />;
4580
}
4681

4782
return (
48-
<div className="processing-form-field-group processing-form-field-with-label">
83+
<div
84+
className={`processing-form-field-group processing-form-field-with-label ${isDragging ? 'dragging' : ''}`}
85+
onDragEnter={handleDragEnter}
86+
onDragOver={handleDragEnter}
87+
onDragLeave={handleDragLeave}
88+
onDrop={handleDrop}
89+
>
4990
<MuiFileInput
5091
className="processing-form-field"
51-
placeholder="Déposer un fichier"
92+
placeholder="Déposer un fichier ou glisser-déposer ici"
5293
value={file}
5394
onChange={handleFileChange}
54-
error={isInvalid}
95+
error={isInvalid ? hasAttemptedUpload : false}
5596
fullWidth
5697
clearIconButtonProps={{
5798
children: <CloseIcon fontSize="small" />,
@@ -68,16 +109,10 @@ const ProcessingFormUpload = ({ mimes, value, isOnError, isPending, onChange }:
68109
},
69110
}}
70111
/>
71-
{isInvalid ? (
112+
{isInvalid && hasAttemptedUpload ? (
72113
<div className="text processing-form-field-label error">
73-
{!file ? (
74-
<>Fichier manquant</>
75-
) : (
76-
<>
77-
Le fichier ne correspond pas à un format compatible, utilisez l&apos;un de ces formats :{' '}
78-
{stringifiesMineTypes}.
79-
</>
80-
)}
114+
Le fichier ne correspond pas à un format compatible, utilisez l&apos;un de ces formats :{' '}
115+
{stringifiesMineTypes}.
81116
</div>
82117
) : null}
83118
</div>

tdm-fe/src/app/components/form/scss/ProcessingFormConfiguration.scss

+23
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
.processing-form-field-group {
44
margin-bottom: 16px !important;
5+
border: 2px dashed transparent;
6+
border-radius: 4px;
7+
transition: all .3s ease;
8+
9+
&.dragging {
10+
background-color: rgba($blue, .05);
11+
border-color: $blue;
12+
box-shadow: 0 0 10px rgba($blue, .2);
13+
transform: scale(1.01);
14+
animation: pulse 1.2s infinite;
15+
}
16+
}
17+
18+
@keyframes pulse {
19+
0% {
20+
box-shadow: 0 0 0 0 rgba($blue, .4);
21+
}
22+
70% {
23+
box-shadow: 0 0 0 10px rgba($blue, 0);
24+
}
25+
100% {
26+
box-shadow: 0 0 0 0 rgba($blue, 0);
27+
}
528
}
629

730
#processing-form-wrapper-param,

tdm-fe/src/app/pages/ProcessingCreationForm.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,9 @@ const ProcessingCreationForm = () => {
270270
* Handle file change
271271
* @param value newly add file
272272
*/
273-
const handleUploadChange = useCallback((value: File | null) => {
273+
const handleUploadChange = useCallback((value: File | null, isValid: boolean) => {
274274
setFile(value);
275-
setIsWaitingInput(false);
275+
setIsWaitingInput(!isValid);
276276
}, []);
277277

278278
/**
@@ -292,7 +292,8 @@ const ProcessingCreationForm = () => {
292292
*/
293293
const handleEmailChange = useCallback((value: string | null) => {
294294
setEmail(value);
295-
setIsWaitingInput(false);
295+
setIsWaitingInput(!value || !EMAIL_REGEX.test(value));
296+
setIsInvalid(!value || !EMAIL_REGEX.test(value));
296297
}, []);
297298

298299
return (

0 commit comments

Comments
 (0)