feat: open multi-part packages in browser

This commit is contained in:
2026-07-22 19:46:17 +02:00
parent 2c998952e5
commit 3b1777a5b3
11 changed files with 428 additions and 64 deletions

View File

@@ -2,25 +2,25 @@ import { useRef, useState, type ChangeEvent, type DragEvent } from 'react';
interface FileDropZoneProps {
disabled?: boolean;
onFile(file: File): void;
onFiles(files: File[]): void;
}
export function FileDropZone({ disabled = false, onFile }: FileDropZoneProps) {
export function FileDropZone({ disabled = false, onFiles }: FileDropZoneProps) {
const input = useRef<HTMLInputElement>(null);
const [dragging, setDragging] = useState(false);
const selectFile = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
const selectFiles = (event: ChangeEvent<HTMLInputElement>) => {
const files = [...(event.target.files ?? [])];
event.target.value = '';
if (file) onFile(file);
if (files.length > 0) onFiles(files);
};
const dropFile = (event: DragEvent<HTMLDivElement>) => {
const dropFiles = (event: DragEvent<HTMLDivElement>) => {
event.preventDefault();
setDragging(false);
if (disabled) return;
const file = event.dataTransfer.files[0];
if (file) onFile(file);
const files = [...event.dataTransfer.files];
if (files.length > 0) onFiles(files);
};
return (
@@ -36,23 +36,24 @@ export function FileDropZone({ disabled = false, onFile }: FileDropZoneProps) {
setDragging(false);
}
}}
onDrop={dropFile}
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 <code>.onepkg</code> notebook
package without sending it to a server.
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"
accept=".one,.onepkg,.cab"
multiple
disabled={disabled}
onChange={selectFile}
aria-label="Choose a OneNote file"
onChange={selectFiles}
aria-label="Choose OneNote files"
/>
<button
type="button"
@@ -60,10 +61,10 @@ export function FileDropZone({ disabled = false, onFile }: FileDropZoneProps) {
disabled={disabled}
onClick={() => input.current?.click()}
>
Choose OneNote file
Choose OneNote files
</button>
<span className="drop-zone__hint">
or drop one .one or .onepkg file here
or drop one .one, or one .onepkg with its .cab parts, here
</span>
</div>
);