diff --git a/src/actions/actionClipboard.tsx b/src/actions/actionClipboard.tsx index 5d92290e..8a25e181 100644 --- a/src/actions/actionClipboard.tsx +++ b/src/actions/actionClipboard.tsx @@ -15,7 +15,9 @@ export const actionCopy = register({ name: "copy", trackEvent: { category: "element" }, perform: (elements, appState, _, app) => { - copyToClipboard(getNonDeletedElements(elements), appState, app.files); + const selectedElements = getSelectedElements(elements, appState, true); + + copyToClipboard(selectedElements, appState, app.files); return { commitToHistory: false, diff --git a/src/clipboard.ts b/src/clipboard.ts index 5b61dae2..bc4d3d3b 100644 --- a/src/clipboard.ts +++ b/src/clipboard.ts @@ -2,7 +2,6 @@ import { ExcalidrawElement, NonDeletedExcalidrawElement, } from "./element/types"; -import { getSelectedElements } from "./scene"; import { AppState, BinaryFiles } from "./types"; import { SVG_EXPORT_TAG } from "./scene/export"; import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts"; @@ -12,7 +11,7 @@ import { isPromiseLike } from "./utils"; type ElementsClipboard = { type: typeof EXPORT_DATA_TYPES.excalidrawClipboard; - elements: ExcalidrawElement[]; + elements: readonly NonDeletedExcalidrawElement[]; files: BinaryFiles | undefined; }; @@ -57,19 +56,20 @@ const clipboardContainsElements = ( export const copyToClipboard = async ( elements: readonly NonDeletedExcalidrawElement[], appState: AppState, - files: BinaryFiles, + files: BinaryFiles | null, ) => { // select binded text elements when copying - const selectedElements = getSelectedElements(elements, appState, true); const contents: ElementsClipboard = { type: EXPORT_DATA_TYPES.excalidrawClipboard, - elements: selectedElements, - files: selectedElements.reduce((acc, element) => { - if (isInitializedImageElement(element) && files[element.fileId]) { - acc[element.fileId] = files[element.fileId]; - } - return acc; - }, {} as BinaryFiles), + elements, + files: files + ? elements.reduce((acc, element) => { + if (isInitializedImageElement(element) && files[element.fileId]) { + acc[element.fileId] = files[element.fileId]; + } + return acc; + }, {} as BinaryFiles) + : undefined, }; const json = JSON.stringify(contents); CLIPBOARD = json; diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 450239ef..ccd3ad86 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -17,6 +17,7 @@ Please add the latest change on the top under the correct section. #### Features +- Expose util `exportToClipboard`[https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exportToClipboard] which allows to copy the scene contents to clipboard as `svg`, `png` or `json` [#5103](https://github.com/excalidraw/excalidraw/pull/5103). - Expose `window.EXCALIDRAW_EXPORT_SOURCE` which you can use to overwrite the `source` field in exported data [#5095](https://github.com/excalidraw/excalidraw/pull/5095). - The `exportToBlob` utility now supports the `exportEmbedScene` option when generating a png image [#5047](https://github.com/excalidraw/excalidraw/pull/5047). - Exported [`restoreLibraryItems`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreLibraryItems) API [#4995](https://github.com/excalidraw/excalidraw/pull/4995). diff --git a/src/packages/excalidraw/README_NEXT.md b/src/packages/excalidraw/README_NEXT.md index f3da869c..ee7c85c2 100644 --- a/src/packages/excalidraw/README_NEXT.md +++ b/src/packages/excalidraw/README_NEXT.md @@ -857,7 +857,7 @@ This function returns the canvas with the exported elements, appState and dimens
 exportToBlob(
-  opts: ExportOpts & {
+  opts: ExportOpts & {
   mimeType?: string,
   quality?: number;
 })
@@ -900,6 +900,34 @@ exportToSvg({
 
 This function returns a promise which resolves to svg of the exported drawing.
 
+#### `exportToClipboard`
+
+**_Signature_**
+
+
+exportToClipboard(
+  opts: ExportOpts & {
+  mimeType?: string,
+  quality?: number;
+  type: 'png' | 'svg' |'json'
+})
+
+ +| Name | Type | Default | Description | +| --- | --- | --- | --- | --- | --- | +| opts | | | This param is same as the params passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exportToCanvas). | +| mimeType | string | "image/png" | Indicates the image format, this will be used when exporting as `png`. | +| quality | number | 0.92 | A value between 0 and 1 indicating the [image quality](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#parameters). Applies only to `image/jpeg`/`image/webp` MIME types. This will be used when exporting as `png`. | +| type | 'png' | 'svg' | 'json' | | This determines the format to which the scene data should be exported. | + +**How to use** + +```js +import { exportToClipboard } from "@excalidraw/excalidraw-next"; +``` + +Copies the scene data in the specified format (determined by `type`) to clipboard. + ##### Additional attributes of appState for `export\*` APIs | Name | Type | Default | Description | diff --git a/src/packages/excalidraw/example/App.js b/src/packages/excalidraw/example/App.js index 4182cd3b..49af956c 100644 --- a/src/packages/excalidraw/example/App.js +++ b/src/packages/excalidraw/example/App.js @@ -10,8 +10,13 @@ import { MIME_TYPES } from "../../../constants"; // This is so that we use the bundled excalidraw.development.js file instead // of the actual source code -const { exportToCanvas, exportToSvg, exportToBlob, Excalidraw } = - window.ExcalidrawLib; +const { + exportToCanvas, + exportToSvg, + exportToBlob, + exportToClipboard, + Excalidraw, +} = window.ExcalidrawLib; const resolvablePromise = () => { let resolve; let reject; @@ -141,6 +146,15 @@ export default function App() { } }, []); + const onCopy = async (type) => { + await exportToClipboard({ + elements: excalidrawRef.current.getSceneElements(), + appState: excalidrawRef.current.getAppState(), + files: excalidrawRef.current.getFiles(), + type, + }); + window.alert(`Copied to clipboard as ${type} sucessfully`); + }; return (

Excalidraw Example

@@ -175,6 +189,7 @@ export default function App() { > Update Library + +
+ + + +
[]; @@ -81,7 +86,7 @@ export const exportToBlob = async ( mimeType?: string; quality?: number; }, -): Promise => { +): Promise => { let { mimeType = MIME_TYPES.png, quality } = opts; if (mimeType === MIME_TYPES.png && typeof quality === "number") { @@ -107,9 +112,12 @@ export const exportToBlob = async ( quality = quality ? quality : /image\/jpe?g/.test(mimeType) ? 0.92 : 0.8; - return new Promise((resolve) => { + return new Promise((resolve, reject) => { canvas.toBlob( - async (blob: Blob | null) => { + async (blob) => { + if (!blob) { + return reject(new Error("couldn't export to blob")); + } if ( blob && mimeType === MIME_TYPES.png && @@ -156,6 +164,33 @@ export const exportToSvg = async ({ ); }; +export const exportToClipboard = async ( + opts: ExportOpts & { + mimeType?: string; + quality?: number; + type: "png" | "svg" | "json"; + }, +) => { + if (opts.type === "svg") { + const svg = await exportToSvg(opts); + await copyTextToSystemClipboard(svg.outerHTML); + } else if (opts.type === "png") { + await copyBlobToClipboardAsPng(exportToBlob(opts)); + } else if (opts.type === "json") { + const appState = { + offsetTop: 0, + offsetLeft: 0, + width: 0, + height: 0, + ...getDefaultAppState(), + ...opts.appState, + }; + await copyToClipboard(opts.elements, appState, opts.files); + } else { + throw new Error("Invalid export type"); + } +}; + export { serializeAsJSON, serializeLibraryAsJSON } from "../data/json"; export { loadFromBlob, loadLibraryFromBlob } from "../data/blob"; export { getFreeDrawSvgPath } from "../renderer/renderElement";