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",
|
||||
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,
|
||||
|
@ -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;
|
||||
|
@ -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).
|
||||
|
@ -857,7 +857,7 @@ This function returns the canvas with the exported elements, appState and dimens
|
||||
|
||||
<pre>
|
||||
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,
|
||||
quality?: number;
|
||||
})
|
||||
@ -900,6 +900,34 @@ exportToSvg({
|
||||
|
||||
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
|
||||
|
||||
| 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
|
||||
// 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 (
|
||||
<div className="App">
|
||||
<h1> Excalidraw Example</h1>
|
||||
@ -175,6 +189,7 @@ export default function App() {
|
||||
>
|
||||
Update Library
|
||||
</button>
|
||||
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -213,6 +228,17 @@ export default function App() {
|
||||
/>
|
||||
Switch to Dark Theme
|
||||
</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 className="excalidraw-wrapper">
|
||||
<Excalidraw
|
||||
|
@ -197,6 +197,7 @@ export {
|
||||
loadLibraryFromBlob,
|
||||
loadFromBlob,
|
||||
getFreeDrawSvgPath,
|
||||
exportToClipboard,
|
||||
} from "../../packages/utils";
|
||||
export { isLinearElement } from "../../element/typeChecks";
|
||||
|
||||
|
@ -10,6 +10,11 @@ import { restore } from "../data/restore";
|
||||
import { MIME_TYPES } from "../constants";
|
||||
import { encodePngMetadata } from "../data/image";
|
||||
import { serializeAsJSON } from "../data/json";
|
||||
import {
|
||||
copyBlobToClipboardAsPng,
|
||||
copyTextToSystemClipboard,
|
||||
copyToClipboard,
|
||||
} from "../clipboard";
|
||||
|
||||
type ExportOpts = {
|
||||
elements: readonly NonDeleted<ExcalidrawElement>[];
|
||||
@ -81,7 +86,7 @@ export const exportToBlob = async (
|
||||
mimeType?: string;
|
||||
quality?: number;
|
||||
},
|
||||
): Promise<Blob | null> => {
|
||||
): Promise<Blob> => {
|
||||
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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user