Handling events
You can use the onInput event on <FileInputArea> to handle selected files.
Use the multiple prop to allow selecting multiple files at the same time.
<FileInputArea
multiple
onInput={(e) => {
const files = e.currentTarget.files;
if (!files || !files.length) return;
}}
/>
Area content
<FileInputArea multiple aria-label="Upload file" onInput={...}>
<Icon
icon={faArrowUpFromBracket}
className="bfc-base-2"
style={{ marginBottom: 8 }}
/>
<div>
Drag & drop file(s) or{" "}
<span className="bf-neutral-link-text">click to upload</span>
</div>
<div className="bfc-base-2">PDF only</div>
</FileInputArea>
List of files
Selected files can be displayed below the input field, with a loading spinner
while uploading.
We've added a disabled<Button> here so it will take up the same space as the
remove button later.
<Button small disabled noPadding>
<Icon.Spinner />
</Button>{" "}
uploading_file.jpg
When the upload completes, swap to a faXMark icon button to remove. Adding
a tooltip for clarity is also a good idea:
<Tooltip content="Remove">
<Button onClick={} variant="flat" small pill noPadding>
<Icon icon={faXmark} />
</Button>
</Tooltip>{" "}
uploaded_file.png
If the upload failed, you can include a red faCircleExclamation icon and a
faRefresh icon button to retry:
<Tooltip content="Remove">
<Button onClick={} variant="flat" small pill noPadding>
<Icon icon={faXmark} />
</Button>
</Tooltip>{" "}
<Tooltip content="Could not upload file">
<Icon icon={faCircleExclamation} className="bfc-alert" marginRight />
</Tooltip>
upload_failed.png{" "}
<Tooltip content="Retry">
<Button small onClick={} variant="flat" noPadding pill>
<Icon icon={faRefresh} />
</Button>
</Tooltip>
Interactive demo
This simulation lets you select some files, fake uploading for ~1s with a 10%
chance to fail.
Sandbox
import { useEffect, useState } from "react";
import { faExclamation } from "@fortawesome/free-solid-svg-icons/faExclamation";
import { faRefresh } from "@fortawesome/free-solid-svg-icons/faRefresh";
import { faXmark } from "@fortawesome/free-solid-svg-icons/faXmark";
import { faArrowUpFromBracket } from "@fortawesome/free-solid-svg-icons/faArrowUpFromBracket";
import Button from "@intility/bifrost-react/Button";
import FileInputArea from "@intility/bifrost-react/FileInputArea";
import Grid from "@intility/bifrost-react/Grid";
import Icon from "@intility/bifrost-react/Icon";
import Tooltip from "@intility/bifrost-react/Tooltip";
function SelectedFile({
file,
removeFile,
}: {
file: File;
removeFile: (file: File) => void;
}) {
const [uploading, setUploading] = useState(false);
const [failed, setFailed] = useState(false);
const initiateUpload = () => {
setFailed(false);
setUploading(true);
setTimeout(
() => {
setUploading(false);
if (Math.random() > 0.9) {
setFailed(true);
}
},
Math.random() * 500 + 500,
);
};
useEffect(initiateUpload, []);
return (
<div>
<Tooltip content="Remove" disabled={uploading}>
<Button
onClick={() => removeFile(file)}
variant="flat"
small
aria-label="Remove file"
noPadding
pill
disabled={uploading}
>
{uploading ? <Icon.Spinner /> : <Icon icon={faXmark} />}
</Button>
</Tooltip>{" "}
{failed && (
<>
<Tooltip content="Could not upload file">
<Icon icon={faExclamation} className="bfc-alert" marginRight />
</Tooltip>
</>
)}
{file.name}{" "}
{failed && (
<Tooltip content="Retry">
<Button small onClick={initiateUpload} variant="flat" noPadding pill>
<Icon icon={faRefresh} />
</Button>
</Tooltip>
)}
</div>
);
}
export default function MultipleFileUpload() {
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const inputEventHandler = (e: React.FormEvent<HTMLInputElement>) => {
const files = e.currentTarget.files;
if (!files || !files.length) return;
setSelectedFiles((x) => [
...x,
...Array.from(files).filter((y) => !x.some((f) => f.name === y.name)),
]);
};
return (
<Grid>
<FileInputArea multiple onInput={inputEventHandler}>
<Icon
icon={faArrowUpFromBracket}
className="bfc-base-2"
style={{ marginBottom: 8 }}
/>
<div>
Drag & drop file(s) or{" "}
<span className="bf-neutral-link-text">click to upload</span>
</div>
<div className="bfc-base-2">PDF only</div>
</FileInputArea>
{selectedFiles.length > 0 && (
<Grid gap={4}>
{selectedFiles.map((file) => (
<SelectedFile
key={file.name}
file={file}
removeFile={(fileToRemove) =>
setSelectedFiles(
selectedFiles.filter((x) => x !== fileToRemove),
)
}
/>
))}
</Grid>
)}
</Grid>
);
}