Skip to content

Commit 6a8148c

Browse files
committed
custom
1 parent a7153d9 commit 6a8148c

27 files changed

+350
-277
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"prettier": "@excalidraw/prettier-config",
9191
"private": true,
9292
"scripts": {
93+
"setup": "yarn && cd src/packages/excalidraw && yarn",
9394
"build-node": "node ./scripts/build-node.js",
9495
"build:app:docker": "REACT_APP_DISABLE_SENTRY=true react-scripts build",
9596
"build:app": "REACT_APP_GIT_SHA=$VERCEL_GIT_COMMIT_SHA react-scripts build",
@@ -115,6 +116,8 @@
115116
"test": "yarn test:app",
116117
"autorelease": "node scripts/autorelease.js",
117118
"prerelease": "node scripts/prerelease.js",
118-
"release": "node scripts/release.js"
119+
"release": "node scripts/release.js",
120+
"pull": "git pull upstream master --rebase",
121+
"publish": "cd src/packages/excalidraw && npm run build:umd && yarn publish"
119122
}
120123
}

src/actions/manager.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export class ActionManager {
8383
.sort((a, b) => (b.keyPriority || 0) - (a.keyPriority || 0))
8484
.filter(
8585
(action) =>
86-
(action.name in canvasActions
86+
(canvasActions && action.name in canvasActions
8787
? canvasActions[action.name as keyof typeof canvasActions]
8888
: true) &&
8989
action.keyTest &&
@@ -141,7 +141,7 @@ export class ActionManager {
141141
if (
142142
this.actions[name] &&
143143
"PanelComponent" in this.actions[name] &&
144-
(name in canvasActions
144+
(canvasActions && name in canvasActions
145145
? canvasActions[name as keyof typeof canvasActions]
146146
: true)
147147
) {

src/components/App.tsx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,10 @@ import {
167167
} from "../keys";
168168
import { distance2d, getGridPoint, isPathALoop } from "../math";
169169
import { renderSceneThrottled } from "../renderer/renderScene";
170-
import { invalidateShapeForElement } from "../renderer/renderElement";
170+
import {
171+
clearRenderCache,
172+
invalidateShapeForElement,
173+
} from "../renderer/renderElement";
171174
import {
172175
calculateScrollCenter,
173176
getTextBindableContainerAtPosition,
@@ -270,7 +273,7 @@ const DeviceContext = React.createContext<Device>(deviceContextInitialValue);
270273
export const useDevice = () => useContext<Device>(DeviceContext);
271274
const ExcalidrawContainerContext = React.createContext<{
272275
container: HTMLDivElement | null;
273-
id: string | null;
276+
id?: string | null;
274277
}>({ container: null, id: null });
275278
export const useExcalidrawContainer = () =>
276279
useContext(ExcalidrawContainerContext);
@@ -385,6 +388,7 @@ class App extends React.Component<AppProps, AppState> {
385388
setActiveTool: this.setActiveTool,
386389
setCursor: this.setCursor,
387390
resetCursor: this.resetCursor,
391+
app: this,
388392
} as const;
389393
if (typeof excalidrawRef === "function") {
390394
excalidrawRef(api);
@@ -484,6 +488,7 @@ class App extends React.Component<AppProps, AppState> {
484488
return (
485489
<div
486490
className={clsx("excalidraw excalidraw-container", {
491+
"excalidraw--zen-mode": zenModeEnabled,
487492
"excalidraw--view-mode": viewModeEnabled,
488493
"excalidraw--mobile": this.device.isMobile,
489494
})}
@@ -499,6 +504,7 @@ class App extends React.Component<AppProps, AppState> {
499504
>
500505
<DeviceContext.Provider value={this.device}>
501506
<LayerUI
507+
onHomeButtonClick={this.props.onHomeButtonClick}
502508
canvas={this.canvas}
503509
appState={this.state}
504510
files={this.files}
@@ -529,6 +535,7 @@ class App extends React.Component<AppProps, AppState> {
529535
}
530536
showThemeBtn={
531537
typeof this.props?.theme === "undefined" &&
538+
this.props.UIOptions.canvasActions &&
532539
this.props.UIOptions.canvasActions.theme
533540
}
534541
libraryReturnUrl={this.props.libraryReturnUrl}
@@ -787,6 +794,13 @@ class App extends React.Component<AppProps, AppState> {
787794
};
788795
}
789796

797+
if (initialData?.scrollX != null) {
798+
scene.appState.scrollX = initialData.scrollX;
799+
}
800+
if (initialData?.scrollY != null) {
801+
scene.appState.scrollY = initialData.scrollY;
802+
}
803+
790804
this.resetHistory();
791805
this.syncActionResult({
792806
...scene,
@@ -917,6 +931,25 @@ class App extends React.Component<AppProps, AppState> {
917931
this.unmounted = true;
918932
this.removeEventListeners();
919933
this.scene.destroy();
934+
clearRenderCache();
935+
936+
this.scene = new Scene();
937+
this.history = new History();
938+
this.actionManager = new ActionManager(
939+
this.syncActionResult,
940+
() => this.state,
941+
() => this.scene.getElementsIncludingDeleted(),
942+
this,
943+
);
944+
this.library = new Library(this);
945+
this.canvas = null;
946+
this.rc = null;
947+
948+
// @ts-ignore
949+
this.excalidrawContainerRef.current = undefined;
950+
this.nearestScrollableContainer = undefined;
951+
this.excalidrawContainerValue = { container: null, id: "unmounted" };
952+
920953
clearTimeout(touchTimeout);
921954
touchTimeout = 0;
922955
}
@@ -975,6 +1008,7 @@ class App extends React.Component<AppProps, AppState> {
9751008
this.disableEvent,
9761009
false,
9771010
);
1011+
document.fonts?.removeEventListener?.("loadingdone", this.onFontLoaded);
9781012

9791013
document.removeEventListener(
9801014
EVENT.GESTURE_START,
@@ -1122,6 +1156,15 @@ class App extends React.Component<AppProps, AppState> {
11221156
});
11231157
}
11241158

1159+
if (
1160+
!this.props.UIOptions.canvasActions &&
1161+
this.state.openMenu === "canvas"
1162+
) {
1163+
this.setState({
1164+
openMenu: null,
1165+
});
1166+
}
1167+
11251168
if (this.props.name && prevProps.name !== this.props.name) {
11261169
this.setState({
11271170
name: this.props.name,
@@ -1269,6 +1312,7 @@ class App extends React.Component<AppProps, AppState> {
12691312
this.scene.getElementsIncludingDeleted(),
12701313
this.state,
12711314
this.files,
1315+
this.props.id,
12721316
);
12731317
}
12741318
}

src/components/Avatar.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
.Avatar {
55
width: 2.5rem;
66
height: 2.5rem;
7-
border-radius: 1.25rem;
7+
border-radius: 50%;
88
display: flex;
99
justify-content: center;
1010
align-items: center;

src/components/Island.scss

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,49 @@
66
border-radius: var(--border-radius-lg);
77
padding: calc(var(--padding) * var(--space-factor));
88
position: relative;
9-
transition: box-shadow 0.5s ease-in-out;
109

1110
&.zen-mode {
1211
box-shadow: none;
1312
}
13+
14+
&::-webkit-scrollbar {
15+
width: 10px;
16+
}
17+
18+
&::-webkit-scrollbar-track {
19+
background-color: transparent;
20+
}
21+
22+
&::-webkit-scrollbar-thumb {
23+
background-color: var(--color-scrollbar-thumb);
24+
}
25+
&::-webkit-scrollbar-thumb:hover {
26+
background-color: var(--color-scrollbar-thumb-hover);
27+
}
28+
29+
&::-webkit-scrollbar-thumb:active {
30+
background-color: var(--color-scrollbar-thumb-active);
31+
}
32+
}
33+
34+
.App-menu_top {
35+
.Stack_vertical {
36+
.Island {
37+
min-width: 216px;
38+
}
39+
.Stack_horizontal {
40+
justify-content: center !important;
41+
}
42+
}
43+
}
44+
45+
&.excalidraw--view-mode {
46+
.App-menu_top {
47+
.Stack_vertical {
48+
.Island {
49+
min-width: auto;
50+
}
51+
}
52+
}
1453
}
1554
}

src/components/JSONExportDialog.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import React, { useState } from "react";
22
import { NonDeletedExcalidrawElement } from "../element/types";
33
import { t } from "../i18n";
44
import { useDevice } from "./App";
5-
import { AppState, ExportOpts, BinaryFiles } from "../types";
5+
import { AppState, CanvasActions, ExportOpts, BinaryFiles } from "../types";
66
import { Dialog } from "./Dialog";
7-
import { exportFile, exportToFileIcon, link } from "./icons";
7+
import { exportToFileIcon, link } from "./icons";
88
import { ToolButton } from "./ToolButton";
99
import { actionSaveFileToDisk } from "../actions/actionExport";
1010
import { Card } from "./Card";
@@ -98,7 +98,7 @@ export const JSONExportDialog = ({
9898
appState: AppState;
9999
files: BinaryFiles;
100100
actionManager: ActionManager;
101-
exportOpts: ExportOpts;
101+
exportOpts: CanvasActions["export"];
102102
canvas: HTMLCanvasElement | null;
103103
}) => {
104104
const [modalIsShown, setModalIsShown] = useState(false);
@@ -111,16 +111,20 @@ export const JSONExportDialog = ({
111111
<>
112112
<ToolButton
113113
onClick={() => {
114-
setModalIsShown(true);
114+
if (typeof exportOpts === "function") {
115+
actionManager.executeAction(actionSaveFileToDisk);
116+
} else {
117+
setModalIsShown(true);
118+
}
115119
}}
116120
data-testid="json-export-button"
117-
icon={exportFile}
121+
icon={exportToFileIcon}
118122
type="button"
119123
aria-label={t("buttons.export")}
120124
showAriaLabel={useDevice().isMobile}
121125
title={t("buttons.export")}
122126
/>
123-
{modalIsShown && (
127+
{modalIsShown && exportOpts && typeof exportOpts !== "function" && (
124128
<Dialog onCloseRequest={handleClose} title={t("buttons.export")}>
125129
<JSONExportModal
126130
elements={elements}

src/components/LayerUI.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { Stats } from "./Stats";
4141
import { actionToggleStats } from "../actions/actionToggleStats";
4242

4343
interface LayerUIProps {
44+
onHomeButtonClick?: () => void;
4445
actionManager: ActionManager;
4546
appState: AppState;
4647
files: BinaryFiles;
@@ -69,6 +70,7 @@ interface LayerUIProps {
6970
onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void;
7071
}
7172
const LayerUI = ({
73+
onHomeButtonClick,
7274
actionManager,
7375
appState,
7476
files,
@@ -98,7 +100,7 @@ const LayerUI = ({
98100
const device = useDevice();
99101

100102
const renderJSONExportDialog = () => {
101-
if (!UIOptions.canvasActions.export) {
103+
if (!UIOptions.canvasActions || !UIOptions.canvasActions.export) {
102104
return null;
103105
}
104106

@@ -115,7 +117,7 @@ const LayerUI = ({
115117
};
116118

117119
const renderImageExportDialog = () => {
118-
if (!UIOptions.canvasActions.saveAsImage) {
120+
if (!UIOptions.canvasActions || !UIOptions.canvasActions.saveAsImage) {
119121
return null;
120122
}
121123

@@ -162,10 +164,6 @@ const LayerUI = ({
162164
);
163165
};
164166

165-
const Separator = () => {
166-
return <div style={{ width: ".625em" }} />;
167-
};
168-
169167
const renderViewModeCanvasActions = () => {
170168
return (
171169
<Section
@@ -199,13 +197,11 @@ const LayerUI = ({
199197
see https://github.com/excalidraw/excalidraw/pull/1445 */}
200198
<Island padding={2} style={{ zIndex: 1 }}>
201199
<Stack.Col gap={4}>
202-
<Stack.Row gap={1} justifyContent="space-between">
200+
<Stack.Row gap={3} justifyContent="space-between">
203201
{actionManager.renderAction("clearCanvas")}
204-
<Separator />
205202
{actionManager.renderAction("loadScene")}
206203
{renderJSONExportDialog()}
207204
{renderImageExportDialog()}
208-
<Separator />
209205
{onCollabButtonClick && (
210206
<CollabButton
211207
isCollaborating={isCollaborating}
@@ -304,9 +300,10 @@ const LayerUI = ({
304300
gap={4}
305301
className={clsx({ "disable-pointerEvents": zenModeEnabled })}
306302
>
307-
{viewModeEnabled
308-
? renderViewModeCanvasActions()
309-
: renderCanvasActions()}
303+
{UIOptions.canvasActions &&
304+
(viewModeEnabled
305+
? renderViewModeCanvasActions()
306+
: renderCanvasActions())}
310307
{shouldRenderSelectedShapeActions && renderSelectedShapeActions()}
311308
</Stack.Col>
312309
{!viewModeEnabled && (
@@ -378,8 +375,12 @@ const LayerUI = ({
378375
<UserList
379376
collaborators={appState.collaborators}
380377
actionManager={actionManager}
378+
className={clsx("zen-mode-transition", {
379+
"transition-right": zenModeEnabled,
380+
})}
381+
layout="vertical"
381382
/>
382-
{renderTopRightUI?.(device.isMobile, appState)}
383+
{renderTopRightUI?.(device.isMobile, appState, canvas)}
383384
</div>
384385
</div>
385386
</FixedSideContainer>
@@ -530,6 +531,7 @@ const LayerUI = ({
530531
<>
531532
{dialogs}
532533
<MobileMenu
534+
onHomeButtonClick={onHomeButtonClick}
533535
appState={appState}
534536
elements={elements}
535537
actionManager={actionManager}
@@ -548,6 +550,7 @@ const LayerUI = ({
548550
onImageAction={onImageAction}
549551
renderTopRightUI={renderTopRightUI}
550552
renderStats={renderStats}
553+
UIOptions={UIOptions}
551554
/>
552555
</>
553556
) : (
@@ -606,6 +609,7 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
606609

607610
const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[];
608611
return (
612+
prev.renderTopRightUI === next.renderTopRightUI &&
609613
prev.renderCustomFooter === next.renderCustomFooter &&
610614
prev.langCode === next.langCode &&
611615
prev.elements === next.elements &&

0 commit comments

Comments
 (0)