Files
onenote-tools/src/components/FileDropZone.tsx

72 lines
2.1 KiB
TypeScript

import { useRef, useState, type ChangeEvent, type DragEvent } from 'react';
interface FileDropZoneProps {
disabled?: boolean;
onFiles(files: File[]): void;
}
export function FileDropZone({ disabled = false, onFiles }: FileDropZoneProps) {
const input = useRef<HTMLInputElement>(null);
const [dragging, setDragging] = useState(false);
const selectFiles = (event: ChangeEvent<HTMLInputElement>) => {
const files = [...(event.target.files ?? [])];
event.target.value = '';
if (files.length > 0) onFiles(files);
};
const dropFiles = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
setDragging(false);
if (disabled) return;
const files = [...event.dataTransfer.files];
if (files.length > 0) onFiles(files);
};
return (
<div
className={`drop-zone${dragging ? ' drop-zone--active' : ''}`}
onDragEnter={(event) => {
event.preventDefault();
if (!disabled) setDragging(true);
}}
onDragOver={(event) => event.preventDefault()}
onDragLeave={(event) => {
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
setDragging(false);
}
}}
onDrop={dropFiles}
data-testid="file-drop-zone"
>
<p className="drop-zone__eyebrow">Private, local-first reader</p>
<h2>Open a OneNote file</h2>
<p>
Inspect a <code>.one</code> section, or a <code>.onepkg</code> notebook
with any linked <code>.cab</code> parts, without sending it to a server.
</p>
<input
ref={input}
className="visually-hidden"
type="file"
accept=".one,.onepkg,.cab"
multiple
disabled={disabled}
onChange={selectFiles}
aria-label="Choose OneNote files"
/>
<button
type="button"
className="primary-button"
disabled={disabled}
onClick={() => input.current?.click()}
>
Choose OneNote files
</button>
<span className="drop-zone__hint">
or drop one .one, or one .onepkg with its .cab parts, here
</span>
</div>
);
}