|
@@ -230,6 +230,7 @@ import {
|
|
SidebarName,
|
|
SidebarName,
|
|
SidebarTabName,
|
|
SidebarTabName,
|
|
KeyboardModifiersObject,
|
|
KeyboardModifiersObject,
|
|
|
|
+ CollaboratorPointer,
|
|
ToolType,
|
|
ToolType,
|
|
} from "../types";
|
|
} from "../types";
|
|
import {
|
|
import {
|
|
@@ -368,6 +369,8 @@ import { isSidebarDockedAtom } from "./Sidebar/Sidebar";
|
|
import { StaticCanvas, InteractiveCanvas } from "./canvases";
|
|
import { StaticCanvas, InteractiveCanvas } from "./canvases";
|
|
import { Renderer } from "../scene/Renderer";
|
|
import { Renderer } from "../scene/Renderer";
|
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
import { ShapeCache } from "../scene/ShapeCache";
|
|
|
|
+import { LaserToolOverlay } from "./LaserTool/LaserTool";
|
|
|
|
+import { LaserPathManager } from "./LaserTool/LaserPathManager";
|
|
|
|
|
|
const AppContext = React.createContext<AppClassProperties>(null!);
|
|
const AppContext = React.createContext<AppClassProperties>(null!);
|
|
const AppPropsContext = React.createContext<AppProps>(null!);
|
|
const AppPropsContext = React.createContext<AppProps>(null!);
|
|
@@ -497,6 +500,8 @@ class App extends React.Component<AppProps, AppState> {
|
|
null;
|
|
null;
|
|
lastViewportPosition = { x: 0, y: 0 };
|
|
lastViewportPosition = { x: 0, y: 0 };
|
|
|
|
|
|
|
|
+ laserPathManager: LaserPathManager = new LaserPathManager(this);
|
|
|
|
+
|
|
constructor(props: AppProps) {
|
|
constructor(props: AppProps) {
|
|
super(props);
|
|
super(props);
|
|
const defaultAppState = getDefaultAppState();
|
|
const defaultAppState = getDefaultAppState();
|
|
@@ -1205,12 +1210,14 @@ class App extends React.Component<AppProps, AppState> {
|
|
!this.scene.getElementsIncludingDeleted().length
|
|
!this.scene.getElementsIncludingDeleted().length
|
|
}
|
|
}
|
|
app={this}
|
|
app={this}
|
|
|
|
+ isCollaborating={this.props.isCollaborating}
|
|
>
|
|
>
|
|
{this.props.children}
|
|
{this.props.children}
|
|
</LayerUI>
|
|
</LayerUI>
|
|
<div className="excalidraw-textEditorContainer" />
|
|
<div className="excalidraw-textEditorContainer" />
|
|
<div className="excalidraw-contextMenuContainer" />
|
|
<div className="excalidraw-contextMenuContainer" />
|
|
<div className="excalidraw-eye-dropper-container" />
|
|
<div className="excalidraw-eye-dropper-container" />
|
|
|
|
+ <LaserToolOverlay manager={this.laserPathManager} />
|
|
{selectedElements.length === 1 &&
|
|
{selectedElements.length === 1 &&
|
|
!this.state.contextMenu &&
|
|
!this.state.contextMenu &&
|
|
this.state.showHyperlinkPopup && (
|
|
this.state.showHyperlinkPopup && (
|
|
@@ -1738,6 +1745,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
this.removeEventListeners();
|
|
this.removeEventListeners();
|
|
this.scene.destroy();
|
|
this.scene.destroy();
|
|
this.library.destroy();
|
|
this.library.destroy();
|
|
|
|
+ this.laserPathManager.destroy();
|
|
ShapeCache.destroy();
|
|
ShapeCache.destroy();
|
|
SnapCache.destroy();
|
|
SnapCache.destroy();
|
|
clearTimeout(touchTimeout);
|
|
clearTimeout(touchTimeout);
|
|
@@ -3052,6 +3060,15 @@ class App extends React.Component<AppProps, AppState> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
|
|
|
|
+ if (this.state.activeTool.type === "laser") {
|
|
|
|
+ this.setActiveTool({ type: "selection" });
|
|
|
|
+ } else {
|
|
|
|
+ this.setActiveTool({ type: "laser" });
|
|
|
|
+ }
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (
|
|
if (
|
|
event[KEYS.CTRL_OR_CMD] &&
|
|
event[KEYS.CTRL_OR_CMD] &&
|
|
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
|
|
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
|
|
@@ -4462,6 +4479,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
this.lastPointerDownEvent = event;
|
|
this.lastPointerDownEvent = event;
|
|
|
|
|
|
this.setState({
|
|
this.setState({
|
|
@@ -4470,10 +4491,6 @@ class App extends React.Component<AppProps, AppState> {
|
|
});
|
|
});
|
|
this.savePointer(event.clientX, event.clientY, "down");
|
|
this.savePointer(event.clientX, event.clientY, "down");
|
|
|
|
|
|
- if (this.handleCanvasPanUsingWheelOrSpaceDrag(event)) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// only handle left mouse button or touch
|
|
// only handle left mouse button or touch
|
|
if (
|
|
if (
|
|
event.button !== POINTER_BUTTON.MAIN &&
|
|
event.button !== POINTER_BUTTON.MAIN &&
|
|
@@ -4564,6 +4581,11 @@ class App extends React.Component<AppProps, AppState> {
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO);
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO);
|
|
} else if (this.state.activeTool.type === "frame") {
|
|
} else if (this.state.activeTool.type === "frame") {
|
|
this.createFrameElementOnPointerDown(pointerDownState);
|
|
this.createFrameElementOnPointerDown(pointerDownState);
|
|
|
|
+ } else if (this.state.activeTool.type === "laser") {
|
|
|
|
+ this.laserPathManager.startPath(
|
|
|
|
+ pointerDownState.lastCoords.x,
|
|
|
|
+ pointerDownState.lastCoords.y,
|
|
|
|
+ );
|
|
} else if (
|
|
} else if (
|
|
this.state.activeTool.type !== "eraser" &&
|
|
this.state.activeTool.type !== "eraser" &&
|
|
this.state.activeTool.type !== "hand"
|
|
this.state.activeTool.type !== "hand"
|
|
@@ -4587,7 +4609,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
lastPointerUp = onPointerUp;
|
|
lastPointerUp = onPointerUp;
|
|
|
|
|
|
- if (!this.state.viewModeEnabled) {
|
|
|
|
|
|
+ if (!this.state.viewModeEnabled || this.state.activeTool.type === "laser") {
|
|
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
|
window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
|
|
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
|
window.addEventListener(EVENT.POINTER_UP, onPointerUp);
|
|
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
|
window.addEventListener(EVENT.KEYDOWN, onKeyDown);
|
|
@@ -5783,6 +5805,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (this.state.activeTool.type === "laser") {
|
|
|
|
+ this.laserPathManager.addPointToPath(pointerCoords.x, pointerCoords.y);
|
|
|
|
+ }
|
|
|
|
+
|
|
const [gridX, gridY] = getGridPoint(
|
|
const [gridX, gridY] = getGridPoint(
|
|
pointerCoords.x,
|
|
pointerCoords.x,
|
|
pointerCoords.y,
|
|
pointerCoords.y,
|
|
@@ -7029,6 +7055,11 @@ class App extends React.Component<AppProps, AppState> {
|
|
: unbindLinearElements)(this.scene.getSelectedElements(this.state));
|
|
: unbindLinearElements)(this.scene.getSelectedElements(this.state));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (activeTool.type === "laser") {
|
|
|
|
+ this.laserPathManager.endPath();
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!activeTool.locked && activeTool.type !== "freedraw") {
|
|
if (!activeTool.locked && activeTool.type !== "freedraw") {
|
|
resetCursor(this.interactiveCanvas);
|
|
resetCursor(this.interactiveCanvas);
|
|
this.setState({
|
|
this.setState({
|
|
@@ -8273,15 +8304,21 @@ class App extends React.Component<AppProps, AppState> {
|
|
if (!x || !y) {
|
|
if (!x || !y) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- const pointer = viewportCoordsToSceneCoords(
|
|
|
|
|
|
+ const { x: sceneX, y: sceneY } = viewportCoordsToSceneCoords(
|
|
{ clientX: x, clientY: y },
|
|
{ clientX: x, clientY: y },
|
|
this.state,
|
|
this.state,
|
|
);
|
|
);
|
|
|
|
|
|
- if (isNaN(pointer.x) || isNaN(pointer.y)) {
|
|
|
|
|
|
+ if (isNaN(sceneX) || isNaN(sceneY)) {
|
|
// sometimes the pointer goes off screen
|
|
// sometimes the pointer goes off screen
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ const pointer: CollaboratorPointer = {
|
|
|
|
+ x: sceneX,
|
|
|
|
+ y: sceneY,
|
|
|
|
+ tool: this.state.activeTool.type === "laser" ? "laser" : "pointer",
|
|
|
|
+ };
|
|
|
|
+
|
|
this.props.onPointerUpdate?.({
|
|
this.props.onPointerUpdate?.({
|
|
pointer,
|
|
pointer,
|
|
button,
|
|
button,
|