diff --git a/src/components/App.tsx b/src/components/App.tsx index ebf4d1e0e..a0f5d27fd 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -274,6 +274,7 @@ export type ExcalidrawImperativeAPI = { setScrollToContent: InstanceType["setScrollToContent"]; getSceneElements: InstanceType["getSceneElements"]; getAppState: () => InstanceType["state"]; + setCanvasOffsets: InstanceType["setCanvasOffsets"]; readyPromise: ResolvablePromise; ready: true; }; @@ -297,8 +298,6 @@ class App extends React.Component { const { width = window.innerWidth, height = window.innerHeight, - offsetLeft, - offsetTop, excalidrawRef, viewModeEnabled = false, zenModeEnabled = false, @@ -312,7 +311,7 @@ class App extends React.Component { isLoading: true, width, height, - ...this.getCanvasOffsets({ offsetLeft, offsetTop }), + ...this.getCanvasOffsets(), viewModeEnabled, zenModeEnabled, gridSize: gridModeEnabled ? GRID_SIZE : null, @@ -335,6 +334,7 @@ class App extends React.Component { setScrollToContent: this.setScrollToContent, getSceneElements: this.getSceneElements, getAppState: () => this.state, + setCanvasOffsets: this.setCanvasOffsets, } as const; if (typeof excalidrawRef === "function") { excalidrawRef(api); @@ -755,14 +755,8 @@ class App extends React.Component { if (searchParams.has("web-share-target")) { // Obtain a file that was shared via the Web Share Target API. this.restoreFileFromShare(); - } else if ( - typeof this.props.offsetLeft === "number" && - typeof this.props.offsetTop === "number" - ) { - // Optimization to avoid extra render on init. - this.initializeScene(); } else { - this.setState(this.getCanvasOffsets(this.props), () => { + this.setState(this.getCanvasOffsets(), () => { this.initializeScene(); }); } @@ -867,16 +861,12 @@ class App extends React.Component { if ( prevProps.width !== this.props.width || - prevProps.height !== this.props.height || - (typeof this.props.offsetLeft === "number" && - prevProps.offsetLeft !== this.props.offsetLeft) || - (typeof this.props.offsetTop === "number" && - prevProps.offsetTop !== this.props.offsetTop) + prevProps.height !== this.props.height ) { this.setState({ width: this.props.width ?? window.innerWidth, height: this.props.height ?? window.innerHeight, - ...this.getCanvasOffsets(this.props), + ...this.getCanvasOffsets(), }); } @@ -4046,33 +4036,22 @@ class App extends React.Component { } }, 300); - private getCanvasOffsets(offsets?: { - offsetLeft?: number; - offsetTop?: number; - }): Pick { - if ( - typeof offsets?.offsetLeft === "number" && - typeof offsets?.offsetTop === "number" - ) { - return { - offsetLeft: offsets.offsetLeft, - offsetTop: offsets.offsetTop, - }; - } + public setCanvasOffsets = () => { + this.setState({ ...this.getCanvasOffsets() }); + }; + + private getCanvasOffsets(): Pick { if (this.excalidrawContainerRef?.current?.parentElement) { const parentElement = this.excalidrawContainerRef.current.parentElement; const { left, top } = parentElement.getBoundingClientRect(); return { - offsetLeft: - typeof offsets?.offsetLeft === "number" ? offsets.offsetLeft : left, - offsetTop: - typeof offsets?.offsetTop === "number" ? offsets.offsetTop : top, + offsetLeft: left, + offsetTop: top, }; } return { - offsetLeft: - typeof offsets?.offsetLeft === "number" ? offsets.offsetLeft : 0, - offsetTop: typeof offsets?.offsetTop === "number" ? offsets.offsetTop : 0, + offsetLeft: 0, + offsetTop: 0, }; } diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md index 647927541..ef8fce69d 100644 --- a/src/packages/excalidraw/CHANGELOG.md +++ b/src/packages/excalidraw/CHANGELOG.md @@ -19,6 +19,9 @@ Please add the latest change on the top under the correct section. ### Features - Add `name` prop which indicates 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 [#3273](https://github.com/excalidraw/excalidraw/pull/3273). +- Export API `setCanvasOffsets` via `ref` to set the offsets for Excalidraw[#3265](https://github.com/excalidraw/excalidraw/pull/3265). + #### BREAKING CHANGE + - `offsetLeft` and `offsetTop` props have been removed now so you have to use the `setCanvasOffsets` via `ref` to achieve the same. - Export API to export the drawing to canvas, svg and blob [#3258](https://github.com/excalidraw/excalidraw/pull/3258). For more info you can check the [readme](https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw/README.md#user-content-export-utils) - Add a `theme` prop to indicate Excalidraw's theme. [#3228](https://github.com/excalidraw/excalidraw/pull/3228). When this prop is passed, the theme is fully controlled by host app. - Support `libraryReturnUrl` prop to indicate what URL to install libraries to [#3227](https://github.com/excalidraw/excalidraw/pull/3227). diff --git a/src/packages/excalidraw/README.md b/src/packages/excalidraw/README.md index 8e9702c76..4ba6b7d9e 100644 --- a/src/packages/excalidraw/README.md +++ b/src/packages/excalidraw/README.md @@ -362,8 +362,6 @@ export default function IndexPage() { | --- | --- | --- | --- | | [`width`](#width) | Number | `window.innerWidth` | The width of Excalidraw component | | [`height`](#height) | Number | `window.innerHeight` | The height of Excalidraw component | -| [`offsetLeft`](#offsetLeft) | Number | `0` | left position relative to which Excalidraw should be rendered | -| [`offsetTop`](#offsetTop) | Number | `0` | top position relative to which Excalidraw should render | | [`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. | | [`initialData`](#initialData) |
{elements?: ExcalidrawElement[], appState?: AppState } 
| null | The initial data with which app loads. | | [`ref`](#ref) | [`createRef`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or [`callbackRef`](https://reactjs.org/docs/refs-and-the-dom.html#callback-refs) or
{ current: { readyPromise: resolvablePromise } }
| | Ref to be passed to Excalidraw | @@ -388,14 +386,6 @@ This props defines the `width` of the Excalidraw component. Defaults to `window. This props defines the `height` of the Excalidraw component. Defaults to `window.innerHeight` if not passed. -#### `offsetLeft` - -This prop defines `left` position relative to which Excalidraw should be rendered. Defaults to `0` if not passed. - -#### `offsetTop` - -This prop defines `top` position relative to which Excalidraw should be rendered. Defaults to `0` if not passed. - #### `onChange` Every time component updates, this callback if passed will get triggered and has the below signature. @@ -466,6 +456,7 @@ You can pass a `ref` when you want to access some excalidraw APIs. We expose the | getAppState |
 () => AppState
| Returns current appState | | history | `{ clear: () => void }` | This is the history API. `history.clear()` will clear the history | | setScrollToContent |
 (ExcalidrawElement[]) => void 
| Scroll to the nearest element to center | +| setCanvasOffsets | `() => void` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You should call this API when your app changes the dimensions/position of the Excalidraw container, such as when toggling a sidebar. You don't have to call this when the position is changed on page scroll (we handled that ourselves). | #### `readyPromise` diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index cca2ae5b2..93cf7404d 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -15,8 +15,6 @@ const Excalidraw = (props: ExcalidrawProps) => { const { width, height, - offsetLeft, - offsetTop, onChange, initialData, excalidrawRef, @@ -58,8 +56,6 @@ const Excalidraw = (props: ExcalidrawProps) => { { const ELEM_WIDTH = 100; const ELEM_HEIGHT = 60; - await render( - , - ); + const originalGetBoundingClientRect = + global.window.HTMLDivElement.prototype.getBoundingClientRect; + // override getBoundingClientRect as by default it will always return all values as 0 even if customized in html + global.window.HTMLDivElement.prototype.getBoundingClientRect = () => ({ + top: OFFSET_TOP, + left: OFFSET_LEFT, + bottom: 10, + right: 10, + width: 100, + x: 10, + y: 20, + height: 100, + toJSON: () => {}, + }); + await render( +
+ +
, + ); await waitFor(() => { expect(h.state.width).toBe(WIDTH); expect(h.state.height).toBe(HEIGHT); @@ -45,5 +59,6 @@ describe("appState", () => { expect(h.state.scrollX).toBe(WIDTH / 2 - ELEM_WIDTH / 2); expect(h.state.scrollY).toBe(HEIGHT / 2 - ELEM_HEIGHT / 2); }); + global.window.HTMLDivElement.prototype.getBoundingClientRect = originalGetBoundingClientRect; }); }); diff --git a/src/types.ts b/src/types.ts index edc738973..956f8f518 100644 --- a/src/types.ts +++ b/src/types.ts @@ -162,10 +162,6 @@ export type ExcalidrawAPIRefValue = export interface ExcalidrawProps { width?: number; height?: number; - /** if not supplied, calculated by Excalidraw */ - offsetLeft?: number; - /** if not supplied, calculated by Excalidraw */ - offsetTop?: number; onChange?: ( elements: readonly ExcalidrawElement[], appState: AppState,