feat: export exportToClipboard util from package (#5103)
* feat: export copyToClipboard from package * use promise constructor for better browser supprt * add type to exportToClipboard * update docs * use json instead of text and use selected element in actionCopy * pass `files` in example `exportToClipboard` * fix bad access Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
aee1e2451e
commit
6a0f800716
@ -15,7 +15,9 @@ export const actionCopy = register({
|
|||||||
name: "copy",
|
name: "copy",
|
||||||
trackEvent: { category: "element" },
|
trackEvent: { category: "element" },
|
||||||
perform: (elements, appState, _, app) => {
|
perform: (elements, appState, _, app) => {
|
||||||
copyToClipboard(getNonDeletedElements(elements), appState, app.files);
|
const selectedElements = getSelectedElements(elements, appState, true);
|
||||||
|
|
||||||
|
copyToClipboard(selectedElements, appState, app.files);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commitToHistory: false,
|
commitToHistory: false,
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
} from "./element/types";
|
} from "./element/types";
|
||||||
import { getSelectedElements } from "./scene";
|
|
||||||
import { AppState, BinaryFiles } from "./types";
|
import { AppState, BinaryFiles } from "./types";
|
||||||
import { SVG_EXPORT_TAG } from "./scene/export";
|
import { SVG_EXPORT_TAG } from "./scene/export";
|
||||||
import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts";
|
import { tryParseSpreadsheet, Spreadsheet, VALID_SPREADSHEET } from "./charts";
|
||||||
@ -12,7 +11,7 @@ import { isPromiseLike } from "./utils";
|
|||||||
|
|
||||||
type ElementsClipboard = {
|
type ElementsClipboard = {
|
||||||
type: typeof EXPORT_DATA_TYPES.excalidrawClipboard;
|
type: typeof EXPORT_DATA_TYPES.excalidrawClipboard;
|
||||||
elements: ExcalidrawElement[];
|
elements: readonly NonDeletedExcalidrawElement[];
|
||||||
files: BinaryFiles | undefined;
|
files: BinaryFiles | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,19 +56,20 @@ const clipboardContainsElements = (
|
|||||||
export const copyToClipboard = async (
|
export const copyToClipboard = async (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
files: BinaryFiles,
|
files: BinaryFiles | null,
|
||||||
) => {
|
) => {
|
||||||
// select binded text elements when copying
|
// select binded text elements when copying
|
||||||
const selectedElements = getSelectedElements(elements, appState, true);
|
|
||||||
const contents: ElementsClipboard = {
|
const contents: ElementsClipboard = {
|
||||||
type: EXPORT_DATA_TYPES.excalidrawClipboard,
|
type: EXPORT_DATA_TYPES.excalidrawClipboard,
|
||||||
elements: selectedElements,
|
elements,
|
||||||
files: selectedElements.reduce((acc, element) => {
|
files: files
|
||||||
if (isInitializedImageElement(element) && files[element.fileId]) {
|
? elements.reduce((acc, element) => {
|
||||||
acc[element.fileId] = files[element.fileId];
|
if (isInitializedImageElement(element) && files[element.fileId]) {
|
||||||
}
|
acc[element.fileId] = files[element.fileId];
|
||||||
return acc;
|
}
|
||||||
}, {} as BinaryFiles),
|
return acc;
|
||||||
|
}, {} as BinaryFiles)
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
const json = JSON.stringify(contents);
|
const json = JSON.stringify(contents);
|
||||||
CLIPBOARD = json;
|
CLIPBOARD = json;
|
||||||
|
@ -17,6 +17,7 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
#### Features
|
#### 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).
|
- 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).
|
- 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).
|
- Exported [`restoreLibraryItems`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreLibraryItems) API [#4995](https://github.com/excalidraw/excalidraw/pull/4995).
|
||||||
|
@ -857,7 +857,7 @@ This function returns the canvas with the exported elements, appState and dimens
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
exportToBlob(
|
exportToBlob(
|
||||||
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L10">ExportOpts</a> & {
|
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & {
|
||||||
mimeType?: string,
|
mimeType?: string,
|
||||||
quality?: number;
|
quality?: number;
|
||||||
})
|
})
|
||||||
@ -900,6 +900,34 @@ exportToSvg({
|
|||||||
|
|
||||||
This function returns a promise which resolves to svg of the exported drawing.
|
This function returns a promise which resolves to svg of the exported drawing.
|
||||||
|
|
||||||
|
#### `exportToClipboard`
|
||||||
|
|
||||||
|
**_Signature_**
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
exportToClipboard(
|
||||||
|
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & {
|
||||||
|
mimeType?: string,
|
||||||
|
quality?: number;
|
||||||
|
type: 'png' | 'svg' |'json'
|
||||||
|
})
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
| 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
|
##### Additional attributes of appState for `export\*` APIs
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|
@ -10,8 +10,13 @@ import { MIME_TYPES } from "../../../constants";
|
|||||||
// This is so that we use the bundled excalidraw.development.js file instead
|
// This is so that we use the bundled excalidraw.development.js file instead
|
||||||
// of the actual source code
|
// of the actual source code
|
||||||
|
|
||||||
const { exportToCanvas, exportToSvg, exportToBlob, Excalidraw } =
|
const {
|
||||||
window.ExcalidrawLib;
|
exportToCanvas,
|
||||||
|
exportToSvg,
|
||||||
|
exportToBlob,
|
||||||
|
exportToClipboard,
|
||||||
|
Excalidraw,
|
||||||
|
} = window.ExcalidrawLib;
|
||||||
const resolvablePromise = () => {
|
const resolvablePromise = () => {
|
||||||
let resolve;
|
let resolve;
|
||||||
let reject;
|
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 (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<h1> Excalidraw Example</h1>
|
<h1> Excalidraw Example</h1>
|
||||||
@ -175,6 +189,7 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
Update Library
|
Update Library
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -213,6 +228,17 @@ export default function App() {
|
|||||||
/>
|
/>
|
||||||
Switch to Dark Theme
|
Switch to Dark Theme
|
||||||
</label>
|
</label>
|
||||||
|
<div>
|
||||||
|
<button onClick={onCopy.bind(null, "png")}>
|
||||||
|
Copy to Clipboard as PNG
|
||||||
|
</button>
|
||||||
|
<button onClick={onCopy.bind(null, "svg")}>
|
||||||
|
Copy to Clipboard as SVG
|
||||||
|
</button>
|
||||||
|
<button onClick={onCopy.bind(null, "json")}>
|
||||||
|
Copy to Clipboard as JSON
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="excalidraw-wrapper">
|
<div className="excalidraw-wrapper">
|
||||||
<Excalidraw
|
<Excalidraw
|
||||||
|
@ -197,6 +197,7 @@ export {
|
|||||||
loadLibraryFromBlob,
|
loadLibraryFromBlob,
|
||||||
loadFromBlob,
|
loadFromBlob,
|
||||||
getFreeDrawSvgPath,
|
getFreeDrawSvgPath,
|
||||||
|
exportToClipboard,
|
||||||
} from "../../packages/utils";
|
} from "../../packages/utils";
|
||||||
export { isLinearElement } from "../../element/typeChecks";
|
export { isLinearElement } from "../../element/typeChecks";
|
||||||
|
|
||||||
|
@ -10,6 +10,11 @@ import { restore } from "../data/restore";
|
|||||||
import { MIME_TYPES } from "../constants";
|
import { MIME_TYPES } from "../constants";
|
||||||
import { encodePngMetadata } from "../data/image";
|
import { encodePngMetadata } from "../data/image";
|
||||||
import { serializeAsJSON } from "../data/json";
|
import { serializeAsJSON } from "../data/json";
|
||||||
|
import {
|
||||||
|
copyBlobToClipboardAsPng,
|
||||||
|
copyTextToSystemClipboard,
|
||||||
|
copyToClipboard,
|
||||||
|
} from "../clipboard";
|
||||||
|
|
||||||
type ExportOpts = {
|
type ExportOpts = {
|
||||||
elements: readonly NonDeleted<ExcalidrawElement>[];
|
elements: readonly NonDeleted<ExcalidrawElement>[];
|
||||||
@ -81,7 +86,7 @@ export const exportToBlob = async (
|
|||||||
mimeType?: string;
|
mimeType?: string;
|
||||||
quality?: number;
|
quality?: number;
|
||||||
},
|
},
|
||||||
): Promise<Blob | null> => {
|
): Promise<Blob> => {
|
||||||
let { mimeType = MIME_TYPES.png, quality } = opts;
|
let { mimeType = MIME_TYPES.png, quality } = opts;
|
||||||
|
|
||||||
if (mimeType === MIME_TYPES.png && typeof quality === "number") {
|
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;
|
quality = quality ? quality : /image\/jpe?g/.test(mimeType) ? 0.92 : 0.8;
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve, reject) => {
|
||||||
canvas.toBlob(
|
canvas.toBlob(
|
||||||
async (blob: Blob | null) => {
|
async (blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
return reject(new Error("couldn't export to blob"));
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
blob &&
|
blob &&
|
||||||
mimeType === MIME_TYPES.png &&
|
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 { serializeAsJSON, serializeLibraryAsJSON } from "../data/json";
|
||||||
export { loadFromBlob, loadLibraryFromBlob } from "../data/blob";
|
export { loadFromBlob, loadLibraryFromBlob } from "../data/blob";
|
||||||
export { getFreeDrawSvgPath } from "../renderer/renderElement";
|
export { getFreeDrawSvgPath } from "../renderer/renderElement";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user