@groupeactual/ui-kit 1.7.0-beta.4 → 1.7.0-beta.6

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