72 lines
2.1 KiB
TypeScript
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>
|
|
);
|
|
}
|