From d1e52d8f0592d196971b5e0f7daed7a38589a1c0 Mon Sep 17 00:00:00 2001 From: Paulo Gustavo Veiga Date: Sat, 14 Jan 2023 22:41:32 -0800 Subject: [PATCH] Add image export support. --- packages/editor/lang/en.json | 8 ++--- .../pane/keyboard-shortcut-help/index.tsx | 2 +- packages/mindplot/src/components/Designer.ts | 32 ++++++++++++++----- .../components/export/BinaryImageExporter.ts | 2 +- .../src/components/export/Exporter.ts | 10 +++--- .../action-dispatcher/export-dialog/index.tsx | 6 ++-- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/editor/lang/en.json b/packages/editor/lang/en.json index 983fb8ca..8815c1f6 100644 --- a/packages/editor/lang/en.json +++ b/packages/editor/lang/en.json @@ -47,6 +47,9 @@ "appbar.tooltip-undo": { "defaultMessage": "Undo" }, + "editor-panel.icon-title": { + "defaultMessage": "Icon" + }, "editor-panel.link-panel-title": { "defaultMessage": "Link" }, @@ -158,9 +161,6 @@ "icon-picker.show-images": { "defaultMessage": "Show images" }, - "editor-panel.icon-title": { - "defaultMessage": "Icon" - }, "link.help_text": { "defaultMessage": "Address is not valid" }, @@ -195,7 +195,7 @@ "defaultMessage": "Collpase children" }, "shortcut-help-pane.copy-and-text": { - "defaultMessage": "Copy and paste topics" + "defaultMessage": "Copy and paste topics/Copy mindmap image to clipboard." }, "shortcut-help-pane.delete-topic": { "defaultMessage": "Delete topic" diff --git a/packages/editor/src/components/action-widget/pane/keyboard-shortcut-help/index.tsx b/packages/editor/src/components/action-widget/pane/keyboard-shortcut-help/index.tsx index 0422e6bc..ee1e2e84 100644 --- a/packages/editor/src/components/action-widget/pane/keyboard-shortcut-help/index.tsx +++ b/packages/editor/src/components/action-widget/pane/keyboard-shortcut-help/index.tsx @@ -133,7 +133,7 @@ const KeyboardShorcutsHelp = (): ReactElement => { Ctrl + C / Ctrl + V diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts index 0115b7d5..6ca6b067 100644 --- a/packages/mindplot/src/components/Designer.ts +++ b/packages/mindplot/src/components/Designer.ts @@ -57,6 +57,7 @@ import WidgetManager from './WidgetManager'; import { TopicShapeType } from './model/INodeModel'; import { LineType } from './ConnectionLine'; import XMLSerializerFactory from './persistence/XMLSerializerFactory'; +import ImageExpoterFactory from './export/ImageExporterFactory'; class Designer extends Events { private _mindmap: Mindmap | null; @@ -377,7 +378,7 @@ class Designer extends Events { } } - copyToClipboard(): void { + async copyToClipboard(): Promise { let topics = this.getModel().filterSelectedTopics(); if (topics.length > 0) { const mindmap = new Mindmap(); @@ -391,19 +392,34 @@ class Designer extends Events { nodeModel.connectTo(central); }); - // Serialize to mindmap ... + // Create text blob ... const serializer = XMLSerializerFactory.createFromMindmap(mindmap); const document = serializer.toXML(mindmap); - const xmmStr: string = new XMLSerializer().serializeToString(document); + const xmlStr: string = new XMLSerializer().serializeToString(document); + const textPlainBlob = new Blob([xmlStr], { type: 'text/plain' }); - // Convert to node, only text/html is supported... - const type = 'text/plain'; - const blob = new Blob([xmmStr], { type }); + // Create image blob ... + const workspace = designer.getWorkSpace(); + const svgElement = workspace.getSVGElement(); + const size = { width: window.innerWidth, height: window.innerHeight }; + + const imageUrl = ImageExpoterFactory.create( + 'png', + svgElement, + size.width, + size.height, + false, + ); + let imgStr = await imageUrl.exportAndEncode(); + imgStr = imgStr.replace('octet/stream', 'image/png'); + const imgBlob = await (await fetch(imgStr)).blob(); + + // Finally, add to clipboard ... const clipboard = new ClipboardItem({ - [blob.type]: blob, + [textPlainBlob.type]: textPlainBlob, + [imgBlob.type]: imgBlob, }); - // Copy to clipboard ... navigator.clipboard.write([clipboard]).then( () => console.log('Copy of node success'), (e) => console.error(e), diff --git a/packages/mindplot/src/components/export/BinaryImageExporter.ts b/packages/mindplot/src/components/export/BinaryImageExporter.ts index ba98b8e2..05946757 100644 --- a/packages/mindplot/src/components/export/BinaryImageExporter.ts +++ b/packages/mindplot/src/components/export/BinaryImageExporter.ts @@ -44,7 +44,7 @@ class BinaryImageExporter extends Exporter { } export(): Promise { - throw new Error('Images can not be exporeted'); + throw new Error('Images can not be exported'); } exportAndEncode(): Promise { diff --git a/packages/mindplot/src/components/export/Exporter.ts b/packages/mindplot/src/components/export/Exporter.ts index fbcb20a4..d28d7abc 100644 --- a/packages/mindplot/src/components/export/Exporter.ts +++ b/packages/mindplot/src/components/export/Exporter.ts @@ -25,12 +25,10 @@ abstract class Exporter { this._contentType = contentType; } - exportAndEncode(): Promise { - const exportValue = this.export(); - return exportValue.then((value: string) => { - const blob = new Blob([value], { type: this._contentType }); - return URL.createObjectURL(blob); - }); + async exportAndEncode(): Promise { + const exportValue = await this.export(); + const blob = new Blob([exportValue], { type: this._contentType }); + return URL.createObjectURL(blob); } abstract export(): Promise; diff --git a/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx index 7bc2b14d..83a03779 100644 --- a/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx +++ b/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx @@ -127,8 +127,10 @@ const ExportDialog = ({ exporter = TextExporterFactory.create(formatType, mindmap); break; } - default: - throw new Error('Unsupported encoding'); + default: { + const exhaustiveCheck: never = formatType; + throw new Error(`Unhandled color case: ${exhaustiveCheck}`); + } } return exporter.exportAndEncode();