useFileInput.tsx 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { useCallback, useRef, useState } from "react";
  2. export interface FileWithPath extends File {
  3. readonly path?: string;
  4. }
  5. export default function useFileInput({ directory }: { directory?: boolean }) {
  6. const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
  7. const inputRef = useRef<HTMLInputElement>();
  8. const openSelectorDialog = useCallback(() => {
  9. if (inputRef.current) {
  10. inputRef.current.value = null;
  11. inputRef.current.click();
  12. }
  13. }, []);
  14. const handleChange: React.ChangeEventHandler<HTMLInputElement> = async (
  15. event,
  16. ) => {
  17. if (!!event.target && !!event.target.files) {
  18. const files = [...event.target.files].map((file) =>
  19. toFileWithPath(file),
  20. );
  21. setSelectedFiles(files);
  22. }
  23. };
  24. const getInputProps = useCallback(
  25. () => ({
  26. type: "file",
  27. multiple: true,
  28. style: { display: "none" },
  29. ...(directory ? { directory: "", webkitdirectory: "" } : {}),
  30. ref: inputRef,
  31. onChange: handleChange,
  32. }),
  33. [],
  34. );
  35. return {
  36. getInputProps,
  37. open: openSelectorDialog,
  38. selectedFiles: selectedFiles,
  39. };
  40. }
  41. // https://github.com/react-dropzone/file-selector/blob/master/src/file.ts#L88
  42. export function toFileWithPath(file: File, path?: string): FileWithPath {
  43. if (typeof (file as any).path !== "string") {
  44. // on electron, path is already set to the absolute path
  45. const { webkitRelativePath } = file;
  46. Object.defineProperty(file, "path", {
  47. value:
  48. typeof path === "string"
  49. ? path
  50. : typeof webkitRelativePath === "string" && // If <input webkitdirectory> is set,
  51. // the File will have a {webkitRelativePath} property
  52. // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory
  53. webkitRelativePath.length > 0
  54. ? webkitRelativePath
  55. : file.name,
  56. writable: false,
  57. configurable: false,
  58. enumerable: true,
  59. });
  60. }
  61. return file;
  62. }