fix: Support Excalidraw inside scrollable container (#3018)

* refactor: remove position fixed from excalidraw container, modal and stats

* remove unused css

* remove position fixed from toast and scroll to content

* Make excal interactable by fixing offsets and set popover as fixed since position needs to be calculate from viewport  top

* Assign 200px less than height of Excalidraw to the selected shapes actions o UI doesn't overflow

* update changelog, readme and package.json
This commit is contained in:
Aakansha Doshi 2021-02-14 18:18:34 +05:30 committed by GitHub
parent 5b343a9d46
commit 830fb64a25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 73 additions and 44 deletions

View File

@ -51,6 +51,7 @@ import {
LINE_CONFIRM_THRESHOLD, LINE_CONFIRM_THRESHOLD,
MIME_TYPES, MIME_TYPES,
POINTER_BUTTON, POINTER_BUTTON,
SCROLL_TIMEOUT,
TAP_TWICE_TIMEOUT, TAP_TWICE_TIMEOUT,
TEXT_TO_CENTER_SNAP_THRESHOLD, TEXT_TO_CENTER_SNAP_THRESHOLD,
TOUCH_CTX_MENU_TIMEOUT, TOUCH_CTX_MENU_TIMEOUT,
@ -825,6 +826,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
document.addEventListener(EVENT.PASTE, this.pasteFromClipboard); document.addEventListener(EVENT.PASTE, this.pasteFromClipboard);
document.addEventListener(EVENT.CUT, this.onCut); document.addEventListener(EVENT.CUT, this.onCut);
document.addEventListener(EVENT.SCROLL, this.onScroll);
window.addEventListener(EVENT.RESIZE, this.onResize, false); window.addEventListener(EVENT.RESIZE, this.onResize, false);
window.addEventListener(EVENT.UNLOAD, this.onUnload, false); window.addEventListener(EVENT.UNLOAD, this.onUnload, false);
@ -998,6 +1000,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
} }
} }
private onScroll = debounce(() => {
this.setState({ ...this.getCanvasOffsets() });
}, SCROLL_TIMEOUT);
// Copy/paste // Copy/paste
private onCut = withBatchedUpdates((event: ClipboardEvent) => { private onCut = withBatchedUpdates((event: ClipboardEvent) => {

View File

@ -442,7 +442,15 @@ const LayerUI = ({
"transition-left": zenModeEnabled, "transition-left": zenModeEnabled,
})} })}
> >
<Island className={CLASSES.SHAPE_ACTIONS_MENU} padding={2}> <Island
className={CLASSES.SHAPE_ACTIONS_MENU}
padding={2}
style={{
// we want to make sure this doesn't overflow so substracting 200
// which is approximately height of zoom footer and top left menu items with some buffer
maxHeight: `${appState.height - 200}px`,
}}
>
<SelectedShapeActions <SelectedShapeActions
appState={appState} appState={appState}
elements={elements} elements={elements}
@ -603,18 +611,6 @@ const LayerUI = ({
> >
{t("buttons.exitZenMode")} {t("buttons.exitZenMode")}
</button> </button>
{appState.scrolledOutside && (
<button
className="scroll-back-to-content"
onClick={() => {
setAppState({
...calculateScrollCenter(elements, appState, canvas),
});
}}
>
{t("buttons.scrollBackToContent")}
</button>
)}
</footer> </footer>
); );
@ -677,6 +673,18 @@ const LayerUI = ({
{renderBottomAppMenu()} {renderBottomAppMenu()}
{renderGitHubCorner()} {renderGitHubCorner()}
{renderFooter()} {renderFooter()}
{appState.scrolledOutside && (
<button
className="scroll-back-to-content"
onClick={() => {
setAppState({
...calculateScrollCenter(elements, appState, canvas),
});
}}
>
{t("buttons.scrollBackToContent")}
</button>
)}
</div> </div>
); );
}; };

View File

@ -1,8 +1,13 @@
@import "../css/variables.module"; @import "../css/variables.module";
.excalidraw { .excalidraw {
&.excalidraw-modal-container {
position: absolute;
z-index: 10;
}
.Modal { .Modal {
position: fixed; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
@ -15,7 +20,7 @@
} }
.Modal__background { .Modal__background {
position: fixed; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
@ -82,7 +87,7 @@
} }
.Modal__content { .Modal__content {
position: fixed; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;

View File

@ -54,7 +54,7 @@ const useBodyRoot = () => {
?.classList.contains("Appearance_dark"); ?.classList.contains("Appearance_dark");
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("excalidraw"); div.classList.add("excalidraw", "excalidraw-modal-container");
if (isDarkTheme) { if (isDarkTheme) {
div.classList.add("Appearance_dark"); div.classList.add("Appearance_dark");

View File

@ -1,14 +1,6 @@
.excalidraw { .excalidraw {
.popover { .popover {
position: absolute; position: fixed;
z-index: 10; z-index: 10;
} }
.popover .cover {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
} }

View File

@ -1,7 +1,7 @@
@import "../css/variables.module"; @import "../css/variables.module";
.Stats { .Stats {
position: fixed; position: absolute;
top: 64px; top: 64px;
right: 12px; right: 12px;
font-size: 12px; font-size: 12px;

View File

@ -11,7 +11,7 @@
left: 50%; left: 50%;
margin-left: -150px; margin-left: -150px;
padding: 4px 0; padding: 4px 0;
position: fixed; position: absolute;
text-align: center; text-align: center;
width: 300px; width: 300px;
z-index: 999999; z-index: 999999;

View File

@ -47,6 +47,7 @@ export enum EVENT {
TOUCH_END = "touchend", TOUCH_END = "touchend",
HASHCHANGE = "hashchange", HASHCHANGE = "hashchange",
VISIBILITY_CHANGE = "visibilitychange", VISIBILITY_CHANGE = "visibilitychange",
SCROLL = "scroll",
} }
export const ENV = { export const ENV = {
@ -92,6 +93,7 @@ export const TOUCH_CTX_MENU_TIMEOUT = 500;
export const TITLE_TIMEOUT = 10000; export const TITLE_TIMEOUT = 10000;
export const TOAST_TIMEOUT = 5000; export const TOAST_TIMEOUT = 5000;
export const VERSION_TIMEOUT = 30000; export const VERSION_TIMEOUT = 30000;
export const SCROLL_TIMEOUT = 500;
export const ZOOM_STEP = 0.1; export const ZOOM_STEP = 0.1;

View File

@ -10,7 +10,6 @@
.excalidraw { .excalidraw {
color: var(--text-color-primary); color: var(--text-color-primary);
display: flex; display: flex;
position: fixed;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
@ -362,7 +361,6 @@
.App-menu__left { .App-menu__left {
overflow-y: auto; overflow-y: auto;
max-height: calc(100vh - 236px);
} }
.dropdown-select { .dropdown-select {
@ -434,7 +432,7 @@
.scroll-back-to-content { .scroll-back-to-content {
color: var(--popup-text-color); color: var(--popup-text-color);
position: fixed; position: absolute;
left: 50%; left: 50%;
bottom: 30px; bottom: 30px;
transform: translateX(-50%); transform: translateX(-50%);

View File

@ -12,6 +12,20 @@ The change should be grouped under one of the below section and must contain PR
Please add the latest change on the top under the correct section. Please add the latest change on the top under the correct section.
--> -->
## 0.3.1
## Excalidraw API
### Fixes
- Support Excalidraw inside scrollable container [#3018](https://github.com/excalidraw/excalidraw/pull/3018)
## Excalidraw Library
### Fixes
- Allow to toggle between modes when view only mode to make UI consistent [#3009](https://github.com/excalidraw/excalidraw/pull/3009)
## 0.3.0 ## 0.3.0
## Excalidraw API ## Excalidraw API

View File

@ -37,18 +37,18 @@ You can update the value of `PUBLIC_URL` if you want to serve it from a differen
1. If you are using a Web bundler (for instance, Webpack), you can import it as an ES6 module as shown below 1. If you are using a Web bundler (for instance, Webpack), you can import it as an ES6 module as shown below
```js ```js
import React, { useEffect, useState, createRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import Excalidraw from "@excalidraw/excalidraw"; import Excalidraw from "@excalidraw/excalidraw";
import InitialData from "./initialData"; import InitialData from "./initialData";
import "./styles.css"; import "./styles.scss";
export default function App() { export default function App() {
const excalidrawRef = createRef(); const excalidrawRef = useRef(null);
const excalidrawWrapperRef = useRef(null);
const [dimensions, setDimensions] = useState({ const [dimensions, setDimensions] = useState({
width: window.innerWidth, width: undefined,
height: window.innerHeight, height: undefined,
}); });
const [viewModeEnabled, setViewModeEnabled] = useState(false); const [viewModeEnabled, setViewModeEnabled] = useState(false);
@ -56,17 +56,21 @@ export default function App() {
const [gridModeEnabled, setGridModeEnabled] = useState(false); const [gridModeEnabled, setGridModeEnabled] = useState(false);
useEffect(() => { useEffect(() => {
setDimensions({
width: excalidrawWrapperRef.current.getBoundingClientRect().width,
height: excalidrawWrapperRef.current.getBoundingClientRect().height,
});
const onResize = () => { const onResize = () => {
setDimensions({ setDimensions({
width: window.innerWidth, width: excalidrawWrapperRef.current.getBoundingClientRect().width,
height: window.innerHeight, height: excalidrawWrapperRef.current.getBoundingClientRect().height,
}); });
}; };
window.addEventListener("resize", onResize); window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize);
}, []); }, [excalidrawWrapperRef]);
const updateScene = () => { const updateScene = () => {
const sceneData = { const sceneData = {
@ -102,6 +106,7 @@ export default function App() {
return ( return (
<div className="App"> <div className="App">
<h1> Excalidraw Example</h1>
<div className="button-wrapper"> <div className="button-wrapper">
<button className="update-scene" onClick={updateScene}> <button className="update-scene" onClick={updateScene}>
Update Scene Update Scene
@ -139,7 +144,7 @@ export default function App() {
Grid mode Grid mode
</label> </label>
</div> </div>
<div className="excalidraw-wrapper"> <div className="excalidraw-wrapper" ref={excalidrawWrapperRef}>
<Excalidraw <Excalidraw
ref={excalidrawRef} ref={excalidrawRef}
width={dimensions.width} width={dimensions.width}
@ -148,7 +153,6 @@ export default function App() {
onChange={(elements, state) => onChange={(elements, state) =>
console.log("Elements :", elements, "State : ", state) console.log("Elements :", elements, "State : ", state)
} }
user={{ name: "Excalidraw User" }}
onPointerUpdate={(payload) => console.log(payload)} onPointerUpdate={(payload) => console.log(payload)}
onCollabButtonClick={() => onCollabButtonClick={() =>
window.alert("You clicked on collab button") window.alert("You clicked on collab button")

View File

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.3.0", "version": "0.3.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@excalidraw/excalidraw", "name": "@excalidraw/excalidraw",
"version": "0.3.0", "version": "0.3.1",
"main": "dist/excalidraw.min.js", "main": "dist/excalidraw.min.js",
"files": [ "files": [
"dist/*" "dist/*"