@groupeactual/ui-kit 1.7.0-beta.3 → 1.7.0-beta.5

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.
@@ -1,634 +0,0 @@
1
- import React, {
2
- ChangeEvent,
3
- useCallback,
4
- useEffect,
5
- useMemo,
6
- useRef,
7
- useState,
8
- } from 'react';
9
-
10
- import { faGoogleDrive } from '@fortawesome/free-brands-svg-icons';
11
- import {
12
- faEye,
13
- faFolderOpen,
14
- faTrash,
15
- faUpload,
16
- } from '@fortawesome/pro-regular-svg-icons';
17
- import {
18
- faCaretDown,
19
- faFileAlt,
20
- faInfoCircle,
21
- } from '@fortawesome/pro-solid-svg-icons';
22
- import { Box, IconButton } from '@mui/material';
23
- import Menu from '@mui/material/Menu';
24
-
25
- import Button from '@/components/Button';
26
- import IconProvider from '@/components/IconProvider';
27
- import MenuItem from '@/components/MenuItem';
28
- import Text from '@/components/Text';
29
- import Tooltip from '@/components/Tooltip';
30
- import GooglePickerWrapper from '@/helpers/GooglePickerWrapper';
31
-
32
- import { acceptText } from './fileuploadersingle.interface';
33
-
34
- export interface Props {
35
- setFile: (_file: File | null) => void;
36
- title?: string;
37
- titleTooltip?: string;
38
- enableGoogleDrive?: boolean;
39
- googleAuthClientId?: string;
40
- googleApiKey?: string;
41
- error?: boolean;
42
- helperText?: string;
43
- fileUrl?: string | null;
44
- fileName?: string | null;
45
- accept?: string[];
46
- acceptText?: acceptText;
47
- disabledInput?: boolean;
48
- children?: JSX.Element | null;
49
- titleAddButton?: string;
50
- _isDroppingFile?: boolean; // * Only used for storybook
51
- validateFile?: (_size: number, _type: string) => boolean;
52
- removeExistingFile?: () => void;
53
- onTouched?: () => void;
54
- onFileDataChange?: (
55
- _fileData: {
56
- name: string;
57
- size: number;
58
- type?: string;
59
- driveFileId?: string;
60
- driveAccessToken?: string;
61
- } | null,
62
- ) => void;
63
- }
64
-
65
- /**
66
- * A single file uploader component that supports file selection from local storage or Google Drive.
67
- *
68
- * @param {string[]} accept - Array of accepted file types, will display acceptText message
69
- * but not verify size or type of your file.
70
- *
71
- * Example:
72
- * ```js
73
- * accept={[
74
- 'application/pdf',
75
- 'image/png',
76
- 'image/jpeg',
77
- 'image/jpg',
78
- ]}
79
- * ```
80
- * @param {Object} acceptText - Object containing text for accepted file format, max size, and additional subtext.
81
- *
82
- * Example:
83
- * ```js
84
- * acceptText={{
85
- maxSize: '10 Mo',
86
- subText: 'Sélectionnez un fichier',
87
- }}
88
- * ```
89
- *
90
- * @param {Function} validateFile - Function to validate the file size and type.
91
- *
92
- * Example:
93
- * ```js
94
- * validateFile={(size, type) => {
95
- * if (size > 10 * 1024 * 1024) {
96
- * setCustomFileError('Le fichier doit être inférieur à 10 Mo');
97
- * return true;
98
- * }
99
- * if (!['application/pdf', 'image/png', 'image/jpeg', 'image/jpg'].includes(type)) {
100
- * setCustomFileError('Le fichier doit être au format PDF, PNG, JPEG ou JPG');
101
- * return true;
102
- * }
103
- * return false;
104
- * }}
105
- * ```
106
- *
107
- * @param {Function} onFileDataChange - Callback function to handle file data changes and give you all informations you need
108
- * to handle google drive file.
109
- */
110
-
111
- const FileUploaderSingle = ({
112
- enableGoogleDrive = false,
113
- googleAuthClientId,
114
- googleApiKey,
115
- error = false,
116
- helperText = '',
117
- accept = [],
118
- acceptText = { fileFormat: '', maxSize: '', subText: '' },
119
- title = '',
120
- titleTooltip,
121
- disabledInput = false,
122
- children = null,
123
- fileUrl = null,
124
- fileName = null,
125
- titleAddButton,
126
- _isDroppingFile = false,
127
- validateFile,
128
- setFile,
129
- removeExistingFile,
130
- onTouched,
131
- onFileDataChange,
132
- }: Props) => {
133
- const DEFAULT_COLOR = '%23CBCBCB';
134
- const ERROR_COLOR = '%23b80025';
135
- const DROPPING_COLOR = '%23004F88';
136
-
137
- const [currentFile, setCurrentFile] = useState<File | null>(null);
138
- const [fileData, setFileData] = useState<{
139
- name: string;
140
- size: number;
141
- type?: string;
142
- driveFileId?: string;
143
- driveAccessToken?: string;
144
- } | null>(fileName ? { name: fileName, size: 0 } : null);
145
-
146
- const [isDroppingFile, setIsDroppingFile] =
147
- useState<boolean>(_isDroppingFile);
148
- const fileInputRef = useRef<HTMLInputElement | null>(null);
149
-
150
- const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
151
- const handleClick = (event: React.MouseEvent<HTMLElement>) => {
152
- setAnchorEl(event.currentTarget);
153
- };
154
- const handleClose = () => {
155
- setAnchorEl(null);
156
- };
157
-
158
- const handleFileChange = useCallback(
159
- (
160
- _event: ChangeEvent<HTMLInputElement> | null,
161
- data?: {
162
- docs: {
163
- name: string;
164
- sizeBytes: number;
165
- mimeType: string;
166
- id: string;
167
- }[];
168
- } | null,
169
- token: string | null = null,
170
- fakeClick?: boolean,
171
- ) => {
172
- if (fakeClick) {
173
- fileInputRef?.current?.click();
174
- }
175
- handleClose();
176
- onTouched?.();
177
-
178
- if (
179
- fileInputRef.current?.files &&
180
- fileInputRef.current.files.length > 0
181
- ) {
182
- const file = fileInputRef.current.files[0];
183
- setCurrentFile(file);
184
- if (validateFile && validateFile(file.size, file.type)) {
185
- setIsDroppingFile(false);
186
- return;
187
- }
188
- onFileDataChange?.({
189
- name: file.name,
190
- size: Math.round(file.size / 1024),
191
- type: file.type,
192
- });
193
- setFileData({
194
- name: file.name,
195
- size: Math.round(file.size / 1024),
196
- type: file.type,
197
- });
198
- setFile(file);
199
- setIsDroppingFile(false);
200
- }
201
-
202
- if (token && data && data.docs && data.docs.length > 0) {
203
- const googleFile = data.docs[0];
204
- const fileData = {
205
- name: googleFile.name,
206
- size: Math.round(googleFile.sizeBytes / 1024),
207
- type: googleFile.mimeType,
208
- driveFileId: googleFile.id,
209
- driveAccessToken: token,
210
- };
211
- if (
212
- validateFile &&
213
- validateFile(googleFile.sizeBytes, googleFile.mimeType)
214
- ) {
215
- return;
216
- }
217
- onFileDataChange?.(fileData);
218
- setIsDroppingFile(false);
219
- setFileData(fileData);
220
- }
221
- },
222
- [
223
- setIsDroppingFile,
224
- setCurrentFile,
225
- setFileData,
226
- setFile,
227
- accept,
228
- onTouched,
229
- ],
230
- );
231
- /**
232
- * Resets all states.
233
- * Also calls the `removeExistingFile` prop if provided do to some logics if needed.
234
- */
235
- const handleDelete = useCallback(() => {
236
- if (fileName) {
237
- removeExistingFile?.();
238
- }
239
- if (currentFile) {
240
- URL.revokeObjectURL(URL.createObjectURL(currentFile));
241
- setCurrentFile(null);
242
- }
243
- setFileData(null);
244
- setIsDroppingFile(false);
245
- setFile(null);
246
- if (fileInputRef.current) {
247
- fileInputRef.current.value = '';
248
- }
249
- onFileDataChange?.(null);
250
- }, [removeExistingFile, setFile, onFileDataChange, currentFile]);
251
-
252
- const dashedColor = useMemo(() => {
253
- if (isDroppingFile) return DROPPING_COLOR;
254
- if (error) return ERROR_COLOR;
255
- return DEFAULT_COLOR;
256
- }, [isDroppingFile, error]);
257
-
258
- const bgColor = useMemo(() => {
259
- if (fileData?.name || disabledInput) return 'greyXLight';
260
- if (isDroppingFile) return 'blueHoverEquivalence';
261
- return 'white';
262
- }, [fileData?.name, disabledInput, isDroppingFile]);
263
-
264
- const titleColor = useMemo(() => {
265
- if (fileData?.name) return 'greyDark';
266
- if (error) return 'redError';
267
- return 'greyXDark';
268
- }, [fileData?.name, error]);
269
-
270
- const inputCss = useMemo(
271
- () => ({
272
- height: '87px',
273
- backgroundImage: `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='4' ry='4' stroke='${dashedColor}' stroke-width='2' stroke-dasharray='8%2c 8' stroke-dashoffset='0' stroke-linecap='round'/%3e%3c/svg%3e")`,
274
- borderRadius: '4px',
275
- py: '24px',
276
- px: '22px',
277
- display: 'flex',
278
- justifyContent: 'space-between',
279
- position: 'relative',
280
- alignItems: 'center',
281
- backgroundColor: bgColor,
282
- }),
283
- [dashedColor, bgColor],
284
- );
285
-
286
- const extractExtensions = (types: string[]): string[] => {
287
- return types.map((type) => {
288
- const extension = type.split('/')[1].toUpperCase();
289
- return extension;
290
- });
291
- };
292
-
293
- useEffect(() => {
294
- if (!fileName) {
295
- return;
296
- }
297
- setFileData({ name: fileName, size: 0 });
298
- }, [fileName]);
299
-
300
- useEffect(() => {
301
- return () => {
302
- if (currentFile) {
303
- const fileURL = URL.createObjectURL(currentFile);
304
- URL.revokeObjectURL(fileURL);
305
- }
306
- };
307
- }, [currentFile]);
308
-
309
- return (
310
- <Box
311
- data-testid="Uploader-document"
312
- sx={{
313
- pb: 3,
314
- }}
315
- onDragLeave={() => setIsDroppingFile(false)}
316
- >
317
- <Box sx={{ display: 'flex' }}>
318
- <Text variant="body2Medium" color={titleColor} pl={1}>
319
- {title}
320
- </Text>
321
- {titleTooltip && (
322
- <Box sx={{ marginLeft: '4px' }}>
323
- <Tooltip title={titleTooltip}>
324
- <IconProvider icon={faInfoCircle} size="sm" color="blueInfo" />
325
- </Tooltip>
326
- </Box>
327
- )}
328
- </Box>
329
- <Tooltip
330
- data-testid="tooltip"
331
- title={(fileData?.name && 'Un seul fichier autorisé') || ''}
332
- sx={{
333
- display: (fileData?.name && 'block') || 'none',
334
- }}
335
- placement="top"
336
- >
337
- <Box
338
- mt={1}
339
- p={2}
340
- sx={inputCss}
341
- onDragOver={(e) => {
342
- e.preventDefault();
343
- setIsDroppingFile(true);
344
- }}
345
- >
346
- <Box
347
- sx={{
348
- opacity: 0,
349
- position: 'absolute',
350
- top: '0px',
351
- width: '100%',
352
- height: '100%',
353
- '& input': {
354
- width: '100%',
355
- height: '100%',
356
- },
357
- }}
358
- >
359
- <input
360
- data-testid="document-input"
361
- disabled={!!fileData?.name || disabledInput}
362
- type="file"
363
- ref={fileInputRef}
364
- onChange={handleFileChange}
365
- onBlur={onTouched}
366
- accept={accept.join(', ')}
367
- />
368
- </Box>
369
- <Box sx={{ display: 'flex', flexDirection: 'column' }}>
370
- <Text
371
- variant="body2Medium"
372
- color={(fileData?.name && 'greyDark') || 'greyXDark'}
373
- >
374
- Déposer un fichier ici
375
- </Text>
376
- <Box sx={{ maxWidth: '240px' }}>
377
- <Text
378
- variant="caption"
379
- sx={{
380
- color: 'greyDark',
381
- marginTop: '2px',
382
- }}
383
- >
384
- Format{' '}
385
- {(acceptText?.fileFormat &&
386
- acceptText?.fileFormat?.trim() !== ''
387
- ? acceptText.fileFormat
388
- : extractExtensions(accept).join(', ')) + ' - '}
389
- {(acceptText.maxSize && acceptText.maxSize) || 'Max 10 Mo'}
390
- {acceptText.subText && (
391
- <>
392
- <br />
393
- {acceptText.subText}
394
- </>
395
- )}
396
- </Text>
397
- </Box>
398
- </Box>
399
- <Box>
400
- <Text
401
- variant="body2Medium"
402
- color={(fileData?.name && 'greyDark') || 'greyXDark'}
403
- >
404
- ou
405
- </Text>
406
- </Box>
407
- <Box>
408
- {(enableGoogleDrive && (
409
- <>
410
- <Button
411
- variant="secondary"
412
- sx={{
413
- display: 'flex',
414
- '&.MuiButton-secondaryPrimary.Mui-disabled': {
415
- backgroundColor: '#FFFFFF !important',
416
- },
417
- }}
418
- onClick={handleClick}
419
- disabled={!!fileData?.name}
420
- endIcon={<IconProvider icon={faCaretDown} />}
421
- >
422
- <IconProvider icon={faUpload} size="md" mr={1} />
423
- {titleAddButton || ''}
424
- </Button>
425
- <Menu
426
- data-testid="seizure-card-menu"
427
- anchorEl={anchorEl}
428
- open={Boolean(anchorEl)}
429
- onClose={handleClose}
430
- anchorOrigin={{
431
- vertical: 'bottom',
432
- horizontal: 'right',
433
- }}
434
- transformOrigin={{
435
- vertical: 'top',
436
- horizontal: 'right',
437
- }}
438
- slotProps={{
439
- paper: {
440
- style: {
441
- marginTop: '8px',
442
- marginLeft: '0',
443
- },
444
- },
445
- }}
446
- >
447
- <MenuItem
448
- testId="pc-add"
449
- onClick={() => handleFileChange(null, null, null, true)}
450
- >
451
- <Box gap={1} display="flex">
452
- <IconProvider size="sm" icon={faFolderOpen} />
453
- <Text variant="body2">Depuis votre PC</Text>
454
- </Box>
455
- </MenuItem>
456
- <GooglePickerWrapper
457
- callback={(data, token) =>
458
- handleFileChange(null, data, token)
459
- }
460
- multiselect={true}
461
- navHidden={false}
462
- googleAuthClientId={googleAuthClientId ?? ''}
463
- googleApiKey={googleApiKey ?? ''}
464
- scopes="https://www.googleapis.com/auth/drive.file"
465
- viewId="FOLDERS"
466
- >
467
- <MenuItem testId="drive-add" onClick={handleClose}>
468
- <Box gap={1} display="flex">
469
- <IconProvider size="sm" icon={faGoogleDrive} />
470
- <Text variant="body2">Depuis Google Drive</Text>
471
- </Box>
472
- </MenuItem>
473
- </GooglePickerWrapper>
474
- </Menu>
475
- </>
476
- )) || (
477
- <Button
478
- variant="secondary"
479
- sx={{
480
- display: 'flex',
481
- '&.MuiButton-secondaryPrimary.Mui-disabled': {
482
- backgroundColor: '#FFFFFF !important',
483
- },
484
- }}
485
- onClick={() => handleFileChange(null, null, null, true)}
486
- disabled={!!fileData?.name}
487
- >
488
- <IconProvider icon={faUpload} size="md" mr={1} />
489
- {titleAddButton}
490
- </Button>
491
- )}
492
- </Box>
493
- </Box>
494
- </Tooltip>
495
- {(error || helperText) && (
496
- <Box pl={1} pt={1}>
497
- <Text
498
- variant="caption"
499
- color={(error && 'redError') || 'greyDark'}
500
- data-testid="helperText"
501
- >
502
- {helperText}
503
- </Text>
504
- </Box>
505
- )}
506
-
507
- {fileData?.name && (
508
- <Box
509
- sx={{
510
- display: 'flex',
511
- alignItems: 'center',
512
- border: '0.5px solid',
513
- borderColor: error ? 'redError' : 'greyLightDefaultBorder',
514
- borderRadius: '0',
515
- justifyContent: 'space-between',
516
- maxHeight: '50px',
517
- }}
518
- mt={2}
519
- p={2}
520
- >
521
- <Box
522
- sx={{
523
- display: 'flex',
524
- alignItems: 'center',
525
- flexShrink: 1,
526
- minWidth: 0,
527
- }}
528
- >
529
- <IconProvider
530
- icon={faFileAlt}
531
- color="greyMediumInactive"
532
- size="sm"
533
- mr={1}
534
- />
535
- <Text
536
- variant="body2Medium"
537
- color="greyXDark"
538
- sx={{
539
- overflow: 'hidden',
540
- whiteSpace: 'nowrap',
541
- textAlign: 'left',
542
- textOverflow: 'ellipsis',
543
- flexShrink: 1,
544
- }}
545
- >
546
- {fileData?.name}
547
- </Text>
548
- {fileData?.size && fileData.size !== 0 ? (
549
- <Text
550
- component="span"
551
- variant="body2Regular"
552
- color="greyDark"
553
- sx={{ minWidth: '41px', marginLeft: '8px' }}
554
- >
555
- ({fileData?.size} ko)
556
- </Text>
557
- ) : null}
558
- </Box>
559
- <Box
560
- sx={{
561
- display: 'flex',
562
- alignItems: 'center',
563
- flexShrink: 0,
564
- }}
565
- >
566
- <IconButton
567
- size="medium"
568
- color="primary"
569
- sx={{
570
- height: '42px',
571
- width: '42px',
572
- mx: 1 / 2,
573
- outline: 'none !important',
574
- borderRadius: '4px',
575
- '&:hover': {
576
- backgroundColor: 'blueHoverOpacity12',
577
- },
578
- }}
579
- data-testid="show_btn"
580
- onClick={() => {
581
- if (fileData?.driveFileId) {
582
- window.open(
583
- `https://drive.google.com/file/d/${fileData.driveFileId}/view?usp=drive_we`,
584
- '_blank',
585
- );
586
- } else if (currentFile) {
587
- const currentFileURL = URL.createObjectURL(currentFile);
588
- window.open(currentFileURL);
589
- } else if (fileUrl) {
590
- window.open(fileUrl);
591
- }
592
- }}
593
- >
594
- <IconProvider
595
- icon={faEye}
596
- color="grey"
597
- size="md"
598
- height="16px"
599
- width="16px"
600
- />
601
- </IconButton>
602
- <IconButton
603
- size="medium"
604
- color="primary"
605
- sx={{
606
- mx: 1 / 2,
607
- height: '42px',
608
- width: '42px',
609
- outline: 'none !important',
610
- borderRadius: '4px',
611
- '&:hover': {
612
- backgroundColor: 'blueHoverOpacity12',
613
- },
614
- }}
615
- data-testid="delete_btn"
616
- onClick={handleDelete}
617
- >
618
- <IconProvider
619
- icon={faTrash}
620
- color="grey"
621
- size="md"
622
- height="16px"
623
- width="16px"
624
- />
625
- </IconButton>
626
- </Box>
627
- </Box>
628
- )}
629
- {children}
630
- </Box>
631
- );
632
- };
633
-
634
- export default FileUploaderSingle;
@@ -1,5 +0,0 @@
1
- export interface acceptText {
2
- fileFormat?: string;
3
- maxSize?: string;
4
- subText?: string;
5
- }