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..c6bc62cb --- /dev/null +++ b/frontend/webEditor/src/serialize/dropListener.ts @@ -0,0 +1,82 @@ +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); + + const file = ev.dataTransfer?.files[0]; + if (!file) { + return []; + } + + if (file.type !== "application/json") { + 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)))]; + } +} + +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; + } +}