Add support for opening multiple selected files from disk (#4128)

This commit is contained in:
Keavon Chambers
2026-05-07 21:21:45 -07:00
committed by GitHub
parent dff8ac5511
commit 9d876ab27d
8 changed files with 108 additions and 32 deletions

View File

@@ -94,8 +94,8 @@ export function createPortfolioStore(subscriptions: SubscriptionsRouter, editor:
});
subscriptions.subscribeFrontendMessage("TriggerOpen", async () => {
const data = await upload(`image/*,.${editor.fileExtension()}`, "data");
editor.openFile(data.filename, data.content);
const files = await upload(`image/*,.${editor.fileExtension()}`, "data", true);
files.forEach((file) => editor.openFile(file.filename, file.content));
});
subscriptions.subscribeFrontendMessage("TriggerImport", async () => {

View File

@@ -32,29 +32,44 @@ export function downloadFile(filename: string, content: Uint8Array) {
export async function upload(accept: string, textOrData: "text"): Promise<UploadResult<string>>;
export async function upload(accept: string, textOrData: "data"): Promise<UploadResult<Uint8Array>>;
export async function upload(accept: string, textOrData: "both"): Promise<UploadResult<{ text: string; data: Uint8Array }>>;
export async function upload(accept: string, textOrData: "text" | "data" | "both"): Promise<UploadResult<string | Uint8Array | { text: string; data: Uint8Array }>> {
export async function upload(accept: string, textOrData: "data", multiple: true): Promise<UploadResult<Uint8Array>[]>;
export async function upload(
accept: string,
textOrData: "text" | "data" | "both",
multiple = false,
): Promise<UploadResult<string | Uint8Array | { text: string; data: Uint8Array }> | UploadResult<Uint8Array>[]> {
return new Promise((resolve) => {
const element = document.createElement("input");
element.type = "file";
element.accept = accept;
element.multiple = multiple;
element.addEventListener(
"change",
async () => {
if (element.files?.length) {
const file = element.files[0];
if (!element.files?.length) return;
const filename = file.name;
const type = file.type;
const content =
textOrData === "text"
? await file.text()
: textOrData === "data"
? new Uint8Array(await file.arrayBuffer())
: { text: await file.text(), data: new Uint8Array(await file.arrayBuffer()) };
resolve({ filename, type, content });
// The `multiple: true` overload constrains `textOrData` to "data", so we know each file produces a Uint8Array
if (multiple) {
const results = await Promise.all(
Array.from(element.files).map(async (file) => ({
filename: file.name,
type: file.type,
content: new Uint8Array(await file.arrayBuffer()),
})),
);
resolve(results);
return;
}
const file = element.files[0];
const content =
textOrData === "text"
? await file.text()
: textOrData === "data"
? new Uint8Array(await file.arrayBuffer())
: { text: await file.text(), data: new Uint8Array(await file.arrayBuffer()) };
resolve({ filename: file.name, type: file.type, content });
},
{ capture: false, once: true },
);