-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathUseFilePicker.tsx
More file actions
111 lines (107 loc) · 4.67 KB
/
UseFilePicker.tsx
File metadata and controls
111 lines (107 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* eslint-disable @typescript-eslint/no-unused-vars */
import { useFilePicker } from 'use-file-picker';
import { Validator, ImageDimensionsValidator, FileAmountLimitValidator } from 'use-file-picker/validators';
import { type UseFilePickerConfig, type FileWithPath } from 'use-file-picker';
import { useState } from 'react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
class CustomValidator extends Validator {
/**
* Validation takes place before parsing. You have access to config passed as argument to useFilePicker hook
* and all plain file objects of selected files by user. Called once for all files after selection.
* Example validator below allowes only even amount of files
* @returns {Promise} resolve means that file passed validation, reject means that file did not pass
*/
async validateBeforeParsing(_config: UseFilePickerConfig, plainFiles: File[]): Promise<void> {
return new Promise((res, rej) => (plainFiles.length % 2 === 0 ? res(undefined) : rej({ oddNumberOfFiles: true })));
}
/**
* Validation takes place after parsing (or is never called if readFilesContent is set to false).
* You have access to config passed as argument to useFilePicker hook, FileWithPath object that is currently
* being validated and the reader object that has loaded that file. Called individually for every file.
* Example validator below allowes only if file hasn't been modified in last 24 hours
* @returns {Promise} resolve means that file passed validation, reject means that file did not pass
*/
async validateAfterParsing(_config: UseFilePickerConfig, file: FileWithPath, _reader: FileReader): Promise<void> {
return new Promise((res, rej) =>
file.lastModified < new Date().getTime() - 24 * 60 * 60 * 1000
? res(undefined)
: rej({ fileRecentlyModified: true, lastModified: file.lastModified })
);
}
}
export const UseFilePicker = () => {
const [selectionMode, setSelectionMode] = useState<'file' | 'dir'>('file');
const { openFilePicker, filesContent, loading, errors, plainFiles, clear } = useFilePicker({
multiple: true,
readAs: 'Text', // availible formats: "Text" | "BinaryString" | "ArrayBuffer" | "DataURL"
initializeWithCustomParameters(inputElement) {
inputElement.webkitdirectory = selectionMode === 'dir';
},
detectCancellation: 'promiseResultOnly',
// accept: ['.png', '.jpeg', '.heic'],
// readFilesContent: false, // ignores file content,
validators: [
new FileAmountLimitValidator({ min: 1, max: 3 }),
// new FileSizeValidator({ maxFileSize: 100_000 /* 100kb in bytes */ }),
new ImageDimensionsValidator({ maxHeight: 600 }),
],
onFilesSelected: ({ plainFiles, filesContent, errors }) => {
// this callback is always called, even if there are errors
console.log('onFilesSelected', plainFiles, filesContent, errors);
},
onFilesRejected: ({ errors }) => {
// this callback is called when there were validation errors
console.log('onFilesRejected', errors);
},
onFilesSuccessfullySelected: ({ plainFiles, filesContent }) => {
// this callback is called when there were no validation errors
console.log('onFilesSuccessfullySelected', plainFiles, filesContent);
},
});
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<button onClick={() => setSelectionMode(selectionMode === 'file' ? 'dir' : 'file')}>
{selectionMode === 'file' ? 'FILE' : 'DIR'}
</button>
<button
onClick={async () => {
const f = await openFilePicker();
console.log(f);
}}
>
Select file
</button>
<button onClick={() => clear()}>Clear</button>
<br />
Amount of selected files:
{plainFiles.length}
<br />
{errors.length ? (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<div style={{ marginTop: '10px' }}>Errors:</div>
{errors.map((error, index) => (
<div key={error.name}>
<span>{index + 1}.</span>
{Object.entries(error).map(([key, value]) => (
<div key={key}>
{key}: {typeof value === 'string' ? value : Array.isArray(value) ? value.join(', ') : null}
</div>
))}
</div>
))}
</div>
) : null}
{/* If readAs is set to DataURL, You can display an image */}
{filesContent.length ? <img src={filesContent[0].content} /> : null}
{filesContent.length ? <div>{filesContent[0].content}</div> : null}
<br />
{plainFiles.map(file => (
<div key={crypto.randomUUID()}>{file.name}</div>
))}
</div>
);
};