|
@@ -122,10 +122,7 @@ import {
|
|
|
|
|
|
import { LinearElementEditor } from "@excalidraw/element/linearElementEditor";
|
|
import { LinearElementEditor } from "@excalidraw/element/linearElementEditor";
|
|
|
|
|
|
-import {
|
|
|
|
- mutateElement,
|
|
|
|
- newElementWith,
|
|
|
|
-} from "@excalidraw/element/mutateElement";
|
|
|
|
|
|
+import { newElementWith } from "@excalidraw/element/mutateElement";
|
|
|
|
|
|
import {
|
|
import {
|
|
newFrameElement,
|
|
newFrameElement,
|
|
@@ -303,6 +300,10 @@ import {
|
|
|
|
|
|
import { isNonDeletedElement } from "@excalidraw/element";
|
|
import { isNonDeletedElement } from "@excalidraw/element";
|
|
|
|
|
|
|
|
+import Scene from "@excalidraw/element/Scene";
|
|
|
|
+
|
|
|
|
+import type { ElementUpdate } from "@excalidraw/element/mutateElement";
|
|
|
|
+
|
|
import type { LocalPoint, Radians } from "@excalidraw/math";
|
|
import type { LocalPoint, Radians } from "@excalidraw/math";
|
|
|
|
|
|
import type {
|
|
import type {
|
|
@@ -328,9 +329,10 @@ import type {
|
|
MagicGenerationData,
|
|
MagicGenerationData,
|
|
ExcalidrawNonSelectionElement,
|
|
ExcalidrawNonSelectionElement,
|
|
ExcalidrawArrowElement,
|
|
ExcalidrawArrowElement,
|
|
|
|
+ ExcalidrawElbowArrowElement,
|
|
} from "@excalidraw/element/types";
|
|
} from "@excalidraw/element/types";
|
|
|
|
|
|
-import type { ValueOf } from "@excalidraw/common/utility-types";
|
|
|
|
|
|
+import type { Mutable, ValueOf } from "@excalidraw/common/utility-types";
|
|
|
|
|
|
import {
|
|
import {
|
|
actionAddToLibrary,
|
|
actionAddToLibrary,
|
|
@@ -403,7 +405,6 @@ import {
|
|
hasBackground,
|
|
hasBackground,
|
|
isSomeElementSelected,
|
|
isSomeElementSelected,
|
|
} from "../scene";
|
|
} from "../scene";
|
|
-import Scene from "../scene/Scene";
|
|
|
|
import { getStateForZoom } from "../scene/zoom";
|
|
import { getStateForZoom } from "../scene/zoom";
|
|
import {
|
|
import {
|
|
dataURLToFile,
|
|
dataURLToFile,
|
|
@@ -760,6 +761,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
if (excalidrawAPI) {
|
|
if (excalidrawAPI) {
|
|
const api: ExcalidrawImperativeAPI = {
|
|
const api: ExcalidrawImperativeAPI = {
|
|
updateScene: this.updateScene,
|
|
updateScene: this.updateScene,
|
|
|
|
+ mutateElement: this.mutateElement,
|
|
updateLibrary: this.library.updateLibrary,
|
|
updateLibrary: this.library.updateLibrary,
|
|
addFiles: this.addFiles,
|
|
addFiles: this.addFiles,
|
|
resetScene: this.resetScene,
|
|
resetScene: this.resetScene,
|
|
@@ -1387,7 +1389,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
private resetEditingFrame = (frame: ExcalidrawFrameLikeElement | null) => {
|
|
private resetEditingFrame = (frame: ExcalidrawFrameLikeElement | null) => {
|
|
if (frame) {
|
|
if (frame) {
|
|
- mutateElement(frame, { name: frame.name?.trim() || null });
|
|
|
|
|
|
+ this.scene.mutateElement(frame, { name: frame.name?.trim() || null });
|
|
}
|
|
}
|
|
this.setState({ editingFrame: null });
|
|
this.setState({ editingFrame: null });
|
|
};
|
|
};
|
|
@@ -1444,7 +1446,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
autoFocus
|
|
autoFocus
|
|
value={frameNameInEdit}
|
|
value={frameNameInEdit}
|
|
onChange={(e) => {
|
|
onChange={(e) => {
|
|
- mutateElement(f, {
|
|
|
|
|
|
+ this.scene.mutateElement(f, {
|
|
name: e.target.value,
|
|
name: e.target.value,
|
|
});
|
|
});
|
|
}}
|
|
}}
|
|
@@ -1670,7 +1672,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
<Hyperlink
|
|
<Hyperlink
|
|
key={firstSelectedElement.id}
|
|
key={firstSelectedElement.id}
|
|
element={firstSelectedElement}
|
|
element={firstSelectedElement}
|
|
- elementsMap={allElementsMap}
|
|
|
|
|
|
+ scene={this.scene}
|
|
setAppState={this.setAppState}
|
|
setAppState={this.setAppState}
|
|
onLinkOpen={this.props.onLinkOpen}
|
|
onLinkOpen={this.props.onLinkOpen}
|
|
setToast={this.setToast}
|
|
setToast={this.setToast}
|
|
@@ -1938,16 +1940,20 @@ class App extends React.Component<AppProps, AppState> {
|
|
// state only.
|
|
// state only.
|
|
// Thus reset so that we prefer local cache (if there was some
|
|
// Thus reset so that we prefer local cache (if there was some
|
|
// generationData set previously)
|
|
// generationData set previously)
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
frameElement,
|
|
frameElement,
|
|
- { customData: { generationData: undefined } },
|
|
|
|
- false,
|
|
|
|
|
|
+ {
|
|
|
|
+ customData: { generationData: undefined },
|
|
|
|
+ },
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
} else {
|
|
} else {
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
frameElement,
|
|
frameElement,
|
|
- { customData: { generationData: data } },
|
|
|
|
- false,
|
|
|
|
|
|
+ {
|
|
|
|
+ customData: { generationData: data },
|
|
|
|
+ },
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
}
|
|
}
|
|
this.magicGenerations.set(frameElement.id, data);
|
|
this.magicGenerations.set(frameElement.id, data);
|
|
@@ -2119,7 +2125,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
this.scene.insertElement(frame);
|
|
this.scene.insertElement(frame);
|
|
|
|
|
|
for (const child of selectedElements) {
|
|
for (const child of selectedElements) {
|
|
- mutateElement(child, { frameId: frame.id });
|
|
|
|
|
|
+ this.scene.mutateElement(child, { frameId: frame.id });
|
|
}
|
|
}
|
|
|
|
|
|
this.setState({
|
|
this.setState({
|
|
@@ -2918,8 +2924,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
nonDeletedElementsMap,
|
|
nonDeletedElementsMap,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
- this.scene.getNonDeletedElements(),
|
|
|
|
|
|
+ this.scene,
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3317,11 +3322,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
newElement,
|
|
newElement,
|
|
this.scene.getElementsMapIncludingDeleted(),
|
|
this.scene.getElementsMapIncludingDeleted(),
|
|
);
|
|
);
|
|
- redrawTextBoundingBox(
|
|
|
|
- newElement,
|
|
|
|
- container,
|
|
|
|
- this.scene.getElementsMapIncludingDeleted(),
|
|
|
|
- );
|
|
|
|
|
|
+ redrawTextBoundingBox(newElement, container, this.scene);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
@@ -3444,7 +3445,11 @@ class App extends React.Component<AppProps, AppState> {
|
|
}
|
|
}
|
|
// hack to reset the `y` coord because we vertically center during
|
|
// hack to reset the `y` coord because we vertically center during
|
|
// insertImageElement
|
|
// insertImageElement
|
|
- mutateElement(initializedImageElement, { y }, false);
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
|
|
+ initializedImageElement,
|
|
|
|
+ { y },
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
|
|
+ );
|
|
|
|
|
|
y = imageElement.y + imageElement.height + 25;
|
|
y = imageElement.y + imageElement.height + 25;
|
|
|
|
|
|
@@ -3998,6 +4003,17 @@ class App extends React.Component<AppProps, AppState> {
|
|
},
|
|
},
|
|
);
|
|
);
|
|
|
|
|
|
|
|
+ public mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
|
|
|
|
+ element: TElement,
|
|
|
|
+ updates: ElementUpdate<TElement>,
|
|
|
|
+ informMutation = true,
|
|
|
|
+ ) => {
|
|
|
|
+ return this.scene.mutateElement(element, updates, {
|
|
|
|
+ informMutation,
|
|
|
|
+ isDragging: false,
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
private triggerRender = (
|
|
private triggerRender = (
|
|
/** force always re-renders canvas even if no change */
|
|
/** force always re-renders canvas even if no change */
|
|
force?: boolean,
|
|
force?: boolean,
|
|
@@ -4166,9 +4182,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
) {
|
|
) {
|
|
this.flowChartCreator.createNodes(
|
|
this.flowChartCreator.createNodes(
|
|
selectedElements[0],
|
|
selectedElements[0],
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
this.state,
|
|
this.state,
|
|
getLinkDirectionFromKey(event.key),
|
|
getLinkDirectionFromKey(event.key),
|
|
|
|
+ this.scene,
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4410,16 +4426,16 @@ class App extends React.Component<AppProps, AppState> {
|
|
}
|
|
}
|
|
|
|
|
|
selectedElements.forEach((element) => {
|
|
selectedElements.forEach((element) => {
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
element,
|
|
element,
|
|
{
|
|
{
|
|
x: element.x + offsetX,
|
|
x: element.x + offsetX,
|
|
y: element.y + offsetY,
|
|
y: element.y + offsetY,
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
|
|
|
|
- updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
|
|
|
|
|
|
+ updateBoundElements(element, this.scene, {
|
|
simultaneouslyUpdated: selectedElements,
|
|
simultaneouslyUpdated: selectedElements,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
@@ -4453,6 +4469,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
this.setState({
|
|
this.setState({
|
|
editingLinearElement: new LinearElementEditor(
|
|
editingLinearElement: new LinearElementEditor(
|
|
selectedElement,
|
|
selectedElement,
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
),
|
|
),
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -4646,11 +4663,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
if (isArrowKey(event.key)) {
|
|
if (isArrowKey(event.key)) {
|
|
bindOrUnbindLinearElements(
|
|
bindOrUnbindLinearElements(
|
|
this.scene.getSelectedElements(this.state).filter(isLinearElement),
|
|
this.scene.getSelectedElements(this.state).filter(isLinearElement),
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
- this.scene.getNonDeletedElements(),
|
|
|
|
- this.scene,
|
|
|
|
isBindingEnabled(this.state),
|
|
isBindingEnabled(this.state),
|
|
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
|
|
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
|
|
|
|
+ this.scene,
|
|
this.state.zoom,
|
|
this.state.zoom,
|
|
);
|
|
);
|
|
this.setState({ suggestedBindings: [] });
|
|
this.setState({ suggestedBindings: [] });
|
|
@@ -4957,7 +4972,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
onChange: withBatchedUpdates((nextOriginalText) => {
|
|
onChange: withBatchedUpdates((nextOriginalText) => {
|
|
updateElement(nextOriginalText, false);
|
|
updateElement(nextOriginalText, false);
|
|
if (isNonDeletedElement(element)) {
|
|
if (isNonDeletedElement(element)) {
|
|
- updateBoundElements(element, this.scene.getNonDeletedElementsMap());
|
|
|
|
|
|
+ updateBoundElements(element, this.scene);
|
|
}
|
|
}
|
|
}),
|
|
}),
|
|
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
|
|
onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
|
|
@@ -5320,7 +5335,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
const minHeight = getApproxMinLineHeight(fontSize, lineHeight);
|
|
const minHeight = getApproxMinLineHeight(fontSize, lineHeight);
|
|
const newHeight = Math.max(container.height, minHeight);
|
|
const newHeight = Math.max(container.height, minHeight);
|
|
const newWidth = Math.max(container.width, minWidth);
|
|
const newWidth = Math.max(container.width, minWidth);
|
|
- mutateElement(container, { height: newHeight, width: newWidth });
|
|
|
|
|
|
+ this.scene.mutateElement(container, {
|
|
|
|
+ height: newHeight,
|
|
|
|
+ width: newWidth,
|
|
|
|
+ });
|
|
sceneX = container.x + newWidth / 2;
|
|
sceneX = container.x + newWidth / 2;
|
|
sceneY = container.y + newHeight / 2;
|
|
sceneY = container.y + newHeight / 2;
|
|
if (parentCenterPosition) {
|
|
if (parentCenterPosition) {
|
|
@@ -5371,7 +5389,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
});
|
|
});
|
|
|
|
|
|
if (!existingTextElement && shouldBindToContainer && container) {
|
|
if (!existingTextElement && shouldBindToContainer && container) {
|
|
- mutateElement(container, {
|
|
|
|
|
|
+ this.scene.mutateElement(container, {
|
|
boundElements: (container.boundElements || []).concat({
|
|
boundElements: (container.boundElements || []).concat({
|
|
type: "text",
|
|
type: "text",
|
|
id: element.id,
|
|
id: element.id,
|
|
@@ -5447,7 +5465,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
) {
|
|
) {
|
|
this.store.shouldCaptureIncrement();
|
|
this.store.shouldCaptureIncrement();
|
|
this.setState({
|
|
this.setState({
|
|
- editingLinearElement: new LinearElementEditor(selectedElements[0]),
|
|
|
|
|
|
+ editingLinearElement: new LinearElementEditor(
|
|
|
|
+ selectedElements[0],
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ ),
|
|
});
|
|
});
|
|
return;
|
|
return;
|
|
} else if (
|
|
} else if (
|
|
@@ -5471,7 +5492,11 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
if (midPoint && midPoint > -1) {
|
|
if (midPoint && midPoint > -1) {
|
|
this.store.shouldCaptureIncrement();
|
|
this.store.shouldCaptureIncrement();
|
|
- LinearElementEditor.deleteFixedSegment(selectedElements[0], midPoint);
|
|
|
|
|
|
+ LinearElementEditor.deleteFixedSegment(
|
|
|
|
+ selectedElements[0],
|
|
|
|
+ this.scene,
|
|
|
|
+ midPoint,
|
|
|
|
+ );
|
|
|
|
|
|
const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords(
|
|
const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords(
|
|
{
|
|
{
|
|
@@ -5854,7 +5879,6 @@ class App extends React.Component<AppProps, AppState> {
|
|
scenePointerX,
|
|
scenePointerX,
|
|
scenePointerY,
|
|
scenePointerY,
|
|
this,
|
|
this,
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
);
|
|
);
|
|
|
|
|
|
if (
|
|
if (
|
|
@@ -5916,7 +5940,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
lastPoint,
|
|
lastPoint,
|
|
) >= LINE_CONFIRM_THRESHOLD
|
|
) >= LINE_CONFIRM_THRESHOLD
|
|
) {
|
|
) {
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
multiElement,
|
|
multiElement,
|
|
{
|
|
{
|
|
points: [
|
|
points: [
|
|
@@ -5924,7 +5948,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
pointFrom<LocalPoint>(scenePointerX - rx, scenePointerY - ry),
|
|
pointFrom<LocalPoint>(scenePointerX - rx, scenePointerY - ry),
|
|
],
|
|
],
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
} else {
|
|
} else {
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
@@ -5940,12 +5964,12 @@ class App extends React.Component<AppProps, AppState> {
|
|
) < LINE_CONFIRM_THRESHOLD
|
|
) < LINE_CONFIRM_THRESHOLD
|
|
) {
|
|
) {
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
multiElement,
|
|
multiElement,
|
|
{
|
|
{
|
|
points: points.slice(0, -1),
|
|
points: points.slice(0, -1),
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
} else {
|
|
} else {
|
|
const [gridX, gridY] = getGridPoint(
|
|
const [gridX, gridY] = getGridPoint(
|
|
@@ -5977,8 +6001,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
if (isPathALoop(points, this.state.zoom.value)) {
|
|
if (isPathALoop(points, this.state.zoom.value)) {
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
}
|
|
}
|
|
|
|
+
|
|
// update last uncommitted point
|
|
// update last uncommitted point
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
multiElement,
|
|
multiElement,
|
|
{
|
|
{
|
|
points: [
|
|
points: [
|
|
@@ -5989,9 +6014,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
),
|
|
),
|
|
],
|
|
],
|
|
},
|
|
},
|
|
- false,
|
|
|
|
{
|
|
{
|
|
isDragging: true,
|
|
isDragging: true,
|
|
|
|
+ informMutation: false,
|
|
},
|
|
},
|
|
);
|
|
);
|
|
|
|
|
|
@@ -6578,7 +6603,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
const frame = this.getTopLayerFrameAtSceneCoords({ x, y });
|
|
const frame = this.getTopLayerFrameAtSceneCoords({ x, y });
|
|
|
|
|
|
- mutateElement(pendingImageElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(pendingImageElement, {
|
|
x,
|
|
x,
|
|
y,
|
|
y,
|
|
frameId: frame ? frame.id : null,
|
|
frameId: frame ? frame.id : null,
|
|
@@ -7633,7 +7658,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
multiElement.type === "line" &&
|
|
multiElement.type === "line" &&
|
|
isPathALoop(multiElement.points, this.state.zoom.value)
|
|
isPathALoop(multiElement.points, this.state.zoom.value)
|
|
) {
|
|
) {
|
|
- mutateElement(multiElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(multiElement, {
|
|
lastCommittedPoint:
|
|
lastCommittedPoint:
|
|
multiElement.points[multiElement.points.length - 1],
|
|
multiElement.points[multiElement.points.length - 1],
|
|
});
|
|
});
|
|
@@ -7644,7 +7669,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
// Elbow arrows cannot be created by putting down points
|
|
// Elbow arrows cannot be created by putting down points
|
|
// only the start and end points can be defined
|
|
// only the start and end points can be defined
|
|
if (isElbowArrow(multiElement) && multiElement.points.length > 1) {
|
|
if (isElbowArrow(multiElement) && multiElement.points.length > 1) {
|
|
- mutateElement(multiElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(multiElement, {
|
|
lastCommittedPoint:
|
|
lastCommittedPoint:
|
|
multiElement.points[multiElement.points.length - 1],
|
|
multiElement.points[multiElement.points.length - 1],
|
|
});
|
|
});
|
|
@@ -7681,7 +7706,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
}));
|
|
}));
|
|
// clicking outside commit zone → update reference for last committed
|
|
// clicking outside commit zone → update reference for last committed
|
|
// point
|
|
// point
|
|
- mutateElement(multiElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(multiElement, {
|
|
lastCommittedPoint: multiElement.points[multiElement.points.length - 1],
|
|
lastCommittedPoint: multiElement.points[multiElement.points.length - 1],
|
|
});
|
|
});
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
@@ -7767,7 +7792,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
),
|
|
),
|
|
};
|
|
};
|
|
});
|
|
});
|
|
- mutateElement(element, {
|
|
|
|
|
|
+ this.scene.mutateElement(element, {
|
|
points: [...element.points, pointFrom<LocalPoint>(0, 0)],
|
|
points: [...element.points, pointFrom<LocalPoint>(0, 0)],
|
|
});
|
|
});
|
|
const boundElement = getHoveredElementForBinding(
|
|
const boundElement = getHoveredElementForBinding(
|
|
@@ -8017,7 +8042,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
index,
|
|
index,
|
|
gridX,
|
|
gridX,
|
|
gridY,
|
|
gridY,
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
|
|
+ this.scene,
|
|
);
|
|
);
|
|
|
|
|
|
flushSync(() => {
|
|
flushSync(() => {
|
|
@@ -8122,7 +8147,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
pointerCoords,
|
|
pointerCoords,
|
|
this,
|
|
this,
|
|
!event[KEYS.CTRL_OR_CMD],
|
|
!event[KEYS.CTRL_OR_CMD],
|
|
- elementsMap,
|
|
|
|
|
|
+ this.scene,
|
|
);
|
|
);
|
|
if (!ret) {
|
|
if (!ret) {
|
|
return;
|
|
return;
|
|
@@ -8349,7 +8374,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
),
|
|
),
|
|
};
|
|
};
|
|
|
|
|
|
- mutateElement(croppingElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(croppingElement, {
|
|
crop: nextCrop,
|
|
crop: nextCrop,
|
|
});
|
|
});
|
|
|
|
|
|
@@ -8606,13 +8631,16 @@ class App extends React.Component<AppProps, AppState> {
|
|
? newElement.pressures
|
|
? newElement.pressures
|
|
: [...newElement.pressures, event.pressure];
|
|
: [...newElement.pressures, event.pressure];
|
|
|
|
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
newElement,
|
|
newElement,
|
|
{
|
|
{
|
|
points: [...points, pointFrom<LocalPoint>(dx, dy)],
|
|
points: [...points, pointFrom<LocalPoint>(dx, dy)],
|
|
pressures,
|
|
pressures,
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ {
|
|
|
|
+ informMutation: false,
|
|
|
|
+ isDragging: false,
|
|
|
|
+ },
|
|
);
|
|
);
|
|
|
|
|
|
this.setState({
|
|
this.setState({
|
|
@@ -8635,24 +8663,23 @@ class App extends React.Component<AppProps, AppState> {
|
|
}
|
|
}
|
|
|
|
|
|
if (points.length === 1) {
|
|
if (points.length === 1) {
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
newElement,
|
|
newElement,
|
|
{
|
|
{
|
|
points: [...points, pointFrom<LocalPoint>(dx, dy)],
|
|
points: [...points, pointFrom<LocalPoint>(dx, dy)],
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
} else if (
|
|
} else if (
|
|
points.length === 2 ||
|
|
points.length === 2 ||
|
|
(points.length > 1 && isElbowArrow(newElement))
|
|
(points.length > 1 && isElbowArrow(newElement))
|
|
) {
|
|
) {
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
newElement,
|
|
newElement,
|
|
{
|
|
{
|
|
points: [...points.slice(0, -1), pointFrom<LocalPoint>(dx, dy)],
|
|
points: [...points.slice(0, -1), pointFrom<LocalPoint>(dx, dy)],
|
|
},
|
|
},
|
|
- false,
|
|
|
|
- { isDragging: true },
|
|
|
|
|
|
+ { isDragging: true, informMutation: false },
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -8763,7 +8790,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
selectedLinearElement:
|
|
selectedLinearElement:
|
|
elementsWithinSelection.length === 1 &&
|
|
elementsWithinSelection.length === 1 &&
|
|
isLinearElement(elementsWithinSelection[0])
|
|
isLinearElement(elementsWithinSelection[0])
|
|
- ? new LinearElementEditor(elementsWithinSelection[0])
|
|
|
|
|
|
+ ? new LinearElementEditor(
|
|
|
|
+ elementsWithinSelection[0],
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ )
|
|
: null,
|
|
: null,
|
|
showHyperlinkPopup:
|
|
showHyperlinkPopup:
|
|
elementsWithinSelection.length === 1 &&
|
|
elementsWithinSelection.length === 1 &&
|
|
@@ -8869,7 +8899,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
.map((e) => elementsMap.get(e.id))
|
|
.map((e) => elementsMap.get(e.id))
|
|
.filter((e) => isElbowArrow(e))
|
|
.filter((e) => isElbowArrow(e))
|
|
.forEach((e) => {
|
|
.forEach((e) => {
|
|
- !!e && mutateElement(e, {}, true);
|
|
|
|
|
|
+ !!e && this.scene.mutateElement(e, {});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -8905,7 +8935,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
this.scene.getNonDeletedElementsMap(),
|
|
this.scene.getNonDeletedElementsMap(),
|
|
);
|
|
);
|
|
if (element) {
|
|
if (element) {
|
|
- mutateElement(element, {}, true);
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
|
|
+ element as ExcalidrawElbowArrowElement,
|
|
|
|
+ {},
|
|
|
|
+ );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -8934,7 +8967,6 @@ class App extends React.Component<AppProps, AppState> {
|
|
element,
|
|
element,
|
|
startBindingElement,
|
|
startBindingElement,
|
|
endBindingElement,
|
|
endBindingElement,
|
|
- elementsMap,
|
|
|
|
this.scene,
|
|
this.scene,
|
|
);
|
|
);
|
|
}
|
|
}
|
|
@@ -9001,7 +9033,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
? []
|
|
? []
|
|
: [...newElement.pressures, childEvent.pressure];
|
|
: [...newElement.pressures, childEvent.pressure];
|
|
|
|
|
|
- mutateElement(newElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(newElement, {
|
|
points: [...points, pointFrom<LocalPoint>(dx, dy)],
|
|
points: [...points, pointFrom<LocalPoint>(dx, dy)],
|
|
pressures,
|
|
pressures,
|
|
lastCommittedPoint: pointFrom<LocalPoint>(dx, dy),
|
|
lastCommittedPoint: pointFrom<LocalPoint>(dx, dy),
|
|
@@ -9048,15 +9080,20 @@ class App extends React.Component<AppProps, AppState> {
|
|
);
|
|
);
|
|
|
|
|
|
if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
|
|
if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
|
|
- mutateElement(newElement, {
|
|
|
|
- points: [
|
|
|
|
- ...newElement.points,
|
|
|
|
- pointFrom<LocalPoint>(
|
|
|
|
- pointerCoords.x - newElement.x,
|
|
|
|
- pointerCoords.y - newElement.y,
|
|
|
|
- ),
|
|
|
|
- ],
|
|
|
|
- });
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
|
|
+ newElement,
|
|
|
|
+ {
|
|
|
|
+ points: [
|
|
|
|
+ ...newElement.points,
|
|
|
|
+ pointFrom<LocalPoint>(
|
|
|
|
+ pointerCoords.x - newElement.x,
|
|
|
|
+ pointerCoords.y - newElement.y,
|
|
|
|
+ ),
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
|
|
+ );
|
|
|
|
+
|
|
this.setState({
|
|
this.setState({
|
|
multiElement: newElement,
|
|
multiElement: newElement,
|
|
newElement,
|
|
newElement,
|
|
@@ -9070,8 +9107,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
newElement,
|
|
newElement,
|
|
this.state,
|
|
this.state,
|
|
pointerCoords,
|
|
pointerCoords,
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
- this.scene.getNonDeletedElements(),
|
|
|
|
|
|
+ this.scene,
|
|
);
|
|
);
|
|
}
|
|
}
|
|
this.setState({ suggestedBindings: [], startBoundElement: null });
|
|
this.setState({ suggestedBindings: [], startBoundElement: null });
|
|
@@ -9089,7 +9125,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
},
|
|
},
|
|
prevState,
|
|
prevState,
|
|
),
|
|
),
|
|
- selectedLinearElement: new LinearElementEditor(newElement),
|
|
|
|
|
|
+ selectedLinearElement: new LinearElementEditor(
|
|
|
|
+ newElement,
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ ),
|
|
}));
|
|
}));
|
|
} else {
|
|
} else {
|
|
this.setState((prevState) => ({
|
|
this.setState((prevState) => ({
|
|
@@ -9112,7 +9151,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
);
|
|
);
|
|
|
|
|
|
if (newElement.width < minWidth) {
|
|
if (newElement.width < minWidth) {
|
|
- mutateElement(newElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(newElement, {
|
|
autoResize: true,
|
|
autoResize: true,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
@@ -9162,7 +9201,14 @@ class App extends React.Component<AppProps, AppState> {
|
|
}
|
|
}
|
|
|
|
|
|
if (newElement) {
|
|
if (newElement) {
|
|
- mutateElement(newElement, getNormalizedDimensions(newElement));
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
|
|
+ newElement,
|
|
|
|
+ getNormalizedDimensions(newElement),
|
|
|
|
+ {
|
|
|
|
+ informMutation: false,
|
|
|
|
+ isDragging: false,
|
|
|
|
+ },
|
|
|
|
+ );
|
|
// the above does not guarantee the scene to be rendered again, hence the trigger below
|
|
// the above does not guarantee the scene to be rendered again, hence the trigger below
|
|
this.scene.triggerUpdate();
|
|
this.scene.triggerUpdate();
|
|
}
|
|
}
|
|
@@ -9194,7 +9240,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
) {
|
|
) {
|
|
// remove the linear element from all groups
|
|
// remove the linear element from all groups
|
|
// before removing it from the frame as well
|
|
// before removing it from the frame as well
|
|
- mutateElement(linearElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(linearElement, {
|
|
groupIds: [],
|
|
groupIds: [],
|
|
});
|
|
});
|
|
|
|
|
|
@@ -9223,12 +9269,12 @@ class App extends React.Component<AppProps, AppState> {
|
|
this.state.editingGroupId!,
|
|
this.state.editingGroupId!,
|
|
);
|
|
);
|
|
|
|
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
element,
|
|
element,
|
|
{
|
|
{
|
|
groupIds: element.groupIds.slice(0, index),
|
|
groupIds: element.groupIds.slice(0, index),
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -9240,12 +9286,12 @@ class App extends React.Component<AppProps, AppState> {
|
|
element.groupIds[element.groupIds.length - 1],
|
|
element.groupIds[element.groupIds.length - 1],
|
|
).length < 2
|
|
).length < 2
|
|
) {
|
|
) {
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
element,
|
|
element,
|
|
{
|
|
{
|
|
groupIds: [],
|
|
groupIds: [],
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
);
|
|
);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
@@ -9355,7 +9401,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
// the one we've hit
|
|
// the one we've hit
|
|
if (selectedElements.length === 1) {
|
|
if (selectedElements.length === 1) {
|
|
this.setState({
|
|
this.setState({
|
|
- selectedLinearElement: new LinearElementEditor(hitElement),
|
|
|
|
|
|
+ selectedLinearElement: new LinearElementEditor(
|
|
|
|
+ hitElement,
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ ),
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -9480,7 +9529,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
selectedLinearElement:
|
|
selectedLinearElement:
|
|
newSelectedElements.length === 1 &&
|
|
newSelectedElements.length === 1 &&
|
|
isLinearElement(newSelectedElements[0])
|
|
isLinearElement(newSelectedElements[0])
|
|
- ? new LinearElementEditor(newSelectedElements[0])
|
|
|
|
|
|
+ ? new LinearElementEditor(
|
|
|
|
+ newSelectedElements[0],
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ )
|
|
: prevState.selectedLinearElement,
|
|
: prevState.selectedLinearElement,
|
|
};
|
|
};
|
|
});
|
|
});
|
|
@@ -9554,7 +9606,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
// Don't set `selectedLinearElement` if its same as the hitElement, this is mainly to prevent resetting the `hoverPointIndex` to -1.
|
|
// Don't set `selectedLinearElement` if its same as the hitElement, this is mainly to prevent resetting the `hoverPointIndex` to -1.
|
|
// Future we should update the API to take care of setting the correct `hoverPointIndex` when initialized
|
|
// Future we should update the API to take care of setting the correct `hoverPointIndex` when initialized
|
|
prevState.selectedLinearElement?.elementId !== hitElement.id
|
|
prevState.selectedLinearElement?.elementId !== hitElement.id
|
|
- ? new LinearElementEditor(hitElement)
|
|
|
|
|
|
+ ? new LinearElementEditor(
|
|
|
|
+ hitElement,
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ )
|
|
: prevState.selectedLinearElement,
|
|
: prevState.selectedLinearElement,
|
|
}));
|
|
}));
|
|
}
|
|
}
|
|
@@ -9647,11 +9702,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
bindOrUnbindLinearElements(
|
|
bindOrUnbindLinearElements(
|
|
linearElements,
|
|
linearElements,
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
- this.scene.getNonDeletedElements(),
|
|
|
|
- this.scene,
|
|
|
|
isBindingEnabled(this.state),
|
|
isBindingEnabled(this.state),
|
|
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
|
|
this.state.selectedLinearElement?.selectedPointsIndices ?? [],
|
|
|
|
+ this.scene,
|
|
this.state.zoom,
|
|
this.state.zoom,
|
|
);
|
|
);
|
|
}
|
|
}
|
|
@@ -9808,12 +9861,12 @@ class App extends React.Component<AppProps, AppState> {
|
|
const dataURL =
|
|
const dataURL =
|
|
this.files[fileId]?.dataURL || (await getDataURL(imageFile));
|
|
this.files[fileId]?.dataURL || (await getDataURL(imageFile));
|
|
|
|
|
|
- const imageElement = mutateElement(
|
|
|
|
|
|
+ const imageElement = this.scene.mutateElement(
|
|
_imageElement,
|
|
_imageElement,
|
|
{
|
|
{
|
|
fileId,
|
|
fileId,
|
|
},
|
|
},
|
|
- false,
|
|
|
|
|
|
+ { informMutation: false, isDragging: false },
|
|
) as NonDeleted<InitializedExcalidrawImageElement>;
|
|
) as NonDeleted<InitializedExcalidrawImageElement>;
|
|
|
|
|
|
return new Promise<NonDeleted<InitializedExcalidrawImageElement>>(
|
|
return new Promise<NonDeleted<InitializedExcalidrawImageElement>>(
|
|
@@ -9879,7 +9932,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
showCursorImagePreview,
|
|
showCursorImagePreview,
|
|
});
|
|
});
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
- mutateElement(imageElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(imageElement, {
|
|
isDeleted: true,
|
|
isDeleted: true,
|
|
});
|
|
});
|
|
this.actionManager.executeAction(actionFinalize);
|
|
this.actionManager.executeAction(actionFinalize);
|
|
@@ -10025,7 +10078,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
imageElement.height < DRAGGING_THRESHOLD / this.state.zoom.value
|
|
imageElement.height < DRAGGING_THRESHOLD / this.state.zoom.value
|
|
) {
|
|
) {
|
|
const placeholderSize = 100 / this.state.zoom.value;
|
|
const placeholderSize = 100 / this.state.zoom.value;
|
|
- mutateElement(imageElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(imageElement, {
|
|
x: imageElement.x - placeholderSize / 2,
|
|
x: imageElement.x - placeholderSize / 2,
|
|
y: imageElement.y - placeholderSize / 2,
|
|
y: imageElement.y - placeholderSize / 2,
|
|
width: placeholderSize,
|
|
width: placeholderSize,
|
|
@@ -10059,7 +10112,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
const x = imageElement.x + imageElement.width / 2 - width / 2;
|
|
const x = imageElement.x + imageElement.width / 2 - width / 2;
|
|
const y = imageElement.y + imageElement.height / 2 - height / 2;
|
|
const y = imageElement.y + imageElement.height / 2 - height / 2;
|
|
|
|
|
|
- mutateElement(imageElement, {
|
|
|
|
|
|
+ this.scene.mutateElement(imageElement, {
|
|
x,
|
|
x,
|
|
y,
|
|
y,
|
|
width,
|
|
width,
|
|
@@ -10490,7 +10543,10 @@ class App extends React.Component<AppProps, AppState> {
|
|
this,
|
|
this,
|
|
),
|
|
),
|
|
selectedLinearElement: isLinearElement(element)
|
|
selectedLinearElement: isLinearElement(element)
|
|
- ? new LinearElementEditor(element)
|
|
|
|
|
|
+ ? new LinearElementEditor(
|
|
|
|
+ element,
|
|
|
|
+ this.scene.getNonDeletedElementsMap(),
|
|
|
|
+ )
|
|
: null,
|
|
: null,
|
|
}
|
|
}
|
|
: this.state),
|
|
: this.state),
|
|
@@ -10523,8 +10579,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
height: distance(pointerDownState.origin.y, pointerCoords.y),
|
|
height: distance(pointerDownState.origin.y, pointerCoords.y),
|
|
shouldMaintainAspectRatio: shouldMaintainAspectRatio(event),
|
|
shouldMaintainAspectRatio: shouldMaintainAspectRatio(event),
|
|
shouldResizeFromCenter: false,
|
|
shouldResizeFromCenter: false,
|
|
|
|
+ scene: this.scene,
|
|
zoom: this.state.zoom.value,
|
|
zoom: this.state.zoom.value,
|
|
- informMutation,
|
|
|
|
|
|
+ informMutation: false,
|
|
});
|
|
});
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -10588,6 +10645,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
: shouldMaintainAspectRatio(event),
|
|
: shouldMaintainAspectRatio(event),
|
|
shouldResizeFromCenter: shouldResizeFromCenter(event),
|
|
shouldResizeFromCenter: shouldResizeFromCenter(event),
|
|
zoom: this.state.zoom.value,
|
|
zoom: this.state.zoom.value,
|
|
|
|
+ scene: this.scene,
|
|
widthAspectRatio: aspectRatio,
|
|
widthAspectRatio: aspectRatio,
|
|
originOffset: this.state.originSnapOffset,
|
|
originOffset: this.state.originSnapOffset,
|
|
informMutation,
|
|
informMutation,
|
|
@@ -10675,7 +10733,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
transformHandleType,
|
|
transformHandleType,
|
|
);
|
|
);
|
|
|
|
|
|
- mutateElement(
|
|
|
|
|
|
+ this.scene.mutateElement(
|
|
croppingElement,
|
|
croppingElement,
|
|
cropElement(
|
|
cropElement(
|
|
croppingElement,
|
|
croppingElement,
|
|
@@ -10690,16 +10748,12 @@ class App extends React.Component<AppProps, AppState> {
|
|
),
|
|
),
|
|
);
|
|
);
|
|
|
|
|
|
- updateBoundElements(
|
|
|
|
- croppingElement,
|
|
|
|
- this.scene.getNonDeletedElementsMap(),
|
|
|
|
- {
|
|
|
|
- newSize: {
|
|
|
|
- width: croppingElement.width,
|
|
|
|
- height: croppingElement.height,
|
|
|
|
- },
|
|
|
|
|
|
+ updateBoundElements(croppingElement, this.scene, {
|
|
|
|
+ newSize: {
|
|
|
|
+ width: croppingElement.width,
|
|
|
|
+ height: croppingElement.height,
|
|
},
|
|
},
|
|
- );
|
|
|
|
|
|
+ });
|
|
|
|
|
|
this.setState({
|
|
this.setState({
|
|
isCropping: transformHandleType && transformHandleType !== "rotation",
|
|
isCropping: transformHandleType && transformHandleType !== "rotation",
|
|
@@ -10813,7 +10867,6 @@ class App extends React.Component<AppProps, AppState> {
|
|
pointerDownState.originalElements,
|
|
pointerDownState.originalElements,
|
|
transformHandleType,
|
|
transformHandleType,
|
|
selectedElements,
|
|
selectedElements,
|
|
- this.scene.getElementsMapIncludingDeleted(),
|
|
|
|
this.scene,
|
|
this.scene,
|
|
shouldRotateWithDiscreteAngle(event),
|
|
shouldRotateWithDiscreteAngle(event),
|
|
shouldResizeFromCenter(event),
|
|
shouldResizeFromCenter(event),
|