feat: Calculate width/height of canvas based on container dimensions (".excalidraw" selector) & remove props width & height (#3379)

* Remove width/height from the ".excalidraw" container so it will sized automatically.
* updated all ref calculation to ".excalidraw" instead of parent since now ".excalidraw" will get resized
* Remove props width/height as its not needed anymore.
* Resize handler is also not needed anymore.
* Position absolute canvas due to #3379 (comment)

* move css to style and remove one extra rerendering

* factor out mock logic for test

* set height, width so as to avoid unnecessary updates of regression snap

* better mock

* better type checking and omit width,height from getDefaultAppState and also restore

* revert

* default to window dimensions in constructor

* update docs

* update

* update

* tweaks
This commit is contained in:
Aakansha Doshi 2021-04-04 15:05:16 +05:30 committed by GitHub
parent 3b976613ba
commit c54a099010
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 243 additions and 258 deletions

View file

@ -293,19 +293,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
actionManager: ActionManager;
private excalidrawContainerRef = React.createRef<HTMLDivElement>();
public static defaultProps: Partial<ExcalidrawProps> = {
width: window.innerWidth,
height: window.innerHeight,
};
private scene: Scene;
private resizeObserver: ResizeObserver | undefined;
constructor(props: ExcalidrawProps) {
super(props);
const defaultAppState = getDefaultAppState();
const {
width = window.innerWidth,
height = window.innerHeight,
excalidrawRef,
viewModeEnabled = false,
zenModeEnabled = false,
@ -317,13 +310,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
...defaultAppState,
theme,
isLoading: true,
width,
height,
...this.getCanvasOffsets(),
viewModeEnabled,
zenModeEnabled,
gridSize: gridModeEnabled ? GRID_SIZE : null,
name,
width: window.innerWidth,
height: window.innerHeight,
};
if (excalidrawRef) {
const readyPromise =
@ -447,10 +440,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
"excalidraw--view-mode": viewModeEnabled,
})}
ref={this.excalidrawContainerRef}
style={{
width: canvasDOMWidth,
height: canvasDOMHeight,
}}
>
<LayerUI
canvas={this.canvas}
@ -561,7 +550,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (typeof this.props.name !== "undefined") {
name = this.props.name;
}
this.setState(
(state) => {
// using Object.assign instead of spread to fool TS 4.2.2+ into
@ -570,10 +558,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
return Object.assign(actionResult.appState || {}, {
editingElement:
editingElement || actionResult.appState?.editingElement || null,
width: state.width,
height: state.height,
offsetTop: state.offsetTop,
offsetLeft: state.offsetLeft,
viewModeEnabled,
zenModeEnabled,
gridSize,
@ -706,7 +690,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
if (!this.state.isLoading) {
this.setState({ isLoading: true });
}
let initialData = null;
try {
initialData = (await this.props.initialData) || null;
@ -715,7 +698,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
const scene = restore(initialData, null);
scene.appState = {
...scene.appState,
isLoading: false,
@ -787,14 +769,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
this.scene.addCallback(this.onSceneUpdated);
this.addEventListeners();
if (
"ResizeObserver" in window &&
this.excalidrawContainerRef?.current?.parentElement
) {
this.resizeObserver = new ResizeObserver(() => this.setCanvasOffsets());
this.resizeObserver?.observe(
this.excalidrawContainerRef.current.parentElement,
);
if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) {
this.resizeObserver = new ResizeObserver(() => {
this.updateDOMRect();
});
this.resizeObserver?.observe(this.excalidrawContainerRef.current);
}
const searchParams = new URLSearchParams(window.location.search.slice(1));
@ -802,9 +781,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
// Obtain a file that was shared via the Web Share Target API.
this.restoreFileFromShare();
} else {
this.setState(this.getCanvasOffsets(), () => {
this.initializeScene();
});
this.updateDOMRect(this.initializeScene);
}
}
@ -906,17 +883,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
this.updateLanguage();
}
if (
prevProps.width !== this.props.width ||
prevProps.height !== this.props.height
) {
this.setState({
width: this.props.width ?? window.innerWidth,
height: this.props.height ?? window.innerHeight,
...this.getCanvasOffsets(),
});
}
if (prevProps.viewModeEnabled !== this.props.viewModeEnabled) {
this.setState(
{ viewModeEnabled: !!this.props.viewModeEnabled },
@ -4093,14 +4059,56 @@ class App extends React.Component<ExcalidrawProps, AppState> {
}
}, 300);
private updateDOMRect = (cb?: () => void) => {
if (this.excalidrawContainerRef?.current) {
const excalidrawContainer = this.excalidrawContainerRef.current;
const {
width,
height,
left: offsetLeft,
top: offsetTop,
} = excalidrawContainer.getBoundingClientRect();
const {
width: currentWidth,
height: currentHeight,
offsetTop: currentOffsetTop,
offsetLeft: currentOffsetLeft,
} = this.state;
if (
width === currentWidth &&
height === currentHeight &&
offsetLeft === currentOffsetLeft &&
offsetTop === currentOffsetTop
) {
if (cb) {
cb();
}
return;
}
this.setState(
{
width,
height,
offsetLeft,
offsetTop,
},
() => {
cb && cb();
},
);
}
};
public setCanvasOffsets = () => {
this.setState({ ...this.getCanvasOffsets() });
};
private getCanvasOffsets(): Pick<AppState, "offsetTop" | "offsetLeft"> {
if (this.excalidrawContainerRef?.current?.parentElement) {
const parentElement = this.excalidrawContainerRef.current.parentElement;
const { left, top } = parentElement.getBoundingClientRect();
if (this.excalidrawContainerRef?.current) {
const excalidrawContainer = this.excalidrawContainerRef.current;
const { left, top } = excalidrawContainer.getBoundingClientRect();
return {
offsetLeft: left,
offsetTop: top,