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";