Add image export support.

This commit is contained in:
Paulo Gustavo Veiga 2023-01-14 22:41:32 -08:00
parent e54d4ff543
commit d1e52d8f05
6 changed files with 38 additions and 22 deletions

View File

@ -47,6 +47,9 @@
"appbar.tooltip-undo": { "appbar.tooltip-undo": {
"defaultMessage": "Undo" "defaultMessage": "Undo"
}, },
"editor-panel.icon-title": {
"defaultMessage": "Icon"
},
"editor-panel.link-panel-title": { "editor-panel.link-panel-title": {
"defaultMessage": "Link" "defaultMessage": "Link"
}, },
@ -158,9 +161,6 @@
"icon-picker.show-images": { "icon-picker.show-images": {
"defaultMessage": "Show images" "defaultMessage": "Show images"
}, },
"editor-panel.icon-title": {
"defaultMessage": "Icon"
},
"link.help_text": { "link.help_text": {
"defaultMessage": "Address is not valid" "defaultMessage": "Address is not valid"
}, },
@ -195,7 +195,7 @@
"defaultMessage": "Collpase children" "defaultMessage": "Collpase children"
}, },
"shortcut-help-pane.copy-and-text": { "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": { "shortcut-help-pane.delete-topic": {
"defaultMessage": "Delete topic" "defaultMessage": "Delete topic"

View File

@ -133,7 +133,7 @@ const KeyboardShorcutsHelp = (): ReactElement => {
<td> <td>
<FormattedMessage <FormattedMessage
id="shortcut-help-pane.copy-and-text" id="shortcut-help-pane.copy-and-text"
defaultMessage="Copy and paste topics" defaultMessage="Copy and paste topics/Copy mindmap image to clipboard."
/> />
</td> </td>
<td>Ctrl + C / Ctrl + V</td> <td>Ctrl + C / Ctrl + V</td>

View File

@ -57,6 +57,7 @@ import WidgetManager from './WidgetManager';
import { TopicShapeType } from './model/INodeModel'; import { TopicShapeType } from './model/INodeModel';
import { LineType } from './ConnectionLine'; import { LineType } from './ConnectionLine';
import XMLSerializerFactory from './persistence/XMLSerializerFactory'; import XMLSerializerFactory from './persistence/XMLSerializerFactory';
import ImageExpoterFactory from './export/ImageExporterFactory';
class Designer extends Events { class Designer extends Events {
private _mindmap: Mindmap | null; private _mindmap: Mindmap | null;
@ -377,7 +378,7 @@ class Designer extends Events {
} }
} }
copyToClipboard(): void { async copyToClipboard(): Promise<void> {
let topics = this.getModel().filterSelectedTopics(); let topics = this.getModel().filterSelectedTopics();
if (topics.length > 0) { if (topics.length > 0) {
const mindmap = new Mindmap(); const mindmap = new Mindmap();
@ -391,19 +392,34 @@ class Designer extends Events {
nodeModel.connectTo(central); nodeModel.connectTo(central);
}); });
// Serialize to mindmap ... // Create text blob ...
const serializer = XMLSerializerFactory.createFromMindmap(mindmap); const serializer = XMLSerializerFactory.createFromMindmap(mindmap);
const document = serializer.toXML(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... // Create image blob ...
const type = 'text/plain'; const workspace = designer.getWorkSpace();
const blob = new Blob([xmmStr], { type }); 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({ const clipboard = new ClipboardItem({
[blob.type]: blob, [textPlainBlob.type]: textPlainBlob,
[imgBlob.type]: imgBlob,
}); });
// Copy to clipboard ...
navigator.clipboard.write([clipboard]).then( navigator.clipboard.write([clipboard]).then(
() => console.log('Copy of node success'), () => console.log('Copy of node success'),
(e) => console.error(e), (e) => console.error(e),

View File

@ -44,7 +44,7 @@ class BinaryImageExporter extends Exporter {
} }
export(): Promise<string> { export(): Promise<string> {
throw new Error('Images can not be exporeted'); throw new Error('Images can not be exported');
} }
exportAndEncode(): Promise<string> { exportAndEncode(): Promise<string> {

View File

@ -25,12 +25,10 @@ abstract class Exporter {
this._contentType = contentType; this._contentType = contentType;
} }
exportAndEncode(): Promise<string> { async exportAndEncode(): Promise<string> {
const exportValue = this.export(); const exportValue = await this.export();
return exportValue.then((value: string) => { const blob = new Blob([exportValue], { type: this._contentType });
const blob = new Blob([value], { type: this._contentType }); return URL.createObjectURL(blob);
return URL.createObjectURL(blob);
});
} }
abstract export(): Promise<string>; abstract export(): Promise<string>;

View File

@ -127,8 +127,10 @@ const ExportDialog = ({
exporter = TextExporterFactory.create(formatType, mindmap); exporter = TextExporterFactory.create(formatType, mindmap);
break; break;
} }
default: default: {
throw new Error('Unsupported encoding'); const exhaustiveCheck: never = formatType;
throw new Error(`Unhandled color case: ${exhaustiveCheck}`);
}
} }
return exporter.exportAndEncode(); return exporter.exportAndEncode();