Browse Source

Adding ability to copy to clipboard as SVG (#1250)

Roxana Chiorean 5 years ago
parent
commit
d5366db341
5 changed files with 55 additions and 7 deletions
  1. 8 0
      src/clipboard.ts
  2. 26 0
      src/components/App.tsx
  3. 14 6
      src/data/index.ts
  4. 1 0
      src/locales/en.json
  5. 6 1
      src/scene/types.ts

+ 8 - 0
src/clipboard.ts

@@ -97,6 +97,14 @@ export async function copyCanvasToClipboardAsPng(canvas: HTMLCanvasElement) {
   });
 }
 
+export async function copyCanvasToClipboardAsSvg(svgroot: SVGSVGElement) {
+  try {
+    await navigator.clipboard.writeText(svgroot.outerHTML);
+  } catch (error) {
+    console.error(error);
+  }
+}
+
 export async function copyTextToSystemClipboard(text: string | null) {
   let copied = false;
   if (probablySupportsClipboardWriteText) {

+ 26 - 0
src/components/App.tsx

@@ -88,6 +88,7 @@ import {
   copyToAppClipboard,
   getClipboardContent,
   probablySupportsClipboardBlob,
+  probablySupportsClipboardWriteText,
 } from "../clipboard";
 import { normalizeScroll } from "../scene";
 import { getCenter, getDistance } from "../gesture";
@@ -565,6 +566,22 @@ export class App extends React.Component<any, AppState> {
     );
   };
 
+  private copyToClipboardAsSvg = () => {
+    const selectedElements = getSelectedElements(
+      globalSceneState.getAllElements(),
+      this.state,
+    );
+    exportCanvas(
+      "clipboard-svg",
+      selectedElements.length
+        ? selectedElements
+        : globalSceneState.getAllElements(),
+      this.state,
+      this.canvas!,
+      this.state,
+    );
+  };
+
   private onTapStart = (event: TouchEvent) => {
     if (!didTapTwice) {
       didTapTwice = true;
@@ -2661,6 +2678,11 @@ export class App extends React.Component<any, AppState> {
               label: t("labels.copyAsPng"),
               action: this.copyToClipboardAsPng,
             },
+          probablySupportsClipboardWriteText &&
+            hasNonDeletedElements(globalSceneState.getAllElements()) && {
+              label: t("labels.copyAsSvg"),
+              action: this.copyToClipboardAsSvg,
+            },
           ...this.actionManager.getContextMenuItems((action) =>
             this.canvasOnlyActions.includes(action.name),
           ),
@@ -2689,6 +2711,10 @@ export class App extends React.Component<any, AppState> {
           label: t("labels.copyAsPng"),
           action: this.copyToClipboardAsPng,
         },
+        probablySupportsClipboardWriteText && {
+          label: t("labels.copyAsSvg"),
+          action: this.copyToClipboardAsSvg,
+        },
         ...this.actionManager.getContextMenuItems(
           (action) => !this.canvasOnlyActions.includes(action.name),
         ),

+ 14 - 6
src/data/index.ts

@@ -7,7 +7,10 @@ import { exportToCanvas, exportToSvg } from "../scene/export";
 import { fileSave } from "browser-nativefs";
 
 import { t } from "../i18n";
-import { copyCanvasToClipboardAsPng } from "../clipboard";
+import {
+  copyCanvasToClipboardAsPng,
+  copyCanvasToClipboardAsSvg,
+} from "../clipboard";
 import { serializeAsJSON } from "./json";
 
 import { ExportType } from "../scene/types";
@@ -299,16 +302,21 @@ export async function exportCanvas(
   if (!hasNonDeletedElements(elements)) {
     return window.alert(t("alerts.cannotExportEmptyCanvas"));
   }
-  if (type === "svg") {
+  if (type === "svg" || type === "clipboard-svg") {
     const tempSvg = exportToSvg(elements, {
       exportBackground,
       viewBackgroundColor,
       exportPadding,
     });
-    await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
-      fileName: `${name}.svg`,
-    });
-    return;
+    if (type === "svg") {
+      await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
+        fileName: `${name}.svg`,
+      });
+      return;
+    } else if (type === "clipboard-svg") {
+      copyCanvasToClipboardAsSvg(tempSvg);
+      return;
+    }
   }
 
   const tempCanvas = exportToCanvas(elements, appState, {

+ 1 - 0
src/locales/en.json

@@ -4,6 +4,7 @@
     "selectAll": "Select All",
     "copy": "Copy",
     "copyAsPng": "Copy to clipboard as PNG",
+    "copyAsSvg": "Copy to clipboard as SVG",
     "bringForward": "Bring Forward",
     "sendToBack": "Send To Back",
     "bringToFront": "Bring To Front",

+ 6 - 1
src/scene/types.ts

@@ -22,7 +22,12 @@ export interface Scene {
   elements: ExcalidrawTextElement[];
 }
 
-export type ExportType = "png" | "clipboard" | "backend" | "svg";
+export type ExportType =
+  | "png"
+  | "clipboard"
+  | "clipboard-svg"
+  | "backend"
+  | "svg";
 
 export type ScrollBars = {
   horizontal: {