From a56dcd7948cf060876f8282677001ec60c804248 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 27 Jan 2026 14:22:53 +0100 Subject: [PATCH 1/2] fix file drop --- frontend/webEditor/src/serialize/di.config.ts | 4 + .../webEditor/src/serialize/dropListener.ts | 83 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 frontend/webEditor/src/serialize/dropListener.ts diff --git a/frontend/webEditor/src/serialize/di.config.ts b/frontend/webEditor/src/serialize/di.config.ts index 4c1c0f4a..493081b5 100644 --- a/frontend/webEditor/src/serialize/di.config.ts +++ b/frontend/webEditor/src/serialize/di.config.ts @@ -9,6 +9,7 @@ import { SaveJsonFileCommand } from "./saveJsonFile"; import { SaveDfdAndDdFileCommand } from "./saveDfdAndDdFile"; import { AnalyzeCommand } from "./analyze"; import { LoadFromUrlCommand } from "./LoadUrl"; +import { JsonDropHandler, LoadDroppedFileCommand } from "./dropListener"; export const serializeModule = new ContainerModule((bind, unbind, isBound, rebind) => { const context = { bind, unbind, isBound, rebind }; @@ -20,6 +21,9 @@ export const serializeModule = new ContainerModule((bind, unbind, isBound, rebin configureCommand(context, SaveJsonFileCommand); configureCommand(context, SaveDfdAndDdFileCommand); configureCommand(context, AnalyzeCommand); + configureCommand(context, LoadDroppedFileCommand); + + bind(TYPES.MouseListener).to(JsonDropHandler); rebind(TYPES.IModelFactory).to(DfdModelFactory); }); diff --git a/frontend/webEditor/src/serialize/dropListener.ts b/frontend/webEditor/src/serialize/dropListener.ts new file mode 100644 index 00000000..c781a701 --- /dev/null +++ b/frontend/webEditor/src/serialize/dropListener.ts @@ -0,0 +1,83 @@ +import { inject, injectable } from "inversify"; +import { ActionDispatcher, ILogger, MouseListener, SModelElementImpl, TYPES } from "sprotty"; +import { Action } from "sprotty-protocol"; +import { FileData, LoadJsonCommand } from "./loadJson"; +import { SavedDiagram } from "./SavedDiagram"; +import { SETTINGS } from "../settings/Settings"; +import { ConstraintRegistry } from "../constraint/constraintRegistry"; +import { FileName } from "../fileName/fileName"; +import { LabelTypeRegistry } from "../labels/LabelTypeRegistry"; +import { LoadingIndicator } from "../loadingIndicator/loadingIndicator"; +import { EditorModeController } from "../settings/editorMode"; + +@injectable() +export class JsonDropHandler extends MouseListener { + constructor(@inject(TYPES.ILogger) private readonly logger: ILogger) { + super(); + } + + drop(_target: SModelElementImpl, ev: DragEvent): Promise[] { + this.logger.log(this, "Drop event detected", ev); + + // Prevent default behavior which would open the file in the browser + ev.preventDefault(); + + const file = ev.dataTransfer?.files[0]; + if (!file) { + return []; + } + + if (file.type !== "application/json") { + alert("Diagram file must be in JSON format"); + return []; + } + + return [file.text().then((t) => LoadDroppedFileAction.create(file.name, JSON.parse(t)))]; + } +} + +interface LoadDroppedFileAction extends Action { + file: FileData; +} + +namespace LoadDroppedFileAction { + export const KIND = "loadDroppedFileAction"; + export function create(fileName: string, content: SavedDiagram): LoadDroppedFileAction { + return { + kind: KIND, + file: { + fileName, + content, + }, + }; + } +} + +export class LoadDroppedFileCommand extends LoadJsonCommand { + static readonly KIND = LoadDroppedFileAction.KIND; + + constructor( + @inject(TYPES.Action) private readonly action: LoadDroppedFileAction, + @inject(TYPES.ILogger) logger: ILogger, + @inject(LabelTypeRegistry) labelTypeRegistry: LabelTypeRegistry, + @inject(ConstraintRegistry) constraintRegistry: ConstraintRegistry, + @inject(SETTINGS.Mode) editorModeController: EditorModeController, + @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, + @inject(FileName) fileName: FileName, + @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, + ) { + super( + logger, + labelTypeRegistry, + constraintRegistry, + editorModeController, + actionDispatcher, + fileName, + loadingIndicator, + ); + } + + protected async getFile(): Promise | undefined> { + return this.action.file; + } +} From aff4bc20a8ac50ae7c3d0ad1fa3f1442868a6b8e Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 27 Jan 2026 14:25:36 +0100 Subject: [PATCH 2/2] move event prevention after file type check --- frontend/webEditor/src/serialize/dropListener.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/webEditor/src/serialize/dropListener.ts b/frontend/webEditor/src/serialize/dropListener.ts index c781a701..c6bc62cb 100644 --- a/frontend/webEditor/src/serialize/dropListener.ts +++ b/frontend/webEditor/src/serialize/dropListener.ts @@ -19,19 +19,18 @@ export class JsonDropHandler extends MouseListener { drop(_target: SModelElementImpl, ev: DragEvent): Promise[] { this.logger.log(this, "Drop event detected", ev); - // Prevent default behavior which would open the file in the browser - ev.preventDefault(); - const file = ev.dataTransfer?.files[0]; if (!file) { return []; } if (file.type !== "application/json") { - alert("Diagram file must be in JSON format"); return []; } + // Prevent default behavior which would open the file in the browser + ev.preventDefault(); + return [file.text().then((t) => LoadDroppedFileAction.create(file.name, JSON.parse(t)))]; } }