feat: Add onPaste prop to customise clipboard paste event (#3420)

* Add Awaited type util

* Expose onPasteFromClipboard props

* Add `event` as second param for advanced usages

* Add support for async flows

* Extract ClipboardData type

* Rename `onPasteFromClipboard` to `onPaste`

* Remove unused type helper

* Add `onPaste` documentation

* tweak docs

* fix

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
Fabien BERNARD 2021-04-09 16:49:58 +02:00 committed by GitHub
parent 89472c14ed
commit d91950bd03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 8 deletions

View File

@ -14,6 +14,13 @@ type ElementsClipboard = {
elements: ExcalidrawElement[];
};
export interface ClipboardData {
spreadsheet?: Spreadsheet;
elements?: readonly ExcalidrawElement[];
text?: string;
errorMessage?: string;
}
let CLIPBOARD = "";
let PREFER_APP_CLIPBOARD = false;
@ -110,12 +117,7 @@ const getSystemClipboard = async (
*/
export const parseClipboard = async (
event: ClipboardEvent | null,
): Promise<{
spreadsheet?: Spreadsheet;
elements?: readonly ExcalidrawElement[];
text?: string;
errorMessage?: string;
}> => {
): Promise<ClipboardData> => {
const systemClipboard = await getSystemClipboard(event);
// if system clipboard empty, couldn't be resolved, or contains previously

View File

@ -1188,6 +1188,11 @@ class App extends React.Component<AppProps, AppState> {
return;
}
const data = await parseClipboard(event);
if (this.props.onPaste) {
if (await this.props.onPaste(data, event)) {
return;
}
}
if (data.errorMessage) {
this.setState({ errorMessage: data.errorMessage });
} else if (data.spreadsheet) {

View File

@ -13,6 +13,10 @@ Please add the latest change on the top under the correct section.
## Unreleased
## Excalidraw API
- Add `onPaste` prop to handle custom clipboard behaviours [#3420](https://github.com/excalidraw/excalidraw/pull/3420).
## Excalidraw Library
### Features

View File

@ -364,6 +364,7 @@ To view the full example visit :point_down:
| [`theme`](#theme) | `light` or `dark` | | The theme of the Excalidraw component |
| [`name`](#name) | string | | Name of the drawing |
| [`UIOptions`](#UIOptions) | <pre>{ canvasActions: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L208"> CanvasActions<a/> }</pre> | [DEFAULT UI OPTIONS](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L129) | To customise UI options. Currently we support customising [`canvas actions`](#canvasActions) |
| [`onPaste`](#onPaste) | <pre>(data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts#L17">ClipboardData</a>, event: ClipboardEvent &#124; null) => boolean</pre> | | Callback to be triggered if passed when the something is pasted in to the scene |
### Dimensions of Excalidraw
@ -528,7 +529,7 @@ This prop controls Excalidraw's theme. When supplied, the value takes precedence
This prop sets the name of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over `intialData.appState.name`, the `name` will be fully controlled by host app and the users won't be able to edit from within Excalidraw.
### `UIOptions`
#### `UIOptions`
This prop can be used to customise UI of Excalidraw. Currently we support customising only [`canvasActions`](#canvasActions). It accepts the below parameters
@ -536,7 +537,7 @@ This prop can be used to customise UI of Excalidraw. Currently we support custom
{ canvasActions: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L208"> CanvasActions<a/> }
</pre>
#### canvasActions
##### canvasActions
| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
@ -548,6 +549,18 @@ This prop can be used to customise UI of Excalidraw. Currently we support custom
| `saveScene` | boolean | true | Implies whether to show `Save button` |
| `theme` | boolean | true | Implies whether to show `Theme toggle` |
#### `onPaste`
This callback is triggered if passed when something is pasted into the scene. You can use this callback in case you want to do something additional when the paste event occurs.
<pre>
(data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts#L17">ClipboardData</a>, event: ClipboardEvent &#124; null) => boolean
</pre>
This callback must return a `boolean` value or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to a boolean value.
In case you want to prevent the excalidraw paste action you must return `true`, it will stop the native excalidraw clipboard management flow (nothing will be pasted into the scene).
### Does it support collaboration ?
No Excalidraw package doesn't come with collaboration, since this would have different implementations on the consumer so we expose the API's which you can use to communicate with Excalidraw as mentioned above. If you are interested in understanding how Excalidraw does it you can check it [here](https://github.com/excalidraw/excalidraw/blob/master/src/excalidraw-app/index.tsx).

View File

@ -29,6 +29,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
theme,
name,
renderCustomStats,
onPaste,
} = props;
const canvasActions = props.UIOptions?.canvasActions;
@ -78,6 +79,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
name={name}
renderCustomStats={renderCustomStats}
UIOptions={UIOptions}
onPaste={onPaste}
/>
</InitializeApp>
);

View File

@ -20,6 +20,7 @@ import { ExcalidrawImperativeAPI } from "./components/App";
import type { ResolvablePromise } from "./utils";
import { Spreadsheet } from "./charts";
import { Language } from "./i18n";
import { ClipboardData } from "./clipboard";
export type Point = Readonly<RoughPoint>;
@ -177,6 +178,10 @@ export interface ExcalidrawProps {
appState: AppState,
canvas: HTMLCanvasElement | null,
) => void;
onPaste?: (
data: ClipboardData,
event: ClipboardEvent | null,
) => Promise<boolean> | boolean;
renderFooter?: (isMobile: boolean) => JSX.Element;
langCode?: Language["code"];
viewModeEnabled?: boolean;