Compare commits
3 Commits
master
...
dwelle/bum
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d77627fbcf | ||
![]() |
07897a2174 | ||
![]() |
fb4e5948fa |
@ -1,7 +1,43 @@
|
|||||||
{
|
{
|
||||||
"extends": ["@excalidraw/eslint-config", "react-app"],
|
"extends": ["plugin:react-hooks/recommended"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": ["react", "@typescript-eslint"],
|
||||||
|
"root": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"import/no-anonymous-default-export": "off",
|
"import/no-anonymous-default-export": "off",
|
||||||
"no-restricted-globals": "off"
|
"no-restricted-globals": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "warn",
|
||||||
|
"curly": "warn",
|
||||||
|
"dot-notation": "warn",
|
||||||
|
"no-console": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"allow": ["warn", "error", "info"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-else-return": "warn",
|
||||||
|
"no-lonely-if": "warn",
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"message": "Use 't(...)' instead of literal text in JSX",
|
||||||
|
"selector": "JSXText[value=/\\w/]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-unneeded-ternary": "warn",
|
||||||
|
"no-unused-expressions": "warn",
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"no-useless-return": "warn",
|
||||||
|
"no-var": "warn",
|
||||||
|
"object-shorthand": "warn",
|
||||||
|
"one-var": ["warn", "never"],
|
||||||
|
"prefer-arrow-callback": "warn",
|
||||||
|
"prefer-const": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"destructuring": "all"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"prefer-template": "warn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,10 @@ const { CLIEngine } = require("eslint");
|
|||||||
const cli = new CLIEngine({});
|
const cli = new CLIEngine({});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"*.{js,ts,tsx}": files => {
|
"*.{js,ts,tsx}": (files) => {
|
||||||
return (
|
return (
|
||||||
"eslint --max-warnings=0 --fix " + files.filter(file => !cli.isPathIgnored(file)).join(" ")
|
"eslint --max-warnings=0 --fix " +
|
||||||
|
files.filter((file) => !cli.isPathIgnored(file)).join(" ")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
"*.{css,scss,json,md,html,yml}": ["prettier --write"],
|
"*.{css,scss,json,md,html,yml}": ["prettier --write"],
|
||||||
|
@ -9,7 +9,7 @@ You will need to import the `Footer` component from the package and wrap your co
|
|||||||
```jsx live
|
```jsx live
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div style={{ height: "500px"}}>
|
<div style={{ height: "500px" }}>
|
||||||
<Excalidraw>
|
<Excalidraw>
|
||||||
<Footer>
|
<Footer>
|
||||||
<button
|
<button
|
||||||
@ -39,7 +39,7 @@ const MobileFooter = ({}) => {
|
|||||||
<Footer>
|
<Footer>
|
||||||
<button
|
<button
|
||||||
className="custom-footer"
|
className="custom-footer"
|
||||||
style= {{ marginLeft: '20px', height: '2rem'}}
|
style={{ marginLeft: "20px", height: "2rem" }}
|
||||||
onClick={() => alert("This is custom footer in mobile menu")}
|
onClick={() => alert("This is custom footer in mobile menu")}
|
||||||
>
|
>
|
||||||
custom footer
|
custom footer
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
# initialData
|
# initialData
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
{ elements?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a>, appState?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a> }
|
{ elements?:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
|
ExcalidrawElement[]
|
||||||
|
</a>
|
||||||
|
, appState?: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||||
|
AppState
|
||||||
|
</a> }
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
This helps to load Excalidraw with `initialData`. It must be an object or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to an object containing the below optional fields.
|
This helps to load Excalidraw with `initialData`. It must be an object or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to an object containing the below optional fields.
|
||||||
@ -46,7 +52,7 @@ function App() {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
appState: { zenModeEnabled: true, viewBackgroundColor: "#a5d8ff" },
|
appState: { zenModeEnabled: true, viewBackgroundColor: "#a5d8ff" },
|
||||||
scrollToContent: true
|
scrollToContent: true,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
# Props
|
# Props
|
||||||
|
|
||||||
All `props` are *optional*.
|
All `props` are _optional_.
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| [`initialData`](/docs/@excalidraw/excalidraw/api/props/initialdata) | `object` | `null` | <code>Promise<object | null></code> | `null` | The initial data with which app loads. |
|
| [`initialData`](/docs/@excalidraw/excalidraw/api/props/initialdata) | `object` | `null` | <code>Promise<object | null></code> | `null` | The initial data with which app loads. |
|
||||||
| [`ref`](/docs/@excalidraw/excalidraw/api/props/ref) | `object` | _ | `Ref` to be passed to Excalidraw |
|
| [`ref`](/docs/@excalidraw/excalidraw/api/props/ref) | `object` | \_ | `Ref` to be passed to Excalidraw |
|
||||||
| [`isCollaborating`](#iscollaborating) | `boolean` | _ | This indicates if the app is in `collaboration` mode |
|
| [`isCollaborating`](#iscollaborating) | `boolean` | \_ | This indicates if the app is in `collaboration` mode |
|
||||||
| [`onChange`](#onchange) | `function` | _ | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw `elements` and the current `app state`. |
|
| [`onChange`](#onchange) | `function` | \_ | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw `elements` and the current `app state`. |
|
||||||
| [`onPointerUpdate`](#onpointerupdate) | `function` | _ | Callback triggered when mouse pointer is updated. |
|
| [`onPointerUpdate`](#onpointerupdate) | `function` | \_ | Callback triggered when mouse pointer is updated. |
|
||||||
| [`onPointerDown`](#onpointerdown) | `function` | _ | This prop if passed gets triggered on pointer down evenets |
|
| [`onPointerDown`](#onpointerdown) | `function` | \_ | This prop if passed gets triggered on pointer down evenets |
|
||||||
| [`onScrollChange`](#onscrollchange) | `function` | _ | This prop if passed gets triggered when scrolling the canvas. |
|
| [`onScrollChange`](#onscrollchange) | `function` | \_ | This prop if passed gets triggered when scrolling the canvas. |
|
||||||
| [`onPaste`](#onpaste) | `function` | _ | Callback to be triggered if passed when the something is pasted in to the scene |
|
| [`onPaste`](#onpaste) | `function` | \_ | Callback to be triggered if passed when the something is pasted in to the scene |
|
||||||
| [`onLibraryChange`](#onlibrarychange) | `function` | _ | The callback if supplied is triggered when the library is updated and receives the library items. |
|
| [`onLibraryChange`](#onlibrarychange) | `function` | \_ | The callback if supplied is triggered when the library is updated and receives the library items. |
|
||||||
| [`onLinkOpen`](#onlinkopen) | `function` | _ | The callback if supplied is triggered when any link is opened. |
|
| [`onLinkOpen`](#onlinkopen) | `function` | \_ | The callback if supplied is triggered when any link is opened. |
|
||||||
| [`langCode`](#langcode) | `string` | `en` | Language code string to be used in Excalidraw |
|
| [`langCode`](#langcode) | `string` | `en` | Language code string to be used in Excalidraw |
|
||||||
| [`renderTopRightUI`](/docs/@excalidraw/excalidraw/api/props/render-props#rendertoprightui) | `function` | _ | Render function that renders custom UI in top right corner |
|
| [`renderTopRightUI`](/docs/@excalidraw/excalidraw/api/props/render-props#rendertoprightui) | `function` | \_ | Render function that renders custom UI in top right corner |
|
||||||
| [`renderCustomStats`](/docs/@excalidraw/excalidraw/api/props/render-props#rendercustomstats) | `function` | _ | Render function that can be used to render custom stats on the stats dialog. |
|
| [`renderCustomStats`](/docs/@excalidraw/excalidraw/api/props/render-props#rendercustomstats) | `function` | \_ | Render function that can be used to render custom stats on the stats dialog. |
|
||||||
| [`renderSidebar`](/docs/@excalidraw/excalidraw/api/props/render-props#rendersidebar) | `function` | _ | Render function that renders custom sidebar. |
|
| [`renderSidebar`](/docs/@excalidraw/excalidraw/api/props/render-props#rendersidebar) | `function` | \_ | Render function that renders custom sidebar. |
|
||||||
| [`viewModeEnabled`](#viewmodeenabled) | `boolean` | _ | This indicates if the app is in `view` mode. |
|
| [`viewModeEnabled`](#viewmodeenabled) | `boolean` | \_ | This indicates if the app is in `view` mode. |
|
||||||
| [`zenModeEnabled`](#zenmodeenabled) | `boolean` | _ | This indicates if the `zen` mode is enabled |
|
| [`zenModeEnabled`](#zenmodeenabled) | `boolean` | \_ | This indicates if the `zen` mode is enabled |
|
||||||
| [`gridModeEnabled`](#gridmodeenabled) | `boolean` | _ | This indicates if the `grid` mode is enabled |
|
| [`gridModeEnabled`](#gridmodeenabled) | `boolean` | \_ | This indicates if the `grid` mode is enabled |
|
||||||
| [`libraryReturnUrl`](#libraryreturnurl) | `string` | _ | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
|
| [`libraryReturnUrl`](#libraryreturnurl) | `string` | \_ | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
|
||||||
| [`theme`](#theme) | `"light"` | `"dark"` | `"light"` | The theme of the Excalidraw component |
|
| [`theme`](#theme) | `"light"` | `"dark"` | `"light"` | The theme of the Excalidraw component |
|
||||||
| [`name`](#name) | `string` | | Name of the drawing |
|
| [`name`](#name) | `string` | | Name of the drawing |
|
||||||
| [`UIOptions`](/docs/@excalidraw/excalidraw/api/props/ui-options) | `object` | [DEFAULT UI OPTIONS](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L151) | To customise UI options. Currently we support customising [`canvas actions`](#canvasactions) |
|
| [`UIOptions`](/docs/@excalidraw/excalidraw/api/props/ui-options) | `object` | [DEFAULT UI OPTIONS](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L151) | To customise UI options. Currently we support customising [`canvas actions`](#canvasactions) |
|
||||||
| [`detectScroll`](#detectscroll) | `boolean` | `true` | Indicates whether to update the offsets when nearest ancestor is scrolled. |
|
| [`detectScroll`](#detectscroll) | `boolean` | `true` | Indicates whether to update the offsets when nearest ancestor is scrolled. |
|
||||||
| [`handleKeyboardGlobally`](#handlekeyboardglobally) | `boolean` | `false` | Indicates whether to bind the keyboard events to document. |
|
| [`handleKeyboardGlobally`](#handlekeyboardglobally) | `boolean` | `false` | Indicates whether to bind the keyboard events to document. |
|
||||||
| [`autoFocus`](#autofocus) | `boolean` | `false` | indicates whether to focus the Excalidraw component on page load |
|
| [`autoFocus`](#autofocus) | `boolean` | `false` | indicates whether to focus the Excalidraw component on page load |
|
||||||
| [`generateIdForFile`](#generateidforfile) | `function` | _ | Allows you to override `id` generation for files added on canvas |
|
| [`generateIdForFile`](#generateidforfile) | `function` | \_ | Allows you to override `id` generation for files added on canvas |
|
||||||
| [`validateEmbeddable`](#validateEmbeddable) | string[] | `boolean | RegExp | RegExp[] | ((link: string) => boolean | undefined)` | \_ | use for custom src url validation |
|
| [`validateEmbeddable`](#validateEmbeddable) | string[] | `boolean | RegExp | RegExp[] | ((link: string) => boolean | undefined)` | \_ | use for custom src url validation |
|
||||||
| [`renderEmbeddable`](/docs/@excalidraw/excalidraw/api/props/render-props#renderEmbeddable) | `function` | \_ | Render function that can override the built-in `<iframe>` |
|
| [`renderEmbeddable`](/docs/@excalidraw/excalidraw/api/props/render-props#renderEmbeddable) | `function` | \_ | Render function that can override the built-in `<iframe>` |
|
||||||
|
|
||||||
@ -95,7 +95,14 @@ This callback is triggered when mouse pointer is updated.
|
|||||||
This prop if passed will be triggered on pointer down events and has the below signature.
|
This prop if passed will be triggered on pointer down events and has the below signature.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
(activeTool: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L115"> AppState["activeTool"]</a>, pointerDownState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L424">PointerDownState</a>) => void
|
(activeTool:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L115">
|
||||||
|
{" "}
|
||||||
|
AppState["activeTool"]
|
||||||
|
</a>
|
||||||
|
, pointerDownState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L424">
|
||||||
|
PointerDownState
|
||||||
|
</a>) => void
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### onScrollChange
|
### onScrollChange
|
||||||
@ -111,7 +118,11 @@ This prop if passed will be triggered when canvas is scrolled and has the below
|
|||||||
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.
|
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>
|
<pre>
|
||||||
(data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts#L18">ClipboardData</a>, event: ClipboardEvent | null) => boolean
|
(data:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/clipboard.ts#L18">
|
||||||
|
ClipboardData
|
||||||
|
</a>
|
||||||
|
, event: ClipboardEvent | null) => boolean
|
||||||
</pre>
|
</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.
|
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.
|
||||||
@ -137,8 +148,11 @@ It is invoked with empty items when user clears the library. You can use this ca
|
|||||||
This prop if passed will be triggered when clicked on `link`. To handle the redirect yourself (such as when using your own router for internal links), you must call `event.preventDefault()`.
|
This prop if passed will be triggered when clicked on `link`. To handle the redirect yourself (such as when using your own router for internal links), you must call `event.preventDefault()`.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
(element: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement</a>,
|
(element:{" "}
|
||||||
event: CustomEvent<{ nativeEvent: MouseEvent }>) => void
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
|
ExcalidrawElement
|
||||||
|
</a>
|
||||||
|
, event: CustomEvent<{ nativeEvent: MouseEvent }>) => void
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -181,31 +195,30 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
|
|||||||
|
|
||||||
### viewModeEnabled
|
### viewModeEnabled
|
||||||
|
|
||||||
This prop indicates whether the app is in `view mode`. When supplied, the value takes precedence over *intialData.appState.viewModeEnabled*, the `view mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
This prop indicates whether the app is in `view mode`. When supplied, the value takes precedence over _intialData.appState.viewModeEnabled_, the `view mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||||
|
|
||||||
### zenModeEnabled
|
### zenModeEnabled
|
||||||
|
|
||||||
This prop indicates whether the app is in `zen mode`. When supplied, the value takes precedence over *intialData.appState.zenModeEnabled*, the `zen mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
This prop indicates whether the app is in `zen mode`. When supplied, the value takes precedence over _intialData.appState.zenModeEnabled_, the `zen mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||||
|
|
||||||
### gridModeEnabled
|
### gridModeEnabled
|
||||||
|
|
||||||
This prop indicates whether the shows the grid. When supplied, the value takes precedence over *intialData.appState.gridModeEnabled*, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
This prop indicates whether the shows the grid. When supplied, the value takes precedence over _intialData.appState.gridModeEnabled_, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
|
||||||
|
|
||||||
### libraryReturnUrl
|
### libraryReturnUrl
|
||||||
|
|
||||||
If supplied, this URL will be used when user tries to install a library from [libraries.excalidraw.com](https://libraries.excalidraw.com).
|
If supplied, this URL will be used when user tries to install a library from [libraries.excalidraw.com](https://libraries.excalidraw.com).
|
||||||
Defaults to *window.location.origin + window.location.pathname*. To install the libraries in the same tab from which it was opened, you need to set `window.name` (to any alphanumeric string) — if it's not set it will open in a new tab.
|
Defaults to _window.location.origin + window.location.pathname_. To install the libraries in the same tab from which it was opened, you need to set `window.name` (to any alphanumeric string) — if it's not set it will open in a new tab.
|
||||||
|
|
||||||
### theme
|
### theme
|
||||||
|
|
||||||
This prop controls Excalidraw's theme. When supplied, the value takes precedence over *intialData.appState.theme*, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app unless *UIOptions.canvasActions.toggleTheme* is set to `true`, in which case the `theme` prop will control Excalidraw's default theme with ability to allow theme switching (you must take care of updating the `theme` prop when you detect a change to `appState.theme` from the [onChange](#onchange) callback).
|
This prop controls Excalidraw's theme. When supplied, the value takes precedence over _intialData.appState.theme_, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app unless _UIOptions.canvasActions.toggleTheme_ is set to `true`, in which case the `theme` prop will control Excalidraw's default theme with ability to allow theme switching (you must take care of updating the `theme` prop when you detect a change to `appState.theme` from the [onChange](#onchange) callback).
|
||||||
|
|
||||||
You can use [`THEME`](/docs/@excalidraw/excalidraw/api/utils#theme) to specify the theme.
|
You can use [`THEME`](/docs/@excalidraw/excalidraw/api/utils#theme) to specify the theme.
|
||||||
|
|
||||||
### name
|
### name
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
### detectScroll
|
### detectScroll
|
||||||
|
|
||||||
@ -226,7 +239,7 @@ This prop indicates whether to `focus` the Excalidraw component on page load. De
|
|||||||
Allows you to override `id` generation for files added on canvas (images). By default, an SHA-1 digest of the file is used.
|
Allows you to override `id` generation for files added on canvas (images). By default, an SHA-1 digest of the file is used.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
(file: File) => string | Promise<string>
|
(file: File) => string | Promise<string>;
|
||||||
```
|
```
|
||||||
|
|
||||||
### validateEmbeddable
|
### validateEmbeddable
|
||||||
|
@ -6,8 +6,7 @@
|
|||||||
(isMobile: boolean, appState:
|
(isMobile: boolean, appState:
|
||||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||||
AppState
|
AppState
|
||||||
</a>
|
</a>) => JSX | null
|
||||||
) => JSX | null
|
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
A function returning `JSX` to render `custom` UI in the top right corner of the app.
|
A function returning `JSX` to render `custom` UI in the top right corner of the app.
|
||||||
@ -74,6 +73,7 @@ You can render `custom sidebar` using this prop. This sidebar is the same that t
|
|||||||
You need to import the `Sidebar` component from `excalidraw` package and pass your content as its `children`. The function `renderSidebar` should return the `Sidebar` instance.
|
You need to import the `Sidebar` component from `excalidraw` package and pass your content as its `children`. The function `renderSidebar` should return the `Sidebar` instance.
|
||||||
|
|
||||||
### Sidebar
|
### Sidebar
|
||||||
|
|
||||||
The `<Sidebar>` component takes these props (all are optional except `children`):
|
The `<Sidebar>` component takes these props (all are optional except `children`):
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
@ -84,9 +84,7 @@ The `<Sidebar>` component takes these props (all are optional except `children`)
|
|||||||
| `docked` | `boolean` | Indicates whether the sidebar is`docked`. By default, the sidebar is `undocked`. If passed, the docking becomes controlled, and you are responsible for updating the `docked` state by listening on `onDock` callback. To decide the breakpoint for docking you can use [UIOptions.dockedSidebarBreakpoint](/docs/@excalidraw/excalidraw/api/props/ui-options#dockedsidebarbreakpoint) for more info on docking. |
|
| `docked` | `boolean` | Indicates whether the sidebar is`docked`. By default, the sidebar is `undocked`. If passed, the docking becomes controlled, and you are responsible for updating the `docked` state by listening on `onDock` callback. To decide the breakpoint for docking you can use [UIOptions.dockedSidebarBreakpoint](/docs/@excalidraw/excalidraw/api/props/ui-options#dockedsidebarbreakpoint) for more info on docking. |
|
||||||
| `dockable` | `boolean` | Indicates whether to show the `dock` button so that user can `dock` the sidebar. If `false`, you can still dock programmatically by passing `docked` as `true`. |
|
| `dockable` | `boolean` | Indicates whether to show the `dock` button so that user can `dock` the sidebar. If `false`, you can still dock programmatically by passing `docked` as `true`. |
|
||||||
|
|
||||||
The sidebar will always include a header with `close / dock` buttons (when applicable).
|
The sidebar will always include a header with `close / dock` buttons (when applicable). You can also add custom content to the header, by rendering `<Sidebar.Header>` as a child of the `<Sidebar>` component. Note that the custom header will still include the default buttons.
|
||||||
You can also add custom content to the header, by rendering `<Sidebar.Header>` as a child of the `<Sidebar>` component. Note that the custom header will still include the default buttons.
|
|
||||||
|
|
||||||
|
|
||||||
### Sidebar.Header
|
### Sidebar.Header
|
||||||
|
|
||||||
@ -102,7 +100,10 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: "500px" }}>
|
<div style={{ height: "500px" }}>
|
||||||
<button className="custom-button" onClick={() => excalidrawAPI.toggleMenu("customSidebar")}>
|
<button
|
||||||
|
className="custom-button"
|
||||||
|
onClick={() => excalidrawAPI.toggleMenu("customSidebar")}
|
||||||
|
>
|
||||||
Toggle Custom Sidebar
|
Toggle Custom Sidebar
|
||||||
</button>
|
</button>
|
||||||
<Excalidraw
|
<Excalidraw
|
||||||
@ -125,7 +126,11 @@ function App() {
|
|||||||
## renderEmbeddable
|
## renderEmbeddable
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
(element: NonDeleted<ExcalidrawEmbeddableElement>, appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>) => JSX.Element | null
|
(element: NonDeleted<ExcalidrawEmbeddableElement>, appState:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||||
|
AppState
|
||||||
|
</a>
|
||||||
|
) => JSX.Element | null
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Allows you to replace the renderer for embeddable elements (which renders `<iframe>` elements).
|
Allows you to replace the renderer for embeddable elements (which renders `<iframe>` elements).
|
||||||
|
@ -65,7 +65,7 @@ If user choses to `dock` the sidebar, it will push the right part of the UI towa
|
|||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div style={{ height: "500px" }}>
|
<div style={{ height: "500px" }}>
|
||||||
<Excalidraw UIOptions={{dockedSidebarBreakpoint: 200}}/>
|
<Excalidraw UIOptions={{ dockedSidebarBreakpoint: 200 }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,25 +14,33 @@ We're working on much improved export utilities. Stay tuned!
|
|||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
exportToCanvas({<br/>
|
exportToCanvas({
|
||||||
elements,<br/>
|
<br />
|
||||||
appState<br/>
|
elements,
|
||||||
getDimensions,<br/>
|
<br />
|
||||||
files,<br/>
|
appState
|
||||||
exportPadding?: number;<br/>
|
<br />
|
||||||
}: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L21">ExportOpts</a>
|
getDimensions,
|
||||||
|
<br />
|
||||||
|
files,
|
||||||
|
<br />
|
||||||
|
exportPadding?: number;
|
||||||
|
<br />
|
||||||
|
}:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L21">
|
||||||
|
ExportOpts
|
||||||
|
</a>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `elements` | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | | The elements to be exported to canvas. |
|
| `elements` | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | | The elements to be exported to canvas. |
|
||||||
| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L23) | [Default App State](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L17) | The app state of the scene. |
|
| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L23) | [Default App State](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L17) | The app state of the scene. |
|
||||||
| [`getDimensions`](#getdimensions) | `function` | _ | A function which returns the `width`, `height`, and optionally `scale` (defaults to `1`), with which canvas is to be exported. |
|
| [`getDimensions`](#getdimensions) | `function` | \_ | A function which returns the `width`, `height`, and optionally `scale` (defaults to `1`), with which canvas is to be exported. |
|
||||||
| `maxWidthOrHeight` | `number` | _ | The maximum `width` or `height` of the exported image. If provided, `getDimensions` is ignored. |
|
| `maxWidthOrHeight` | `number` | \_ | The maximum `width` or `height` of the exported image. If provided, `getDimensions` is ignored. |
|
||||||
| `files` | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59) | _ | The files added to the scene. |
|
| `files` | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59) | \_ | The files added to the scene. |
|
||||||
| `exportPadding` | `number` | `10` | The `padding` to be added on canvas. |
|
| `exportPadding` | `number` | `10` | The `padding` to be added on canvas. |
|
||||||
|
|
||||||
|
|
||||||
#### getDimensions
|
#### getDimensions
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
@ -42,7 +50,8 @@ exportToCanvas({<br/>
|
|||||||
scale?: number
|
scale?: number
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
A function which returns the `width`, `height`, and optionally `scale` (defaults to `1`), with which canvas is to be exported.
|
|
||||||
|
A function which returns the `width`, `height`, and optionally `scale` (defaults to `1`), with which canvas is to be exported.
|
||||||
|
|
||||||
**How to use**
|
**How to use**
|
||||||
|
|
||||||
@ -57,17 +66,17 @@ function App() {
|
|||||||
const [canvasUrl, setCanvasUrl] = useState("");
|
const [canvasUrl, setCanvasUrl] = useState("");
|
||||||
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className="custom-button"
|
className="custom-button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (!excalidrawAPI) {
|
if (!excalidrawAPI) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const elements = excalidrawAPI.getSceneElements();
|
const elements = excalidrawAPI.getSceneElements();
|
||||||
if (!elements || !elements.length) {
|
if (!elements || !elements.length) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const canvas = await exportToCanvas({
|
const canvas = await exportToCanvas({
|
||||||
elements,
|
elements,
|
||||||
@ -76,7 +85,9 @@ function App() {
|
|||||||
exportWithDarkMode: false,
|
exportWithDarkMode: false,
|
||||||
},
|
},
|
||||||
files: excalidrawAPI.getFiles(),
|
files: excalidrawAPI.getFiles(),
|
||||||
getDimensions: () => { return {width: 350, height: 350}}
|
getDimensions: () => {
|
||||||
|
return { width: 350, height: 350 };
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d");
|
||||||
ctx.font = "30px Virgil";
|
ctx.font = "30px Virgil";
|
||||||
@ -90,31 +101,38 @@ function App() {
|
|||||||
<img src={canvasUrl} alt="" />
|
<img src={canvasUrl} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: "400px" }}>
|
<div style={{ height: "400px" }}>
|
||||||
<Excalidraw ref={(api) => setExcalidrawAPI(api)}
|
<Excalidraw ref={(api) => setExcalidrawAPI(api)} />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### exportToBlob
|
### exportToBlob
|
||||||
|
|
||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
exportToBlob(<br/>
|
exportToBlob(
|
||||||
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & {<br/>
|
<br />
|
||||||
mimeType?: string,<br/>
|
opts:{" "}
|
||||||
quality?: number,<br/>
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">
|
||||||
exportPadding?: number;<br/>
|
ExportOpts
|
||||||
})
|
</a>{" "}
|
||||||
|
& {
|
||||||
|
<br />
|
||||||
|
mimeType?: string,
|
||||||
|
<br />
|
||||||
|
quality?: number,
|
||||||
|
<br />
|
||||||
|
exportPadding?: number;
|
||||||
|
<br />
|
||||||
|
})
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `opts` | `object` | _ | This param is passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exporttocanvas) |
|
| `opts` | `object` | \_ | This param is passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exporttocanvas) |
|
||||||
| `mimeType` | `string` | `image/png` | Indicates the image format. |
|
| `mimeType` | `string` | `image/png` | Indicates the image format. |
|
||||||
| `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. |
|
| `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. |
|
||||||
| `exportPadding` | `number` | `10` | The padding to be added on canvas. |
|
| `exportPadding` | `number` | `10` | The padding to be added on canvas. |
|
||||||
@ -132,26 +150,31 @@ Returns a promise which resolves with a [blob](https://developer.mozilla.org/en-
|
|||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
exportToSvg({<br/>
|
exportToSvg({
|
||||||
elements:
|
<br />
|
||||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
elements:
|
||||||
ExcalidrawElement[]
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
</a>,<br/>
|
ExcalidrawElement[]
|
||||||
appState:
|
</a>,<br />
|
||||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95"> AppState
|
appState:
|
||||||
</a>,<br/>
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||||
exportPadding: number,<br/>
|
{" "}
|
||||||
metadata: string,<br/>
|
AppState
|
||||||
files:
|
</a>,<br />
|
||||||
|
exportPadding: number,
|
||||||
|
<br />
|
||||||
|
metadata: string,
|
||||||
|
<br />
|
||||||
|
files:
|
||||||
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59">
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L59">
|
||||||
BinaryFiles
|
BinaryFiles
|
||||||
</a>,<br/>
|
</a>,<br />
|
||||||
});
|
});
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| elements | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | | The elements to exported as `svg `|
|
| elements | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114) | | The elements to exported as `svg ` |
|
||||||
| appState | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) | [defaultAppState](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L11) | The `appState` of the scene |
|
| appState | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) | [defaultAppState](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L11) | The `appState` of the scene |
|
||||||
| exportPadding | number | 10 | The `padding` to be added on canvas |
|
| exportPadding | number | 10 | The `padding` to be added on canvas |
|
||||||
| files | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L64) | undefined | The `files` added to the scene. |
|
| files | [BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L64) | undefined | The `files` added to the scene. |
|
||||||
@ -163,12 +186,21 @@ This function returns a promise which resolves to `svg` of the exported drawing.
|
|||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
exportToClipboard(<br/>
|
exportToClipboard(
|
||||||
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L21">ExportOpts</a> & {<br/>
|
<br />
|
||||||
mimeType?: string,<br/>
|
opts:{" "}
|
||||||
quality?: number;<br/>
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L21">
|
||||||
type: 'png' | 'svg' |'json'<br/>
|
ExportOpts
|
||||||
})
|
</a>{" "}
|
||||||
|
& {
|
||||||
|
<br />
|
||||||
|
mimeType?: string,
|
||||||
|
<br />
|
||||||
|
quality?: number;
|
||||||
|
<br />
|
||||||
|
type: 'png' | 'svg' |'json'
|
||||||
|
<br />
|
||||||
|
})
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
@ -176,7 +208,7 @@ exportToClipboard(<br/>
|
|||||||
| `opts` | | | This param is same as the params passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exporttocanvas). |
|
| `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`. |
|
| `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`. |
|
| `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`. |
|
| `type` | 'png' | 'svg' | 'json' | \_ | This determines the format to which the scene data should be `exported`. |
|
||||||
|
|
||||||
**How to use**
|
**How to use**
|
||||||
|
|
||||||
|
@ -8,7 +8,15 @@ id: "restore"
|
|||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
restoreAppState(appState: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">ImportedDataState["appState"]</a>,<br/> localAppState: Partial<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>> | null): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>
|
restoreAppState(appState:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">
|
||||||
|
ImportedDataState["appState"]
|
||||||
|
</a>
|
||||||
|
,<br />
|
||||||
|
localAppState: Partial<
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||||
|
AppState
|
||||||
|
</a>> | null): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
**_How to use_**
|
**_How to use_**
|
||||||
@ -20,26 +28,36 @@ import { restoreAppState } from "@excalidraw/excalidraw";
|
|||||||
This function will make sure all the `keys` have appropriate `values` in [appState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) and if any key is missing, it will be set to its `default` value.
|
This function will make sure all the `keys` have appropriate `values` in [appState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95) and if any key is missing, it will be set to its `default` value.
|
||||||
|
|
||||||
When `localAppState` is supplied, it's used in place of values that are missing (`undefined`) in `appState` instead of the defaults.
|
When `localAppState` is supplied, it's used in place of values that are missing (`undefined`) in `appState` instead of the defaults.
|
||||||
Use this as a way to not override user's defaults if you persist them.
|
Use this as a way to not override user's defaults if you persist them. You can pass `null` / `undefined` if not applicable.
|
||||||
You can pass `null` / `undefined` if not applicable.
|
|
||||||
|
|
||||||
### restoreElements
|
### restoreElements
|
||||||
|
|
||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
restoreElements(
|
restoreElements( elements:{" "}
|
||||||
elements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ImportedDataState["elements"]</a>,<br/>
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
localElements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null | undefined): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a>,<br/>
|
ImportedDataState["elements"]
|
||||||
opts: { refreshDimensions?: boolean, repairBindings?: boolean }<br/>
|
</a>
|
||||||
)
|
,<br />
|
||||||
|
localElements:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
|
ExcalidrawElement[]
|
||||||
|
</a>{" "}
|
||||||
|
| null | undefined):{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
|
ExcalidrawElement[]
|
||||||
|
</a>
|
||||||
|
,<br />
|
||||||
|
opts: { refreshDimensions?: boolean, repairBindings?: boolean }
|
||||||
|
<br />)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
| Prop | Type | Description |
|
| Prop | Type | Description |
|
||||||
| ---- | ---- | ---- |
|
| --- | --- | --- |
|
||||||
| `elements` | <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ImportedDataState["elements"]</a> | The `elements` to be restored |
|
| `elements` | <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ImportedDataState["elements"]</a> | The `elements` to be restored |
|
||||||
| [`localElements`](#localelements) | <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null | undefined | When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. |
|
| [`localElements`](#localelements) | <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null | undefined | When `localElements` are supplied, they are used to ensure that existing restored elements reuse `version` (and increment it), and regenerate `versionNonce`. |
|
||||||
| [`opts`](#opts) | `Object` | The extra optional parameter to configure restored elements
|
| [`opts`](#opts) | `Object` | The extra optional parameter to configure restored elements |
|
||||||
|
|
||||||
#### localElements
|
#### localElements
|
||||||
|
|
||||||
@ -47,12 +65,13 @@ When `localElements` are supplied, they are used to ensure that existing restore
|
|||||||
Use this when you `import` elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the update
|
Use this when you `import` elements which may already be present in the scene to ensure that you do not disregard the newly imported elements if you're using element version to detect the update
|
||||||
|
|
||||||
#### opts
|
#### opts
|
||||||
|
|
||||||
The extra optional parameter to configure restored elements. It has the following attributes
|
The extra optional parameter to configure restored elements. It has the following attributes
|
||||||
|
|
||||||
| Prop | Type | Description|
|
| Prop | Type | Description |
|
||||||
| --- | --- | ------|
|
| --- | --- | --- |
|
||||||
| `refreshDimensions` | `boolean` | Indicates whether we should also `recalculate` text element dimensions. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration. |
|
| `refreshDimensions` | `boolean` | Indicates whether we should also `recalculate` text element dimensions. Since this is a potentially costly operation, you may want to disable it if you restore elements in tight loops, such as during collaboration. |
|
||||||
| `repairBindings` |`boolean` | Indicates whether the `bindings` for the elements should be repaired. This is to make sure there are no containers with non existent bound text element id and no bound text elements with non existent container id. |
|
| `repairBindings` | `boolean` | Indicates whether the `bindings` for the elements should be repaired. This is to make sure there are no containers with non existent bound text element id and no bound text elements with non existent container id. |
|
||||||
|
|
||||||
**_How to use_**
|
**_How to use_**
|
||||||
|
|
||||||
@ -69,13 +88,29 @@ Parameter `refreshDimensions` indicates whether we should also `recalculate` tex
|
|||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
restore(
|
restore( data:{" "}
|
||||||
data: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">ImportedDataState</a>,<br/>
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">
|
||||||
localAppState: Partial<<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">AppState</a>> | null | undefined,<br/>
|
ImportedDataState
|
||||||
localElements: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">ExcalidrawElement[]</a> | null | undefined<br/>): <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L4">DataState</a><br/>
|
</a>
|
||||||
opts: { refreshDimensions?: boolean, repairBindings?: boolean }<br/>
|
,<br />
|
||||||
|
localAppState: Partial<
|
||||||
)
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L95">
|
||||||
|
AppState
|
||||||
|
</a>
|
||||||
|
> | null | undefined,
|
||||||
|
<br />
|
||||||
|
localElements:{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L114">
|
||||||
|
ExcalidrawElement[]
|
||||||
|
</a>{" "}
|
||||||
|
| null | undefined
|
||||||
|
<br />
|
||||||
|
):{" "}
|
||||||
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L4">
|
||||||
|
DataState
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
opts: { refreshDimensions?: boolean, repairBindings?: boolean }<br />)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
See [`restoreAppState()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreAppState) about `localAppState`, and [`restoreElements()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreElements) about `localElements`.
|
See [`restoreAppState()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreAppState) about `localAppState`, and [`restoreElements()`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreElements) about `localElements`.
|
||||||
@ -93,8 +128,12 @@ This function makes sure elements and state is set to appropriate values and set
|
|||||||
**_Signature_**
|
**_Signature_**
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
restoreLibraryItems(libraryItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">ImportedDataState["libraryItems"]</a>,<br/>
|
restoreLibraryItems(libraryItems:{" "}
|
||||||
defaultStatus: "published" | "unpublished")
|
<a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L34">
|
||||||
|
ImportedDataState["libraryItems"]
|
||||||
|
</a>
|
||||||
|
,<br />
|
||||||
|
defaultStatus: "published" | "unpublished")
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
**_How to use_**
|
**_How to use_**
|
||||||
|
@ -38,6 +38,7 @@ For a complete list of variables, check [theme.scss](https://github.com/excalidr
|
|||||||
--color-primary-light: #dcbec9;
|
--color-primary-light: #dcbec9;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```tsx live
|
```tsx live
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -6,13 +6,11 @@ No, Excalidraw package doesn't come with collaboration built in, since the imple
|
|||||||
|
|
||||||
### Turning off Aggressive Anti-Fingerprinting in Brave browser
|
### Turning off Aggressive Anti-Fingerprinting in Brave browser
|
||||||
|
|
||||||
When *Aggressive Anti-Fingerprinting* is turned on, the `measureText` API breaks which in turn breaks the Text Elements in your drawings. Here is more [info](https://github.com/excalidraw/excalidraw/pull/6336) on the same.
|
When _Aggressive Anti-Fingerprinting_ is turned on, the `measureText` API breaks which in turn breaks the Text Elements in your drawings. Here is more [info](https://github.com/excalidraw/excalidraw/pull/6336) on the same.
|
||||||
|
|
||||||
We strongly recommend turning it off. You can follow the steps below on how to do so.
|
We strongly recommend turning it off. You can follow the steps below on how to do so.
|
||||||
|
|
||||||
|
1. Open [excalidraw.com](https://excalidraw.com) in Brave and click on the **Shield** button 
|
||||||
1. Open [excalidraw.com](https://excalidraw.com) in Brave and click on the **Shield** button
|
|
||||||

|
|
||||||
|
|
||||||
<div style={{width:'30rem'}}>
|
<div style={{width:'30rem'}}>
|
||||||
|
|
||||||
@ -30,8 +28,6 @@ We strongly recommend turning it off. You can follow the steps below on how to d
|
|||||||
|
|
||||||
If disabling this setting doesn't fix the display of text elements, please consider opening an [issue](https://github.com/excalidraw/excalidraw/issues/new) on our GitHub, or message us on [Discord](https://discord.gg/UexuTaE).
|
If disabling this setting doesn't fix the display of text elements, please consider opening an [issue](https://github.com/excalidraw/excalidraw/issues/new) on our GitHub, or message us on [Discord](https://discord.gg/UexuTaE).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Need help?
|
## Need help?
|
||||||
|
|
||||||
Check out the existing [Q&A](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw). If you have any queries or need help, ask us [here](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw).
|
Check out the existing [Q&A](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw). If you have any queries or need help, ask us [here](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw).
|
||||||
|
@ -41,7 +41,9 @@ import { useState, useEffect } from "react";
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
const [Excalidraw, setExcalidraw] = useState(null);
|
const [Excalidraw, setExcalidraw] = useState(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
import("@excalidraw/excalidraw").then((comp) => setExcalidraw(comp.Excalidraw));
|
import("@excalidraw/excalidraw").then((comp) =>
|
||||||
|
setExcalidraw(comp.Excalidraw),
|
||||||
|
);
|
||||||
}, []);
|
}, []);
|
||||||
return <>{Excalidraw && <Excalidraw />}</>;
|
return <>{Excalidraw && <Excalidraw />}</>;
|
||||||
}
|
}
|
||||||
@ -80,7 +82,7 @@ import TabItem from "@theme/TabItem";
|
|||||||
<TabItem value="html" label="html">
|
<TabItem value="html" label="html">
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Excalidraw in browser</title>
|
<title>Excalidraw in browser</title>
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
Pull requests are welcome. For major changes, please [open an issue](https://github.com/excalidraw/excalidraw/issues/new) first to discuss what you would like to change.
|
Pull requests are welcome. For major changes, please [open an issue](https://github.com/excalidraw/excalidraw/issues/new) first to discuss what you would like to change.
|
||||||
|
|
||||||
We have a [roadmap](https://github.com/orgs/excalidraw/projects/3) which we strongly recommend to go through and check if something interests you.
|
We have a [roadmap](https://github.com/orgs/excalidraw/projects/3) which we strongly recommend to go through and check if something interests you. For new contributors we would recommend to start with _Easy_ tasks.
|
||||||
For new contributors we would recommend to start with *Easy* tasks.
|
|
||||||
|
|
||||||
In case you want to pick up something from the roadmap, comment on that issue and one of the project maintainers will assign it to you, post which you can discuss in the issue and start working on it.
|
In case you want to pick up something from the roadmap, comment on that issue and one of the project maintainers will assign it to you, post which you can discuss in the issue and start working on it.
|
||||||
|
|
||||||
@ -69,10 +68,7 @@ It's also a good idea to consider if your change should include additional tests
|
|||||||
|
|
||||||
Finally - always manually test your changes using the convenient staging environment deployed for each pull request. As much as local development attempts to replicate production, there can still be subtle differences in behavior. For larger features consider testing your change in multiple browsers as well.
|
Finally - always manually test your changes using the convenient staging environment deployed for each pull request. As much as local development attempts to replicate production, there can still be subtle differences in behavior. For larger features consider testing your change in multiple browsers as well.
|
||||||
|
|
||||||
:::note
|
:::note Some checks, such as the `lint` and `test`, require approval from the maintainers to run. They will appear as `Expected — Waiting for status to be reported` in the PR checks when they are waiting for approval. :::
|
||||||
Some checks, such as the `lint` and `test`, require approval from the maintainers to run.
|
|
||||||
They will appear as `Expected — Waiting for status to be reported` in the PR checks when they are waiting for approval.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Translating
|
## Translating
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
15
package.json
15
package.json
@ -32,7 +32,6 @@
|
|||||||
"canvas-roundrect-polyfill": "0.0.1",
|
"canvas-roundrect-polyfill": "0.0.1",
|
||||||
"clsx": "1.1.1",
|
"clsx": "1.1.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint-plugin-react": "7.32.2",
|
|
||||||
"fake-indexeddb": "3.1.7",
|
"fake-indexeddb": "3.1.7",
|
||||||
"firebase": "8.3.3",
|
"firebase": "8.3.3",
|
||||||
"i18next-browser-languagedetector": "6.1.4",
|
"i18next-browser-languagedetector": "6.1.4",
|
||||||
@ -58,7 +57,6 @@
|
|||||||
"tunnel-rat": "0.1.2"
|
"tunnel-rat": "0.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@excalidraw/eslint-config": "1.0.3",
|
|
||||||
"@excalidraw/prettier-config": "1.0.2",
|
"@excalidraw/prettier-config": "1.0.2",
|
||||||
"@types/chai": "4.3.0",
|
"@types/chai": "4.3.0",
|
||||||
"@types/jest": "27.4.0",
|
"@types/jest": "27.4.0",
|
||||||
@ -69,20 +67,23 @@
|
|||||||
"@types/react-dom": "18.0.6",
|
"@types/react-dom": "18.0.6",
|
||||||
"@types/resize-observer-browser": "0.1.7",
|
"@types/resize-observer-browser": "0.1.7",
|
||||||
"@types/socket.io-client": "1.4.36",
|
"@types/socket.io-client": "1.4.36",
|
||||||
|
"@typescript-eslint/eslint-plugin": "6.5.0",
|
||||||
|
"@typescript-eslint/parser": "6.5.0",
|
||||||
"@vitejs/plugin-react": "3.1.0",
|
"@vitejs/plugin-react": "3.1.0",
|
||||||
"@vitest/coverage-v8": "0.33.0",
|
"@vitest/coverage-v8": "0.33.0",
|
||||||
"@vitest/ui": "0.32.2",
|
"@vitest/ui": "0.32.2",
|
||||||
"chai": "4.3.6",
|
"chai": "4.3.6",
|
||||||
"dotenv": "16.0.1",
|
"dotenv": "16.0.1",
|
||||||
"eslint-config-prettier": "8.5.0",
|
"eslint": "8.48.0",
|
||||||
"eslint-config-react-app": "7.0.1",
|
"eslint-config-prettier": "9.0.0",
|
||||||
"eslint-plugin-prettier": "3.3.1",
|
"eslint-plugin-react": "7.33.2",
|
||||||
|
"eslint-plugin-react-hooks": "4.6.0",
|
||||||
"http-server": "14.1.1",
|
"http-server": "14.1.1",
|
||||||
"husky": "7.0.4",
|
"husky": "7.0.4",
|
||||||
"jsdom": "22.1.0",
|
"jsdom": "22.1.0",
|
||||||
"lint-staged": "12.3.7",
|
"lint-staged": "12.3.7",
|
||||||
"pepjs": "0.5.3",
|
"pepjs": "0.5.3",
|
||||||
"prettier": "2.6.2",
|
"prettier": "3.0.3",
|
||||||
"rewire": "6.0.0",
|
"rewire": "6.0.0",
|
||||||
"typescript": "4.9.4",
|
"typescript": "4.9.4",
|
||||||
"vite": "4.4.2",
|
"vite": "4.4.2",
|
||||||
@ -112,7 +113,7 @@
|
|||||||
"locales-coverage": "node scripts/build-locales-coverage.js",
|
"locales-coverage": "node scripts/build-locales-coverage.js",
|
||||||
"locales-coverage:description": "node scripts/locales-coverage-description.js",
|
"locales-coverage:description": "node scripts/locales-coverage-description.js",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
|
"prettier": "prettier . --ignore-path=.eslintignore",
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"start:production": "npm run build && npx http-server build -a localhost -p 5001 -o",
|
"start:production": "npm run build && npx http-server build -a localhost -p 5001 -o",
|
||||||
"test:all": "yarn test:typecheck && yarn test:code && yarn test:other && yarn test:app --watch=false",
|
"test:all": "yarn test:typecheck && yarn test:code && yarn test:other && yarn test:app --watch=false",
|
||||||
|
@ -29,10 +29,13 @@ export const actionSelectAllElementsInFrame = register({
|
|||||||
elements,
|
elements,
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
selectedElementIds: elementsInFrame.reduce((acc, element) => {
|
selectedElementIds: elementsInFrame.reduce(
|
||||||
acc[element.id] = true;
|
(acc, element) => {
|
||||||
return acc;
|
acc[element.id] = true;
|
||||||
}, {} as Record<ExcalidrawElement["id"], true>),
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<ExcalidrawElement["id"], true>,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
commitToHistory: false,
|
commitToHistory: false,
|
||||||
};
|
};
|
||||||
|
@ -215,11 +215,11 @@ const _clearAppStateForStorage = <
|
|||||||
exportType: ExportType,
|
exportType: ExportType,
|
||||||
) => {
|
) => {
|
||||||
type ExportableKeys = {
|
type ExportableKeys = {
|
||||||
[K in keyof typeof APP_STATE_STORAGE_CONF]: typeof APP_STATE_STORAGE_CONF[K][ExportType] extends true
|
[K in keyof typeof APP_STATE_STORAGE_CONF]: (typeof APP_STATE_STORAGE_CONF)[K][ExportType] extends true
|
||||||
? K
|
? K
|
||||||
: never;
|
: never;
|
||||||
}[keyof typeof APP_STATE_STORAGE_CONF];
|
}[keyof typeof APP_STATE_STORAGE_CONF];
|
||||||
const stateForExport = {} as { [K in ExportableKeys]?: typeof appState[K] };
|
const stateForExport = {} as { [K in ExportableKeys]?: (typeof appState)[K] };
|
||||||
for (const key of Object.keys(appState) as (keyof typeof appState)[]) {
|
for (const key of Object.keys(appState) as (keyof typeof appState)[]) {
|
||||||
const propConfig = APP_STATE_STORAGE_CONF[key];
|
const propConfig = APP_STATE_STORAGE_CONF[key];
|
||||||
if (propConfig?.[exportType]) {
|
if (propConfig?.[exportType]) {
|
||||||
|
@ -6,12 +6,15 @@ const pick = <R extends Record<string, any>, K extends readonly (keyof R)[]>(
|
|||||||
source: R,
|
source: R,
|
||||||
keys: K,
|
keys: K,
|
||||||
) => {
|
) => {
|
||||||
return keys.reduce((acc, key: K[number]) => {
|
return keys.reduce(
|
||||||
if (key in source) {
|
(acc, key: K[number]) => {
|
||||||
acc[key] = source[key];
|
if (key in source) {
|
||||||
}
|
acc[key] = source[key];
|
||||||
return acc;
|
}
|
||||||
}, {} as Pick<R, K[number]>) as Pick<R, K[number]>;
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Pick<R, K[number]>,
|
||||||
|
) as Pick<R, K[number]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ColorPickerColor =
|
export type ColorPickerColor =
|
||||||
|
@ -3080,7 +3080,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
tool:
|
tool:
|
||||||
| {
|
| {
|
||||||
type:
|
type:
|
||||||
| typeof SHAPES[number]["value"]
|
| (typeof SHAPES)[number]["value"]
|
||||||
| "eraser"
|
| "eraser"
|
||||||
| "hand"
|
| "hand"
|
||||||
| "frame"
|
| "frame"
|
||||||
@ -4770,12 +4770,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
),
|
),
|
||||||
// we need to duplicate because we'll be updating this state
|
// we need to duplicate because we'll be updating this state
|
||||||
lastCoords: { ...origin },
|
lastCoords: { ...origin },
|
||||||
originalElements: this.scene
|
originalElements: this.scene.getNonDeletedElements().reduce(
|
||||||
.getNonDeletedElements()
|
(acc, element) => {
|
||||||
.reduce((acc, element) => {
|
|
||||||
acc.set(element.id, deepCopyElement(element));
|
acc.set(element.id, deepCopyElement(element));
|
||||||
return acc;
|
return acc;
|
||||||
}, new Map() as PointerDownState["originalElements"]),
|
},
|
||||||
|
new Map() as PointerDownState["originalElements"],
|
||||||
|
),
|
||||||
resize: {
|
resize: {
|
||||||
handleType: false,
|
handleType: false,
|
||||||
isResizing: false,
|
isResizing: false,
|
||||||
|
@ -107,7 +107,8 @@
|
|||||||
|
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
background-color: var(--button-color);
|
background-color: var(--button-color);
|
||||||
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.28),
|
box-shadow:
|
||||||
|
0 3px 5px -1px rgba(0, 0, 0, 0.28),
|
||||||
0 6px 10px 0 rgba(0, 0, 0, 0.14);
|
0 6px 10px 0 rgba(0, 0, 0, 0.14);
|
||||||
|
|
||||||
font-family: Cascadia;
|
font-family: Cascadia;
|
||||||
|
@ -70,12 +70,16 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: visibility 0s linear 0s, opacity 0.5s;
|
transition:
|
||||||
|
visibility 0s linear 0s,
|
||||||
|
opacity 0.5s;
|
||||||
|
|
||||||
&--visible {
|
&--visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transition: visibility 0s linear 300ms, opacity 0.5s;
|
transition:
|
||||||
|
visibility 0s linear 300ms,
|
||||||
|
opacity 0.5s;
|
||||||
transition-delay: 0.8s;
|
transition-delay: 0.8s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,13 +391,19 @@
|
|||||||
appearance: none;
|
appearance: none;
|
||||||
background-image: var(--dropdown-icon);
|
background-image: var(--dropdown-icon);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right 0.7rem top 50%, 0 0;
|
background-position:
|
||||||
|
right 0.7rem top 50%,
|
||||||
|
0 0;
|
||||||
|
|
||||||
:root[dir="rtl"] & {
|
:root[dir="rtl"] & {
|
||||||
background-position: left 0.7rem top 50%, 0 0;
|
background-position:
|
||||||
|
left 0.7rem top 50%,
|
||||||
|
0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
background-size: 0.65em auto, 100%;
|
background-size:
|
||||||
|
0.65em auto,
|
||||||
|
100%;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 0 2px var(--focus-highlight-color);
|
box-shadow: 0 0 0 2px var(--focus-highlight-color);
|
||||||
|
@ -273,7 +273,7 @@ export const resizeImageFile = async (
|
|||||||
file: File,
|
file: File,
|
||||||
opts: {
|
opts: {
|
||||||
/** undefined indicates auto */
|
/** undefined indicates auto */
|
||||||
outputType?: typeof MIME_TYPES["jpg"];
|
outputType?: (typeof MIME_TYPES)["jpg"];
|
||||||
maxWidthOrHeight: number;
|
maxWidthOrHeight: number;
|
||||||
},
|
},
|
||||||
): Promise<File> => {
|
): Promise<File> => {
|
||||||
|
@ -51,84 +51,81 @@ export type ValidLinearElement = {
|
|||||||
textAlign?: TextAlign;
|
textAlign?: TextAlign;
|
||||||
verticalAlign?: VerticalAlign;
|
verticalAlign?: VerticalAlign;
|
||||||
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
|
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
|
||||||
end?:
|
end?: (
|
||||||
| (
|
| (
|
||||||
| (
|
| {
|
||||||
| {
|
type: Exclude<
|
||||||
type: Exclude<
|
ExcalidrawBindableElement["type"],
|
||||||
ExcalidrawBindableElement["type"],
|
"image" | "text" | "frame" | "embeddable"
|
||||||
"image" | "text" | "frame" | "embeddable"
|
>;
|
||||||
>;
|
id?: ExcalidrawGenericElement["id"];
|
||||||
id?: ExcalidrawGenericElement["id"];
|
}
|
||||||
}
|
| {
|
||||||
| {
|
id: ExcalidrawGenericElement["id"];
|
||||||
id: ExcalidrawGenericElement["id"];
|
type?: Exclude<
|
||||||
type?: Exclude<
|
ExcalidrawBindableElement["type"],
|
||||||
ExcalidrawBindableElement["type"],
|
"image" | "text" | "frame" | "embeddable"
|
||||||
"image" | "text" | "frame" | "embeddable"
|
>;
|
||||||
>;
|
}
|
||||||
}
|
)
|
||||||
)
|
| ((
|
||||||
| ((
|
| {
|
||||||
| {
|
type: "text";
|
||||||
type: "text";
|
text: string;
|
||||||
text: string;
|
}
|
||||||
}
|
| {
|
||||||
| {
|
type?: "text";
|
||||||
type?: "text";
|
id: ExcalidrawTextElement["id"];
|
||||||
id: ExcalidrawTextElement["id"];
|
text: string;
|
||||||
text: string;
|
}
|
||||||
}
|
|
||||||
) &
|
|
||||||
Partial<ExcalidrawTextElement>)
|
|
||||||
) &
|
) &
|
||||||
MarkOptional<ElementConstructorOpts, "x" | "y">;
|
Partial<ExcalidrawTextElement>)
|
||||||
start?:
|
) &
|
||||||
|
MarkOptional<ElementConstructorOpts, "x" | "y">;
|
||||||
|
start?: (
|
||||||
| (
|
| (
|
||||||
| (
|
| {
|
||||||
| {
|
type: Exclude<
|
||||||
type: Exclude<
|
ExcalidrawBindableElement["type"],
|
||||||
ExcalidrawBindableElement["type"],
|
"image" | "text" | "frame" | "embeddable"
|
||||||
"image" | "text" | "frame" | "embeddable"
|
>;
|
||||||
>;
|
id?: ExcalidrawGenericElement["id"];
|
||||||
id?: ExcalidrawGenericElement["id"];
|
}
|
||||||
}
|
| {
|
||||||
| {
|
id: ExcalidrawGenericElement["id"];
|
||||||
id: ExcalidrawGenericElement["id"];
|
type?: Exclude<
|
||||||
type?: Exclude<
|
ExcalidrawBindableElement["type"],
|
||||||
ExcalidrawBindableElement["type"],
|
"image" | "text" | "frame" | "embeddable"
|
||||||
"image" | "text" | "frame" | "embeddable"
|
>;
|
||||||
>;
|
}
|
||||||
}
|
)
|
||||||
)
|
| ((
|
||||||
| ((
|
| {
|
||||||
| {
|
type: "text";
|
||||||
type: "text";
|
text: string;
|
||||||
text: string;
|
}
|
||||||
}
|
| {
|
||||||
| {
|
type?: "text";
|
||||||
type?: "text";
|
id: ExcalidrawTextElement["id"];
|
||||||
id: ExcalidrawTextElement["id"];
|
text: string;
|
||||||
text: string;
|
}
|
||||||
}
|
|
||||||
) &
|
|
||||||
Partial<ExcalidrawTextElement>)
|
|
||||||
) &
|
) &
|
||||||
MarkOptional<ElementConstructorOpts, "x" | "y">;
|
Partial<ExcalidrawTextElement>)
|
||||||
|
) &
|
||||||
|
MarkOptional<ElementConstructorOpts, "x" | "y">;
|
||||||
} & Partial<ExcalidrawLinearElement>;
|
} & Partial<ExcalidrawLinearElement>;
|
||||||
|
|
||||||
export type ValidContainer =
|
export type ValidContainer = {
|
||||||
| {
|
type: Exclude<ExcalidrawGenericElement["type"], "selection">;
|
||||||
type: Exclude<ExcalidrawGenericElement["type"], "selection">;
|
id?: ExcalidrawGenericElement["id"];
|
||||||
id?: ExcalidrawGenericElement["id"];
|
label?: {
|
||||||
label?: {
|
text: string;
|
||||||
text: string;
|
fontSize?: number;
|
||||||
fontSize?: number;
|
fontFamily?: FontFamilyValues;
|
||||||
fontFamily?: FontFamilyValues;
|
textAlign?: TextAlign;
|
||||||
textAlign?: TextAlign;
|
verticalAlign?: VerticalAlign;
|
||||||
verticalAlign?: VerticalAlign;
|
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
|
||||||
} & MarkOptional<ElementConstructorOpts, "x" | "y">;
|
} & ElementConstructorOpts;
|
||||||
} & ElementConstructorOpts;
|
|
||||||
|
|
||||||
export type ExcalidrawElementSkeleton =
|
export type ExcalidrawElementSkeleton =
|
||||||
| Extract<
|
| Extract<
|
||||||
|
@ -31,7 +31,7 @@ const _ce = ({
|
|||||||
width: w,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
angle: a,
|
angle: a,
|
||||||
} as ExcalidrawElement);
|
}) as ExcalidrawElement;
|
||||||
|
|
||||||
describe("getElementAbsoluteCoords", () => {
|
describe("getElementAbsoluteCoords", () => {
|
||||||
it("test x1 coordinate", () => {
|
it("test x1 coordinate", () => {
|
||||||
|
@ -404,7 +404,7 @@ export class LinearElementEditor {
|
|||||||
static getEditorMidPoints = (
|
static getEditorMidPoints = (
|
||||||
element: NonDeleted<ExcalidrawLinearElement>,
|
element: NonDeleted<ExcalidrawLinearElement>,
|
||||||
appState: InteractiveCanvasAppState,
|
appState: InteractiveCanvasAppState,
|
||||||
): typeof editorMidPointsCache["points"] => {
|
): (typeof editorMidPointsCache)["points"] => {
|
||||||
const boundText = getBoundTextElement(element);
|
const boundText = getBoundTextElement(element);
|
||||||
|
|
||||||
// Since its not needed outside editor unless 2 pointer lines or bound text
|
// Since its not needed outside editor unless 2 pointer lines or bound text
|
||||||
@ -501,7 +501,7 @@ export class LinearElementEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const midPoints: typeof editorMidPointsCache["points"] =
|
const midPoints: (typeof editorMidPointsCache)["points"] =
|
||||||
LinearElementEditor.getEditorMidPoints(element, appState);
|
LinearElementEditor.getEditorMidPoints(element, appState);
|
||||||
while (index < midPoints.length) {
|
while (index < midPoints.length) {
|
||||||
if (midPoints[index] !== null) {
|
if (midPoints[index] !== null) {
|
||||||
@ -609,7 +609,7 @@ export class LinearElementEditor {
|
|||||||
hitElement: NonDeleted<ExcalidrawElement> | null;
|
hitElement: NonDeleted<ExcalidrawElement> | null;
|
||||||
linearElementEditor: LinearElementEditor | null;
|
linearElementEditor: LinearElementEditor | null;
|
||||||
} {
|
} {
|
||||||
const ret: ReturnType<typeof LinearElementEditor["handlePointerDown"]> = {
|
const ret: ReturnType<(typeof LinearElementEditor)["handlePointerDown"]> = {
|
||||||
didAddPoint: false,
|
didAddPoint: false,
|
||||||
hitElement: null,
|
hitElement: null,
|
||||||
linearElementEditor: null,
|
linearElementEditor: null,
|
||||||
|
@ -767,7 +767,7 @@ export const resizeMultipleElements = (
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
const update: typeof elementsAndUpdates[0]["update"] = {
|
const update: (typeof elementsAndUpdates)[0]["update"] = {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
|
@ -70,20 +70,26 @@ export const getElementWithTransformHandleType = (
|
|||||||
zoom: Zoom,
|
zoom: Zoom,
|
||||||
pointerType: PointerType,
|
pointerType: PointerType,
|
||||||
) => {
|
) => {
|
||||||
return elements.reduce((result, element) => {
|
return elements.reduce(
|
||||||
if (result) {
|
(result, element) => {
|
||||||
return result;
|
if (result) {
|
||||||
}
|
return result;
|
||||||
const transformHandleType = resizeTest(
|
}
|
||||||
element,
|
const transformHandleType = resizeTest(
|
||||||
appState,
|
element,
|
||||||
scenePointerX,
|
appState,
|
||||||
scenePointerY,
|
scenePointerX,
|
||||||
zoom,
|
scenePointerY,
|
||||||
pointerType,
|
zoom,
|
||||||
);
|
pointerType,
|
||||||
return transformHandleType ? { element, transformHandleType } : null;
|
);
|
||||||
}, null as { element: NonDeletedExcalidrawElement; transformHandleType: MaybeTransformHandleType } | null);
|
return transformHandleType ? { element, transformHandleType } : null;
|
||||||
|
},
|
||||||
|
null as {
|
||||||
|
element: NonDeletedExcalidrawElement;
|
||||||
|
transformHandleType: MaybeTransformHandleType;
|
||||||
|
} | null,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTransformHandleTypeFromCoords = (
|
export const getTransformHandleTypeFromCoords = (
|
||||||
|
@ -11,18 +11,18 @@ import { MarkNonNullable, ValueOf } from "../utility-types";
|
|||||||
export type ChartType = "bar" | "line";
|
export type ChartType = "bar" | "line";
|
||||||
export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag";
|
export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag";
|
||||||
export type FontFamilyKeys = keyof typeof FONT_FAMILY;
|
export type FontFamilyKeys = keyof typeof FONT_FAMILY;
|
||||||
export type FontFamilyValues = typeof FONT_FAMILY[FontFamilyKeys];
|
export type FontFamilyValues = (typeof FONT_FAMILY)[FontFamilyKeys];
|
||||||
export type Theme = typeof THEME[keyof typeof THEME];
|
export type Theme = (typeof THEME)[keyof typeof THEME];
|
||||||
export type FontString = string & { _brand: "fontString" };
|
export type FontString = string & { _brand: "fontString" };
|
||||||
export type GroupId = string;
|
export type GroupId = string;
|
||||||
export type PointerType = "mouse" | "pen" | "touch";
|
export type PointerType = "mouse" | "pen" | "touch";
|
||||||
export type StrokeRoundness = "round" | "sharp";
|
export type StrokeRoundness = "round" | "sharp";
|
||||||
export type RoundnessType = ValueOf<typeof ROUNDNESS>;
|
export type RoundnessType = ValueOf<typeof ROUNDNESS>;
|
||||||
export type StrokeStyle = "solid" | "dashed" | "dotted";
|
export type StrokeStyle = "solid" | "dashed" | "dotted";
|
||||||
export type TextAlign = typeof TEXT_ALIGN[keyof typeof TEXT_ALIGN];
|
export type TextAlign = (typeof TEXT_ALIGN)[keyof typeof TEXT_ALIGN];
|
||||||
|
|
||||||
type VerticalAlignKeys = keyof typeof VERTICAL_ALIGN;
|
type VerticalAlignKeys = keyof typeof VERTICAL_ALIGN;
|
||||||
export type VerticalAlign = typeof VERTICAL_ALIGN[VerticalAlignKeys];
|
export type VerticalAlign = (typeof VERTICAL_ALIGN)[VerticalAlignKeys];
|
||||||
|
|
||||||
type _ExcalidrawElementBase = Readonly<{
|
type _ExcalidrawElementBase = Readonly<{
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -104,35 +104,38 @@ class History {
|
|||||||
): DehydratedHistoryEntry =>
|
): DehydratedHistoryEntry =>
|
||||||
this.dehydrateHistoryEntry({
|
this.dehydrateHistoryEntry({
|
||||||
appState: clearAppStatePropertiesForHistory(appState),
|
appState: clearAppStatePropertiesForHistory(appState),
|
||||||
elements: elements.reduce((elements, element) => {
|
elements: elements.reduce(
|
||||||
if (
|
(elements, element) => {
|
||||||
isLinearElement(element) &&
|
|
||||||
appState.multiElement &&
|
|
||||||
appState.multiElement.id === element.id
|
|
||||||
) {
|
|
||||||
// don't store multi-point arrow if still has only one point
|
|
||||||
if (
|
if (
|
||||||
|
isLinearElement(element) &&
|
||||||
appState.multiElement &&
|
appState.multiElement &&
|
||||||
appState.multiElement.id === element.id &&
|
appState.multiElement.id === element.id
|
||||||
element.points.length < 2
|
|
||||||
) {
|
) {
|
||||||
return elements;
|
// don't store multi-point arrow if still has only one point
|
||||||
}
|
if (
|
||||||
|
appState.multiElement &&
|
||||||
|
appState.multiElement.id === element.id &&
|
||||||
|
element.points.length < 2
|
||||||
|
) {
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
elements.push({
|
elements.push({
|
||||||
...element,
|
...element,
|
||||||
// don't store last point if not committed
|
// don't store last point if not committed
|
||||||
points:
|
points:
|
||||||
element.lastCommittedPoint !==
|
element.lastCommittedPoint !==
|
||||||
element.points[element.points.length - 1]
|
element.points[element.points.length - 1]
|
||||||
? element.points.slice(0, -1)
|
? element.points.slice(0, -1)
|
||||||
: element.points,
|
: element.points,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
elements.push(element);
|
elements.push(element);
|
||||||
}
|
}
|
||||||
return elements;
|
return elements;
|
||||||
}, [] as Mutable<typeof elements>),
|
},
|
||||||
|
[] as Mutable<typeof elements>,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
shouldCreateEntry(nextEntry: HistoryEntry): boolean {
|
shouldCreateEntry(nextEntry: HistoryEntry): boolean {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
@ -36,7 +36,7 @@ const hashSelectionOpts = (
|
|||||||
type _ = Assert<
|
type _ = Assert<
|
||||||
SameType<
|
SameType<
|
||||||
Required<HashableKeys>,
|
Required<HashableKeys>,
|
||||||
Pick<Required<HashableKeys>, typeof keys[number]>
|
Pick<Required<HashableKeys>, (typeof keys)[number]>
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -167,20 +167,20 @@ export const _generateElementShape = (
|
|||||||
rightX - verticalRadius
|
rightX - verticalRadius
|
||||||
} ${rightY - horizontalRadius}
|
} ${rightY - horizontalRadius}
|
||||||
C ${rightX} ${rightY}, ${rightX} ${rightY}, ${
|
C ${rightX} ${rightY}, ${rightX} ${rightY}, ${
|
||||||
rightX - verticalRadius
|
rightX - verticalRadius
|
||||||
} ${rightY + horizontalRadius}
|
} ${rightY + horizontalRadius}
|
||||||
L ${bottomX + verticalRadius} ${bottomY - horizontalRadius}
|
L ${bottomX + verticalRadius} ${bottomY - horizontalRadius}
|
||||||
C ${bottomX} ${bottomY}, ${bottomX} ${bottomY}, ${
|
C ${bottomX} ${bottomY}, ${bottomX} ${bottomY}, ${
|
||||||
bottomX - verticalRadius
|
bottomX - verticalRadius
|
||||||
} ${bottomY - horizontalRadius}
|
} ${bottomY - horizontalRadius}
|
||||||
L ${leftX + verticalRadius} ${leftY + horizontalRadius}
|
L ${leftX + verticalRadius} ${leftY + horizontalRadius}
|
||||||
C ${leftX} ${leftY}, ${leftX} ${leftY}, ${leftX + verticalRadius} ${
|
C ${leftX} ${leftY}, ${leftX} ${leftY}, ${leftX + verticalRadius} ${
|
||||||
leftY - horizontalRadius
|
leftY - horizontalRadius
|
||||||
}
|
}
|
||||||
L ${topX - verticalRadius} ${topY + horizontalRadius}
|
L ${topX - verticalRadius} ${topY + horizontalRadius}
|
||||||
C ${topX} ${topY}, ${topX} ${topY}, ${topX + verticalRadius} ${
|
C ${topX} ${topY}, ${topX} ${topY}, ${topX + verticalRadius} ${
|
||||||
topY + horizontalRadius
|
topY + horizontalRadius
|
||||||
}`,
|
}`,
|
||||||
generateRoughOptions(element, true),
|
generateRoughOptions(element, true),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -167,8 +167,8 @@ export const exportToSvg = async (
|
|||||||
|
|
||||||
exportingFrameClipPath = `<clipPath id=${exportingFrame.id}>
|
exportingFrameClipPath = `<clipPath id=${exportingFrame.id}>
|
||||||
<rect transform="translate(${exportingFrame.x + offsetX} ${
|
<rect transform="translate(${exportingFrame.x + offsetX} ${
|
||||||
exportingFrame.y + offsetY
|
exportingFrame.y + offsetY
|
||||||
}) rotate(${exportingFrame.angle} ${cx} ${cy})"
|
}) rotate(${exportingFrame.angle} ${cx} ${cy})"
|
||||||
width="${exportingFrame.width}"
|
width="${exportingFrame.width}"
|
||||||
height="${exportingFrame.height}"
|
height="${exportingFrame.height}"
|
||||||
>
|
>
|
||||||
@ -235,10 +235,13 @@ const getCanvasSize = (
|
|||||||
if (!isExportingWholeCanvas || onlyExportingSingleFrame) {
|
if (!isExportingWholeCanvas || onlyExportingSingleFrame) {
|
||||||
const frames = elements.filter((element) => element.type === "frame");
|
const frames = elements.filter((element) => element.type === "frame");
|
||||||
|
|
||||||
const exportedFrameIds = frames.reduce((acc, frame) => {
|
const exportedFrameIds = frames.reduce(
|
||||||
acc[frame.id] = true;
|
(acc, frame) => {
|
||||||
return acc;
|
acc[frame.id] = true;
|
||||||
}, {} as Record<string, true>);
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, true>,
|
||||||
|
);
|
||||||
|
|
||||||
// elements in a frame do not affect the canvas size if we're not exporting
|
// elements in a frame do not affect the canvas size if we're not exporting
|
||||||
// the whole canvas
|
// the whole canvas
|
||||||
|
@ -34,10 +34,13 @@ const { h } = window;
|
|||||||
export class API {
|
export class API {
|
||||||
static setSelectedElements = (elements: ExcalidrawElement[]) => {
|
static setSelectedElements = (elements: ExcalidrawElement[]) => {
|
||||||
h.setState({
|
h.setState({
|
||||||
selectedElementIds: elements.reduce((acc, element) => {
|
selectedElementIds: elements.reduce(
|
||||||
acc[element.id] = true;
|
(acc, element) => {
|
||||||
return acc;
|
acc[element.id] = true;
|
||||||
}, {} as Record<ExcalidrawElement["id"], true>),
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<ExcalidrawElement["id"], true>,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ export type BinaryFiles = Record<ExcalidrawElement["id"], BinaryFileData>;
|
|||||||
export type LastActiveTool =
|
export type LastActiveTool =
|
||||||
| {
|
| {
|
||||||
type:
|
type:
|
||||||
| typeof SHAPES[number]["value"]
|
| (typeof SHAPES)[number]["value"]
|
||||||
| "eraser"
|
| "eraser"
|
||||||
| "hand"
|
| "hand"
|
||||||
| "frame"
|
| "frame"
|
||||||
@ -196,7 +196,7 @@ export type AppState = {
|
|||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
type:
|
type:
|
||||||
| typeof SHAPES[number]["value"]
|
| (typeof SHAPES)[number]["value"]
|
||||||
| "eraser"
|
| "eraser"
|
||||||
| "hand"
|
| "hand"
|
||||||
| "frame"
|
| "frame"
|
||||||
|
@ -372,7 +372,7 @@ export const updateActiveTool = (
|
|||||||
data: (
|
data: (
|
||||||
| {
|
| {
|
||||||
type:
|
type:
|
||||||
| typeof SHAPES[number]["value"]
|
| (typeof SHAPES)[number]["value"]
|
||||||
| "eraser"
|
| "eraser"
|
||||||
| "hand"
|
| "hand"
|
||||||
| "frame"
|
| "frame"
|
||||||
|
@ -188,11 +188,14 @@ const getTargetElementsMap = <T extends ExcalidrawElement>(
|
|||||||
elements: readonly T[],
|
elements: readonly T[],
|
||||||
indices: number[],
|
indices: number[],
|
||||||
) => {
|
) => {
|
||||||
return indices.reduce((acc, index) => {
|
return indices.reduce(
|
||||||
const element = elements[index];
|
(acc, index) => {
|
||||||
acc[element.id] = element;
|
const element = elements[index];
|
||||||
return acc;
|
acc[element.id] = element;
|
||||||
}, {} as Record<string, ExcalidrawElement>);
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, ExcalidrawElement>,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const _shiftElements = (
|
const _shiftElements = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user