implement file dropping

This commit is contained in:
Reimar 2025-10-15 09:34:02 +02:00
parent e3326f0076
commit ff304c620c
3 changed files with 48 additions and 13 deletions

View File

@ -62,6 +62,7 @@ const projectName = document.querySelector("#project-name");
const saveButton = document.querySelector("#save-button"); const saveButton = document.querySelector("#save-button");
const loadButton = document.querySelector("#load-button"); const loadButton = document.querySelector("#load-button");
const exportButton = document.querySelector("#export-button"); const exportButton = document.querySelector("#export-button");
const dropZone = document.querySelector("main");
const projectSaveHandler = new ProjectSaveHandler(editor, assetEditor, assetProvider, projectName); const projectSaveHandler = new ProjectSaveHandler(editor, assetEditor, assetProvider, projectName);
@ -105,7 +106,7 @@ exportButton.onclick = async () => {
}; };
loadButton.onclick = () => { loadButton.onclick = () => {
projectSaveHandler.loadFromFile(); projectSaveHandler.showLoadFilePrompt();
}; };
saveButton.onclick = () => { saveButton.onclick = () => {
@ -120,3 +121,26 @@ addEventListener("keydown", (ev) => {
}); });
toggleAssetEditorButton.addEventListener("click", () => assetEditor.toggleEditor()); toggleAssetEditorButton.addEventListener("click", () => assetEditor.toggleEditor());
window.ondragover = (ev) => ev.preventDefault();
window.ondrop = (ev) => ev.preventDefault();
dropZone.onclick = (ev) => ev.preventDefault();
dropZone.ondragover = (ev) => ev.preventDefault();
dropZone.ondrop = async (ev) => {
for (const file of ev.dataTransfer.files) {
if (file.name.endsWith(".karlkode")) {
await projectSaveHandler.loadFromFile(file);
continue;
}
if (file.type.startsWith("image/")) {
await assetEditor.addAsset({ name: file.name, file });
continue;
}
alert("Invalid file: " + file.name);
}
ev.preventDefault();
};

View File

@ -1,6 +1,7 @@
import { promptUpload } from "./prompt_upload.js"; import { promptUpload } from "./prompt_upload.js";
import { downloadFile, slugify } from "./utils.js"; import { downloadFile, slugify } from "./utils.js";
import { HtmlExporter } from "./html_exporter.js"; import { HtmlExporter } from "./html_exporter.js";
import { KarlkoderCodec } from "./karlkoder_codec.js";
export class ProjectSaveHandler { export class ProjectSaveHandler {
constructor(editor, assetEditor, assetProvider, projectName) { constructor(editor, assetEditor, assetProvider, projectName) {
@ -22,16 +23,7 @@ export class ProjectSaveHandler {
}); });
} }
async loadFromFile() { async showLoadFilePrompt() {
if (
!this.isSaved &&
!confirm(
"Your project has not been saved. Loading a new project will override your current one. Continue?",
)
) {
return;
}
const files = await promptUpload(".karlkode", false); const files = await promptUpload(".karlkode", false);
if (files.length === 0) { if (files.length === 0) {
return; return;
@ -43,8 +35,21 @@ export class ProjectSaveHandler {
); );
} }
this.loadFromFile(files[0]);
}
async loadFromFile(file) {
if (
!this.isSaved &&
!confirm(
"Your project has not been saved. Loading a new project will override your current one. Continue?",
)
) {
return;
}
const items = KarlkoderCodec.de( const items = KarlkoderCodec.de(
await fetch(URL.createObjectURL(files[0])).then((x) => x.bytes()), await fetch(URL.createObjectURL(file)).then((x) => x.bytes()),
); );
const assets = items const assets = items
@ -57,7 +62,7 @@ export class ProjectSaveHandler {
const code = items.find((x) => x.tag === "code"); const code = items.find((x) => x.tag === "code");
delete code.tag; delete code.tag;
assetEditor.importAssets( this.assetEditor.importAssets(
assets.map(({ name, mime, content }) => { assets.map(({ name, mime, content }) => {
const file = new File([content], name, { type: mime }); const file = new File([content], name, { type: mime });
@ -68,6 +73,8 @@ export class ProjectSaveHandler {
this.projectName.value = code.name; this.projectName.value = code.name;
const dec = new TextDecoder(); const dec = new TextDecoder();
this.editor.setValue(dec.decode(code.content)); this.editor.setValue(dec.decode(code.content));
this.isSaved = true;
} }
async saveFile() { async saveFile() {

View File

@ -24,6 +24,10 @@ main {
gap: 2rem; gap: 2rem;
} }
#file-drop-input {
display: none;
}
.section-header { .section-header {
background-color: #bdbdbd; background-color: #bdbdbd;
border-top-left-radius: 0.5rem; border-top-left-radius: 0.5rem;