mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
Move linear element handling out of App.tsx
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
parent
c2b78346c1
commit
3068787ac4
4 changed files with 532 additions and 420 deletions
|
@ -1,14 +1,7 @@
|
||||||
import {
|
import { average } from "@excalidraw/math";
|
||||||
average,
|
|
||||||
type GlobalPoint,
|
|
||||||
type LocalPoint,
|
|
||||||
pointTranslate,
|
|
||||||
vector,
|
|
||||||
} from "@excalidraw/math";
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
ExcalidrawElement,
|
|
||||||
FontFamilyValues,
|
FontFamilyValues,
|
||||||
FontString,
|
FontString,
|
||||||
} from "@excalidraw/element/types";
|
} from "@excalidraw/element/types";
|
||||||
|
@ -1208,6 +1201,3 @@ export const escapeDoubleQuotes = (str: string) => {
|
||||||
|
|
||||||
export const castArray = <T>(value: T | T[]): T[] =>
|
export const castArray = <T>(value: T | T[]): T[] =>
|
||||||
Array.isArray(value) ? value : [value];
|
Array.isArray(value) ? value : [value];
|
||||||
|
|
||||||
export const toLocalPoint = (p: GlobalPoint, element: ExcalidrawElement) =>
|
|
||||||
pointTranslate<GlobalPoint, LocalPoint>(p, vector(-element.x, -element.y));
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {
|
||||||
invariant,
|
invariant,
|
||||||
isDevEnv,
|
isDevEnv,
|
||||||
isTestEnv,
|
isTestEnv,
|
||||||
toLocalPoint,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -527,14 +526,18 @@ export const bindLinearElement = (
|
||||||
const points = Array.from(linearElement.points);
|
const points = Array.from(linearElement.points);
|
||||||
|
|
||||||
if (isArrowElement(linearElement)) {
|
if (isArrowElement(linearElement)) {
|
||||||
points[edgePointIndex] = toLocalPoint(
|
const [x, y] = bindPointToSnapToElementOutline(
|
||||||
bindPointToSnapToElementOutline(
|
|
||||||
linearElement,
|
|
||||||
hoveredElement,
|
|
||||||
startOrEnd,
|
|
||||||
elementsMap,
|
|
||||||
),
|
|
||||||
linearElement,
|
linearElement,
|
||||||
|
hoveredElement,
|
||||||
|
startOrEnd,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
points[edgePointIndex] = LinearElementEditor.createPointAt(
|
||||||
|
linearElement,
|
||||||
|
elementsMap,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,6 @@ import {
|
||||||
arrayToMap,
|
arrayToMap,
|
||||||
type EXPORT_IMAGE_TYPES,
|
type EXPORT_IMAGE_TYPES,
|
||||||
randomInteger,
|
randomInteger,
|
||||||
toLocalPoint,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -114,7 +113,6 @@ import {
|
||||||
fixBindingsAfterDeletion,
|
fixBindingsAfterDeletion,
|
||||||
getHoveredElementForBinding,
|
getHoveredElementForBinding,
|
||||||
isBindingEnabled,
|
isBindingEnabled,
|
||||||
isLinearElementSimpleAndAlreadyBound,
|
|
||||||
maybeBindLinearElement,
|
maybeBindLinearElement,
|
||||||
shouldEnableBindingForPointerEvent,
|
shouldEnableBindingForPointerEvent,
|
||||||
updateBoundElements,
|
updateBoundElements,
|
||||||
|
@ -172,7 +170,6 @@ import {
|
||||||
} from "@excalidraw/element/typeChecks";
|
} from "@excalidraw/element/typeChecks";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getLockedLinearCursorAlignSize,
|
|
||||||
getNormalizedDimensions,
|
getNormalizedDimensions,
|
||||||
isElementCompletelyInViewport,
|
isElementCompletelyInViewport,
|
||||||
isElementInViewport,
|
isElementInViewport,
|
||||||
|
@ -307,7 +304,6 @@ import { isNonDeletedElement } from "@excalidraw/element";
|
||||||
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
|
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ExcalidrawBindableElement,
|
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawFreeDrawElement,
|
ExcalidrawFreeDrawElement,
|
||||||
ExcalidrawGenericElement,
|
ExcalidrawGenericElement,
|
||||||
|
@ -466,6 +462,14 @@ import { isMaybeMermaidDefinition } from "../mermaid";
|
||||||
|
|
||||||
import { LassoTrail } from "../lasso";
|
import { LassoTrail } from "../lasso";
|
||||||
|
|
||||||
|
import {
|
||||||
|
handleCanvasPointerMoveForLinearElement,
|
||||||
|
handleDoubleClickForLinearElement,
|
||||||
|
maybeSuggestBindingsForLinearElementAtCoords,
|
||||||
|
onPointerMoveFromPointerDownOnLinearElement,
|
||||||
|
onPointerUpFromPointerDownOnLinearElementHandler,
|
||||||
|
} from "../linear";
|
||||||
|
|
||||||
import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
|
import { activeConfirmDialogAtom } from "./ActiveConfirmDialog";
|
||||||
import BraveMeasureTextError from "./BraveMeasureTextError";
|
import BraveMeasureTextError from "./BraveMeasureTextError";
|
||||||
import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu";
|
import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu";
|
||||||
|
@ -5447,75 +5451,16 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
|
|
||||||
if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
|
if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
|
||||||
if (
|
if (
|
||||||
event[KEYS.CTRL_OR_CMD] &&
|
handleDoubleClickForLinearElement(
|
||||||
(!this.state.editingLinearElement ||
|
this,
|
||||||
this.state.editingLinearElement.elementId !==
|
this.store,
|
||||||
selectedElements[0].id) &&
|
selectedElements[0],
|
||||||
!isElbowArrow(selectedElements[0])
|
event,
|
||||||
|
sceneX,
|
||||||
|
sceneY,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
this.store.shouldCaptureIncrement();
|
|
||||||
this.setState({
|
|
||||||
editingLinearElement: new LinearElementEditor(selectedElements[0]),
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
} else if (
|
|
||||||
this.state.selectedLinearElement &&
|
|
||||||
isElbowArrow(selectedElements[0])
|
|
||||||
) {
|
|
||||||
const hitCoords = LinearElementEditor.getSegmentMidpointHitCoords(
|
|
||||||
this.state.selectedLinearElement,
|
|
||||||
{ x: sceneX, y: sceneY },
|
|
||||||
this.state,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
);
|
|
||||||
const midPoint = hitCoords
|
|
||||||
? LinearElementEditor.getSegmentMidPointIndex(
|
|
||||||
this.state.selectedLinearElement,
|
|
||||||
this.state,
|
|
||||||
hitCoords,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
)
|
|
||||||
: -1;
|
|
||||||
|
|
||||||
if (midPoint && midPoint > -1) {
|
|
||||||
this.store.shouldCaptureIncrement();
|
|
||||||
LinearElementEditor.deleteFixedSegment(selectedElements[0], midPoint);
|
|
||||||
|
|
||||||
const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords(
|
|
||||||
{
|
|
||||||
...this.state.selectedLinearElement,
|
|
||||||
segmentMidPointHoveredCoords: null,
|
|
||||||
},
|
|
||||||
{ x: sceneX, y: sceneY },
|
|
||||||
this.state,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
);
|
|
||||||
const nextIndex = nextCoords
|
|
||||||
? LinearElementEditor.getSegmentMidPointIndex(
|
|
||||||
this.state.selectedLinearElement,
|
|
||||||
this.state,
|
|
||||||
nextCoords,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
selectedLinearElement: {
|
|
||||||
...this.state.selectedLinearElement,
|
|
||||||
pointerDownState: {
|
|
||||||
...this.state.selectedLinearElement.pointerDownState,
|
|
||||||
segmentMidpoint: {
|
|
||||||
index: nextIndex,
|
|
||||||
value: hitCoords,
|
|
||||||
added: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
segmentMidPointHoveredCoords: nextCoords,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5896,9 +5841,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
// and point
|
// and point
|
||||||
const { newElement } = this.state;
|
const { newElement } = this.state;
|
||||||
if (isBindingElement(newElement, false)) {
|
if (isBindingElement(newElement, false)) {
|
||||||
this.maybeSuggestBindingsForLinearElementAtCoords(
|
maybeSuggestBindingsForLinearElementAtCoords(
|
||||||
newElement,
|
newElement,
|
||||||
[scenePointer],
|
[scenePointer],
|
||||||
|
this,
|
||||||
this.state.startBoundElement,
|
this.state.startBoundElement,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -5908,121 +5854,14 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
|
|
||||||
if (this.state.multiElement) {
|
if (this.state.multiElement) {
|
||||||
const { multiElement } = this.state;
|
const { multiElement } = this.state;
|
||||||
const { x: rx, y: ry } = multiElement;
|
handleCanvasPointerMoveForLinearElement(
|
||||||
|
multiElement,
|
||||||
const { points, lastCommittedPoint } = multiElement;
|
this,
|
||||||
const lastPoint = points[points.length - 1];
|
scenePointerX,
|
||||||
|
scenePointerY,
|
||||||
setCursorForShape(this.interactiveCanvas, this.state);
|
event,
|
||||||
|
this.triggerRender,
|
||||||
if (lastPoint === lastCommittedPoint) {
|
);
|
||||||
// if we haven't yet created a temp point and we're beyond commit-zone
|
|
||||||
// threshold, add a point
|
|
||||||
if (
|
|
||||||
pointDistance(
|
|
||||||
pointFrom(scenePointerX - rx, scenePointerY - ry),
|
|
||||||
lastPoint,
|
|
||||||
) >= LINE_CONFIRM_THRESHOLD
|
|
||||||
) {
|
|
||||||
mutateElement(
|
|
||||||
multiElement,
|
|
||||||
{
|
|
||||||
points: [
|
|
||||||
...points,
|
|
||||||
pointFrom<LocalPoint>(scenePointerX - rx, scenePointerY - ry),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
||||||
// in this branch, we're inside the commit zone, and no uncommitted
|
|
||||||
// point exists. Thus do nothing (don't add/remove points).
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
points.length > 2 &&
|
|
||||||
lastCommittedPoint &&
|
|
||||||
pointDistance(
|
|
||||||
pointFrom(scenePointerX - rx, scenePointerY - ry),
|
|
||||||
lastCommittedPoint,
|
|
||||||
) < LINE_CONFIRM_THRESHOLD
|
|
||||||
) {
|
|
||||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
||||||
mutateElement(
|
|
||||||
multiElement,
|
|
||||||
{
|
|
||||||
points: points.slice(0, -1),
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const [gridX, gridY] = getGridPoint(
|
|
||||||
scenePointerX,
|
|
||||||
scenePointerY,
|
|
||||||
event[KEYS.CTRL_OR_CMD] || isElbowArrow(multiElement)
|
|
||||||
? null
|
|
||||||
: this.getEffectiveGridSize(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [lastCommittedX, lastCommittedY] =
|
|
||||||
multiElement?.lastCommittedPoint ?? [0, 0];
|
|
||||||
|
|
||||||
let dxFromLastCommitted = gridX - rx - lastCommittedX;
|
|
||||||
let dyFromLastCommitted = gridY - ry - lastCommittedY;
|
|
||||||
|
|
||||||
if (shouldRotateWithDiscreteAngle(event)) {
|
|
||||||
({ width: dxFromLastCommitted, height: dyFromLastCommitted } =
|
|
||||||
getLockedLinearCursorAlignSize(
|
|
||||||
// actual coordinate of the last committed point
|
|
||||||
lastCommittedX + rx,
|
|
||||||
lastCommittedY + ry,
|
|
||||||
// cursor-grid coordinate
|
|
||||||
gridX,
|
|
||||||
gridY,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPathALoop(points, this.state.zoom.value)) {
|
|
||||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
|
||||||
}
|
|
||||||
// update last uncommitted point
|
|
||||||
mutateElement(
|
|
||||||
multiElement,
|
|
||||||
{
|
|
||||||
points: [
|
|
||||||
...points.slice(0, -1),
|
|
||||||
isArrowElement(multiElement)
|
|
||||||
? toLocalPoint(
|
|
||||||
getOutlineAvoidingPoint(
|
|
||||||
multiElement,
|
|
||||||
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
|
||||||
multiElement.points.length - 1,
|
|
||||||
this.scene,
|
|
||||||
this.state.zoom,
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
multiElement.x + lastCommittedX + dxFromLastCommitted,
|
|
||||||
multiElement.y + lastCommittedY + dyFromLastCommitted,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
multiElement,
|
|
||||||
)
|
|
||||||
: pointFrom<LocalPoint>(
|
|
||||||
lastCommittedX + dxFromLastCommitted,
|
|
||||||
lastCommittedY + dyFromLastCommitted,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
{
|
|
||||||
isDragging: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// in this path, we're mutating multiElement to reflect
|
|
||||||
// how it will be after adding pointer position as the next point
|
|
||||||
// trigger update here so that new element canvas renders again to reflect this
|
|
||||||
this.triggerRender(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -8301,9 +8140,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
pointerCoords.x,
|
pointerCoords.x,
|
||||||
pointerCoords.y,
|
pointerCoords.y,
|
||||||
(element, pointsSceneCoords) => {
|
(element, pointsSceneCoords) => {
|
||||||
this.maybeSuggestBindingsForLinearElementAtCoords(
|
maybeSuggestBindingsForLinearElementAtCoords(
|
||||||
element,
|
element,
|
||||||
pointsSceneCoords,
|
pointsSceneCoords,
|
||||||
|
this,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
linearElementEditor,
|
linearElementEditor,
|
||||||
|
@ -8691,120 +8531,14 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (isLinearElement(newElement)) {
|
} else if (isLinearElement(newElement)) {
|
||||||
pointerDownState.drag.hasOccurred = true;
|
onPointerMoveFromPointerDownOnLinearElement(
|
||||||
const points = newElement.points;
|
|
||||||
const [gridX, gridY] = getGridPoint(
|
|
||||||
pointerCoords.x,
|
|
||||||
pointerCoords.y,
|
|
||||||
event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize(),
|
|
||||||
);
|
|
||||||
let dx = gridX - newElement.x;
|
|
||||||
let dy = gridY - newElement.y;
|
|
||||||
|
|
||||||
if (shouldRotateWithDiscreteAngle(event) && points.length === 2) {
|
|
||||||
({ width: dx, height: dy } = getLockedLinearCursorAlignSize(
|
|
||||||
newElement.x,
|
|
||||||
newElement.y,
|
|
||||||
pointerCoords.x,
|
|
||||||
pointerCoords.y,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (points.length === 1) {
|
|
||||||
mutateElement(
|
|
||||||
newElement,
|
|
||||||
{
|
|
||||||
points: [
|
|
||||||
...points,
|
|
||||||
isArrowElement(newElement)
|
|
||||||
? toLocalPoint(
|
|
||||||
getOutlineAvoidingPoint(
|
|
||||||
newElement,
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
pointerCoords.x,
|
|
||||||
pointerCoords.y,
|
|
||||||
),
|
|
||||||
newElement.points.length - 1,
|
|
||||||
this.scene,
|
|
||||||
this.state.zoom,
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
newElement.x + dx,
|
|
||||||
newElement.y + dy,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
newElement,
|
|
||||||
)
|
|
||||||
: pointFrom<LocalPoint>(dx, dy),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
points.length === 2 ||
|
|
||||||
(points.length > 1 && isElbowArrow(newElement))
|
|
||||||
) {
|
|
||||||
mutateElement(
|
|
||||||
newElement,
|
|
||||||
{
|
|
||||||
points: [
|
|
||||||
...points.slice(0, -1),
|
|
||||||
isArrowElement(newElement)
|
|
||||||
? toLocalPoint(
|
|
||||||
getOutlineAvoidingPoint(
|
|
||||||
newElement,
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
pointerCoords.x,
|
|
||||||
pointerCoords.y,
|
|
||||||
),
|
|
||||||
newElement.points.length - 1,
|
|
||||||
this.scene,
|
|
||||||
this.state.zoom,
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
newElement.x + dx,
|
|
||||||
newElement.y + dy,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
newElement,
|
|
||||||
)
|
|
||||||
: pointFrom<LocalPoint>(dx, dy),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
{ isDragging: true },
|
|
||||||
);
|
|
||||||
LinearElementEditor.movePoints(newElement, [
|
|
||||||
{
|
|
||||||
index: 0,
|
|
||||||
isDragging: false,
|
|
||||||
point: toLocalPoint(
|
|
||||||
getOutlineAvoidingPoint(
|
|
||||||
newElement,
|
|
||||||
pointFrom<GlobalPoint>(
|
|
||||||
pointerDownState.origin.x,
|
|
||||||
pointerDownState.origin.y,
|
|
||||||
),
|
|
||||||
0,
|
|
||||||
this.scene,
|
|
||||||
this.state.zoom,
|
|
||||||
),
|
|
||||||
newElement,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
newElement,
|
newElement,
|
||||||
});
|
this,
|
||||||
|
pointerDownState,
|
||||||
if (isBindingElement(newElement, false)) {
|
pointerCoords,
|
||||||
// When creating a linear element by dragging
|
event,
|
||||||
this.maybeSuggestBindingsForLinearElementAtCoords(
|
elementsMap,
|
||||||
newElement,
|
);
|
||||||
[pointerCoords],
|
|
||||||
this.state.startBoundElement,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
pointerDownState.lastCoords.x = pointerCoords.x;
|
pointerDownState.lastCoords.x = pointerCoords.x;
|
||||||
pointerDownState.lastCoords.y = pointerCoords.y;
|
pointerDownState.lastCoords.y = pointerCoords.y;
|
||||||
|
@ -9170,65 +8904,15 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLinearElement(newElement)) {
|
if (isLinearElement(newElement)) {
|
||||||
if (newElement!.points.length > 1) {
|
onPointerUpFromPointerDownOnLinearElementHandler(
|
||||||
this.store.shouldCaptureIncrement();
|
newElement,
|
||||||
}
|
multiElement,
|
||||||
const pointerCoords = viewportCoordsToSceneCoords(
|
this,
|
||||||
|
this.store,
|
||||||
|
pointerDownState,
|
||||||
childEvent,
|
childEvent,
|
||||||
this.state,
|
activeTool,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
|
|
||||||
mutateElement(newElement, {
|
|
||||||
points: [
|
|
||||||
...newElement.points,
|
|
||||||
pointFrom<LocalPoint>(
|
|
||||||
pointerCoords.x - newElement.x,
|
|
||||||
pointerCoords.y - newElement.y,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
multiElement: newElement,
|
|
||||||
newElement,
|
|
||||||
});
|
|
||||||
} else if (pointerDownState.drag.hasOccurred && !multiElement) {
|
|
||||||
if (
|
|
||||||
isBindingEnabled(this.state) &&
|
|
||||||
isBindingElement(newElement, false)
|
|
||||||
) {
|
|
||||||
maybeBindLinearElement(
|
|
||||||
newElement,
|
|
||||||
this.state,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
this.scene.getNonDeletedElements(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.setState({ suggestedBindings: [], startBoundElement: null });
|
|
||||||
if (!activeTool.locked) {
|
|
||||||
resetCursor(this.interactiveCanvas);
|
|
||||||
this.setState((prevState) => ({
|
|
||||||
newElement: null,
|
|
||||||
activeTool: updateActiveTool(this.state, {
|
|
||||||
type: "selection",
|
|
||||||
}),
|
|
||||||
selectedElementIds: makeNextSelectedElementIds(
|
|
||||||
{
|
|
||||||
...prevState.selectedElementIds,
|
|
||||||
[newElement.id]: true,
|
|
||||||
},
|
|
||||||
prevState,
|
|
||||||
),
|
|
||||||
selectedLinearElement: new LinearElementEditor(newElement),
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this.setState((prevState) => ({
|
|
||||||
newElement: null,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
// so that the scene gets rendered again to display the newly drawn linear as well
|
|
||||||
this.scene.triggerUpdate();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10294,49 +9978,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private maybeSuggestBindingsForLinearElementAtCoords = (
|
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
|
||||||
/** scene coords */
|
|
||||||
pointerCoords: {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}[],
|
|
||||||
// During line creation the start binding hasn't been written yet
|
|
||||||
// into `linearElement`
|
|
||||||
oppositeBindingBoundElement?: ExcalidrawBindableElement | null,
|
|
||||||
): void => {
|
|
||||||
if (!pointerCoords.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const suggestedBindings = pointerCoords.reduce(
|
|
||||||
(acc: NonDeleted<ExcalidrawBindableElement>[], coords) => {
|
|
||||||
const hoveredBindableElement = getHoveredElementForBinding(
|
|
||||||
coords,
|
|
||||||
this.scene.getNonDeletedElements(),
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
this.state.zoom,
|
|
||||||
isElbowArrow(linearElement),
|
|
||||||
isElbowArrow(linearElement),
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
hoveredBindableElement != null &&
|
|
||||||
!isLinearElementSimpleAndAlreadyBound(
|
|
||||||
linearElement,
|
|
||||||
oppositeBindingBoundElement?.id,
|
|
||||||
hoveredBindableElement,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
acc.push(hoveredBindableElement);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
this.setState({ suggestedBindings });
|
|
||||||
};
|
|
||||||
|
|
||||||
private clearSelection(hitElement: ExcalidrawElement | null): void {
|
private clearSelection(hitElement: ExcalidrawElement | null): void {
|
||||||
this.setState((prevState) => ({
|
this.setState((prevState) => ({
|
||||||
selectedElementIds: makeNextSelectedElementIds({}, prevState),
|
selectedElementIds: makeNextSelectedElementIds({}, prevState),
|
||||||
|
|
478
packages/excalidraw/linear.ts
Normal file
478
packages/excalidraw/linear.ts
Normal file
|
@ -0,0 +1,478 @@
|
||||||
|
import {
|
||||||
|
CURSOR_TYPE,
|
||||||
|
getGridPoint,
|
||||||
|
KEYS,
|
||||||
|
LINE_CONFIRM_THRESHOLD,
|
||||||
|
shouldRotateWithDiscreteAngle,
|
||||||
|
updateActiveTool,
|
||||||
|
viewportCoordsToSceneCoords,
|
||||||
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
|
import { getLockedLinearCursorAlignSize } from "@excalidraw/element/sizeHelpers";
|
||||||
|
|
||||||
|
import {
|
||||||
|
isArrowElement,
|
||||||
|
isBindingElement,
|
||||||
|
isElbowArrow,
|
||||||
|
} from "@excalidraw/element/typeChecks";
|
||||||
|
import {
|
||||||
|
getHoveredElementForBinding,
|
||||||
|
getOutlineAvoidingPoint,
|
||||||
|
isBindingEnabled,
|
||||||
|
isLinearElementSimpleAndAlreadyBound,
|
||||||
|
maybeBindLinearElement,
|
||||||
|
} from "@excalidraw/element/binding";
|
||||||
|
|
||||||
|
import { pointDistance, pointFrom } from "@excalidraw/math";
|
||||||
|
import { mutateElement } from "@excalidraw/element/mutateElement";
|
||||||
|
|
||||||
|
import { LinearElementEditor } from "@excalidraw/element/linearElementEditor";
|
||||||
|
|
||||||
|
import { isPathALoop } from "@excalidraw/element/shapes";
|
||||||
|
|
||||||
|
import { makeNextSelectedElementIds } from "@excalidraw/element/selection";
|
||||||
|
|
||||||
|
import type { GlobalPoint, LocalPoint } from "@excalidraw/math";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ExcalidrawBindableElement,
|
||||||
|
ExcalidrawLinearElement,
|
||||||
|
NonDeleted,
|
||||||
|
NonDeletedSceneElementsMap,
|
||||||
|
} from "@excalidraw/element/types";
|
||||||
|
|
||||||
|
import { resetCursor, setCursor, setCursorForShape } from "./cursor";
|
||||||
|
|
||||||
|
import type App from "./components/App";
|
||||||
|
|
||||||
|
import type { ActiveTool, PointerDownState } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when the user drags the pointer to create a new linear element.
|
||||||
|
*/
|
||||||
|
export function onPointerMoveFromPointerDownOnLinearElement(
|
||||||
|
newElement: ExcalidrawLinearElement,
|
||||||
|
app: App,
|
||||||
|
pointerDownState: PointerDownState,
|
||||||
|
pointerCoords: { x: number; y: number },
|
||||||
|
event: PointerEvent,
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
|
) {
|
||||||
|
pointerDownState.drag.hasOccurred = true;
|
||||||
|
const points = newElement.points;
|
||||||
|
const [gridX, gridY] = getGridPoint(
|
||||||
|
pointerCoords.x,
|
||||||
|
pointerCoords.y,
|
||||||
|
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||||
|
);
|
||||||
|
let dx = gridX - newElement.x;
|
||||||
|
let dy = gridY - newElement.y;
|
||||||
|
|
||||||
|
if (shouldRotateWithDiscreteAngle(event) && points.length === 2) {
|
||||||
|
({ width: dx, height: dy } = getLockedLinearCursorAlignSize(
|
||||||
|
newElement.x,
|
||||||
|
newElement.y,
|
||||||
|
pointerCoords.x,
|
||||||
|
pointerCoords.y,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (points.length === 1) {
|
||||||
|
let x = newElement.x + dx;
|
||||||
|
let y = newElement.y + dy;
|
||||||
|
if (isArrowElement(newElement)) {
|
||||||
|
[x, y] = getOutlineAvoidingPoint(
|
||||||
|
newElement,
|
||||||
|
pointFrom<GlobalPoint>(pointerCoords.x, pointerCoords.y),
|
||||||
|
newElement.points.length - 1,
|
||||||
|
app.scene,
|
||||||
|
app.state.zoom,
|
||||||
|
pointFrom<GlobalPoint>(newElement.x + dx, newElement.y + dy),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutateElement(
|
||||||
|
newElement,
|
||||||
|
{
|
||||||
|
points: [
|
||||||
|
...points,
|
||||||
|
LinearElementEditor.createPointAt(
|
||||||
|
newElement,
|
||||||
|
elementsMap,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
app.getEffectiveGridSize(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
points.length === 2 ||
|
||||||
|
(points.length > 1 && isElbowArrow(newElement))
|
||||||
|
) {
|
||||||
|
const targets = [
|
||||||
|
{
|
||||||
|
index: points.length - 1,
|
||||||
|
isDragging: true,
|
||||||
|
point: pointFrom<LocalPoint>(dx, dy),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isArrowElement(newElement)) {
|
||||||
|
const [x, y] = getOutlineAvoidingPoint(
|
||||||
|
newElement,
|
||||||
|
pointFrom<GlobalPoint>(
|
||||||
|
pointerDownState.origin.x,
|
||||||
|
pointerDownState.origin.y,
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
app.scene,
|
||||||
|
app.state.zoom,
|
||||||
|
);
|
||||||
|
targets.unshift({
|
||||||
|
index: 0,
|
||||||
|
isDragging: false,
|
||||||
|
point: LinearElementEditor.createPointAt(
|
||||||
|
newElement,
|
||||||
|
elementsMap,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
app.getEffectiveGridSize(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearElementEditor.movePoints(newElement, targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.setState({
|
||||||
|
newElement,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isBindingElement(newElement, false)) {
|
||||||
|
// When creating a linear element by dragging
|
||||||
|
maybeSuggestBindingsForLinearElementAtCoords(
|
||||||
|
newElement,
|
||||||
|
[pointerCoords],
|
||||||
|
app,
|
||||||
|
app.state.startBoundElement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function handleCanvasPointerMoveForLinearElement(
|
||||||
|
multiElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
app: App,
|
||||||
|
scenePointerX: number,
|
||||||
|
scenePointerY: number,
|
||||||
|
event: React.PointerEvent<HTMLCanvasElement>,
|
||||||
|
triggerRender: (forceUpdate?: boolean) => void,
|
||||||
|
) {
|
||||||
|
const { x: rx, y: ry } = multiElement;
|
||||||
|
|
||||||
|
const { points, lastCommittedPoint } = multiElement;
|
||||||
|
const lastPoint = points[points.length - 1];
|
||||||
|
|
||||||
|
setCursorForShape(app.interactiveCanvas, app.state);
|
||||||
|
|
||||||
|
if (lastPoint === lastCommittedPoint) {
|
||||||
|
// if we haven't yet created a temp point and we're beyond commit-zone
|
||||||
|
// threshold, add a point
|
||||||
|
if (
|
||||||
|
pointDistance(
|
||||||
|
pointFrom(scenePointerX - rx, scenePointerY - ry),
|
||||||
|
lastPoint,
|
||||||
|
) >= LINE_CONFIRM_THRESHOLD
|
||||||
|
) {
|
||||||
|
mutateElement(
|
||||||
|
multiElement,
|
||||||
|
{
|
||||||
|
points: [
|
||||||
|
...points,
|
||||||
|
pointFrom<LocalPoint>(scenePointerX - rx, scenePointerY - ry),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setCursor(app.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||||
|
// in this branch, we're inside the commit zone, and no uncommitted
|
||||||
|
// point exists. Thus do nothing (don't add/remove points).
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
points.length > 2 &&
|
||||||
|
lastCommittedPoint &&
|
||||||
|
pointDistance(
|
||||||
|
pointFrom(scenePointerX - rx, scenePointerY - ry),
|
||||||
|
lastCommittedPoint,
|
||||||
|
) < LINE_CONFIRM_THRESHOLD
|
||||||
|
) {
|
||||||
|
setCursor(app.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||||
|
mutateElement(
|
||||||
|
multiElement,
|
||||||
|
{
|
||||||
|
points: points.slice(0, -1),
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const [gridX, gridY] = getGridPoint(
|
||||||
|
scenePointerX,
|
||||||
|
scenePointerY,
|
||||||
|
event[KEYS.CTRL_OR_CMD] || isElbowArrow(multiElement)
|
||||||
|
? null
|
||||||
|
: app.getEffectiveGridSize(),
|
||||||
|
);
|
||||||
|
console.log(points);
|
||||||
|
const [lastCommittedX, lastCommittedY] =
|
||||||
|
multiElement?.lastCommittedPoint ?? [0, 0];
|
||||||
|
|
||||||
|
let dxFromLastCommitted = gridX - rx - lastCommittedX;
|
||||||
|
let dyFromLastCommitted = gridY - ry - lastCommittedY;
|
||||||
|
|
||||||
|
if (shouldRotateWithDiscreteAngle(event)) {
|
||||||
|
({ width: dxFromLastCommitted, height: dyFromLastCommitted } =
|
||||||
|
getLockedLinearCursorAlignSize(
|
||||||
|
// actual coordinate of the last committed point
|
||||||
|
lastCommittedX + rx,
|
||||||
|
lastCommittedY + ry,
|
||||||
|
// cursor-grid coordinate
|
||||||
|
gridX,
|
||||||
|
gridY,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPathALoop(points, app.state.zoom.value)) {
|
||||||
|
setCursor(app.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = multiElement.x + lastCommittedX + dxFromLastCommitted;
|
||||||
|
let y = multiElement.y + lastCommittedY + dyFromLastCommitted;
|
||||||
|
|
||||||
|
if (isArrowElement(multiElement)) {
|
||||||
|
[x, y] = getOutlineAvoidingPoint(
|
||||||
|
multiElement,
|
||||||
|
pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
|
||||||
|
multiElement.points.length - 1,
|
||||||
|
app.scene,
|
||||||
|
app.state.zoom,
|
||||||
|
pointFrom<GlobalPoint>(x, y),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update last uncommitted point
|
||||||
|
LinearElementEditor.movePoints(multiElement, [
|
||||||
|
{
|
||||||
|
index: points.length - 1,
|
||||||
|
point: LinearElementEditor.createPointAt(
|
||||||
|
multiElement,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
app.getEffectiveGridSize(),
|
||||||
|
),
|
||||||
|
isDragging: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// in this path, we're mutating multiElement to reflect
|
||||||
|
// how it will be after adding pointer position as the next point
|
||||||
|
// trigger update here so that new element canvas renders again to reflect this
|
||||||
|
triggerRender(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onPointerUpFromPointerDownOnLinearElementHandler(
|
||||||
|
newElement: ExcalidrawLinearElement,
|
||||||
|
multiElement: NonDeleted<ExcalidrawLinearElement> | null,
|
||||||
|
app: App,
|
||||||
|
store: App["store"],
|
||||||
|
pointerDownState: PointerDownState,
|
||||||
|
childEvent: PointerEvent,
|
||||||
|
activeTool: {
|
||||||
|
lastActiveTool: ActiveTool | null;
|
||||||
|
locked: boolean;
|
||||||
|
fromSelection: boolean;
|
||||||
|
} & ActiveTool,
|
||||||
|
) {
|
||||||
|
if (newElement!.points.length > 1) {
|
||||||
|
store.shouldCaptureIncrement();
|
||||||
|
}
|
||||||
|
const pointerCoords = viewportCoordsToSceneCoords(childEvent, app.state);
|
||||||
|
|
||||||
|
if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
|
||||||
|
mutateElement(newElement, {
|
||||||
|
points: [
|
||||||
|
...newElement.points,
|
||||||
|
pointFrom<LocalPoint>(
|
||||||
|
pointerCoords.x - newElement.x,
|
||||||
|
pointerCoords.y - newElement.y,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
app.setState({
|
||||||
|
multiElement: newElement,
|
||||||
|
newElement,
|
||||||
|
});
|
||||||
|
} else if (pointerDownState.drag.hasOccurred && !multiElement) {
|
||||||
|
if (isBindingEnabled(app.state) && isBindingElement(newElement, false)) {
|
||||||
|
maybeBindLinearElement(
|
||||||
|
newElement,
|
||||||
|
app.state,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
app.scene.getNonDeletedElements(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
app.setState({ suggestedBindings: [], startBoundElement: null });
|
||||||
|
if (!activeTool.locked) {
|
||||||
|
resetCursor(app.interactiveCanvas);
|
||||||
|
app.setState((prevState) => ({
|
||||||
|
newElement: null,
|
||||||
|
activeTool: updateActiveTool(app.state, {
|
||||||
|
type: "selection",
|
||||||
|
}),
|
||||||
|
selectedElementIds: makeNextSelectedElementIds(
|
||||||
|
{
|
||||||
|
...prevState.selectedElementIds,
|
||||||
|
[newElement.id]: true,
|
||||||
|
},
|
||||||
|
prevState,
|
||||||
|
),
|
||||||
|
selectedLinearElement: new LinearElementEditor(newElement),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
app.setState((prevState) => ({
|
||||||
|
newElement: null,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// so that the scene gets rendered again to display the newly drawn linear as well
|
||||||
|
app.scene.triggerUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles double click on a linear element to edit it or delete a segment
|
||||||
|
*/
|
||||||
|
export function handleDoubleClickForLinearElement(
|
||||||
|
app: App,
|
||||||
|
store: App["store"],
|
||||||
|
selectedElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
event: React.MouseEvent<HTMLCanvasElement>,
|
||||||
|
sceneX: number,
|
||||||
|
sceneY: number,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
event[KEYS.CTRL_OR_CMD] &&
|
||||||
|
(!app.state.editingLinearElement ||
|
||||||
|
app.state.editingLinearElement.elementId !== selectedElement.id) &&
|
||||||
|
!isElbowArrow(selectedElement)
|
||||||
|
) {
|
||||||
|
store.shouldCaptureIncrement();
|
||||||
|
app.setState({
|
||||||
|
editingLinearElement: new LinearElementEditor(selectedElement),
|
||||||
|
});
|
||||||
|
} else if (app.state.selectedLinearElement && isElbowArrow(selectedElement)) {
|
||||||
|
const hitCoords = LinearElementEditor.getSegmentMidpointHitCoords(
|
||||||
|
app.state.selectedLinearElement,
|
||||||
|
{ x: sceneX, y: sceneY },
|
||||||
|
app.state,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
);
|
||||||
|
const midPoint = hitCoords
|
||||||
|
? LinearElementEditor.getSegmentMidPointIndex(
|
||||||
|
app.state.selectedLinearElement,
|
||||||
|
app.state,
|
||||||
|
hitCoords,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
)
|
||||||
|
: -1;
|
||||||
|
|
||||||
|
if (midPoint && midPoint > -1) {
|
||||||
|
store.shouldCaptureIncrement();
|
||||||
|
LinearElementEditor.deleteFixedSegment(selectedElement, midPoint);
|
||||||
|
|
||||||
|
const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords(
|
||||||
|
{
|
||||||
|
...app.state.selectedLinearElement,
|
||||||
|
segmentMidPointHoveredCoords: null,
|
||||||
|
},
|
||||||
|
{ x: sceneX, y: sceneY },
|
||||||
|
app.state,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
);
|
||||||
|
const nextIndex = nextCoords
|
||||||
|
? LinearElementEditor.getSegmentMidPointIndex(
|
||||||
|
app.state.selectedLinearElement,
|
||||||
|
app.state,
|
||||||
|
nextCoords,
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
app.setState({
|
||||||
|
selectedLinearElement: {
|
||||||
|
...app.state.selectedLinearElement,
|
||||||
|
pointerDownState: {
|
||||||
|
...app.state.selectedLinearElement.pointerDownState,
|
||||||
|
segmentMidpoint: {
|
||||||
|
index: nextIndex,
|
||||||
|
value: hitCoords,
|
||||||
|
added: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
segmentMidPointHoveredCoords: nextCoords,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function maybeSuggestBindingsForLinearElementAtCoords(
|
||||||
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
/** scene coords */
|
||||||
|
pointerCoords: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}[],
|
||||||
|
app: App,
|
||||||
|
// During line creation the start binding hasn't been written yet
|
||||||
|
// into `linearElement`
|
||||||
|
oppositeBindingBoundElement?: ExcalidrawBindableElement | null,
|
||||||
|
) {
|
||||||
|
if (!pointerCoords.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const suggestedBindings = pointerCoords.reduce(
|
||||||
|
(acc: NonDeleted<ExcalidrawBindableElement>[], coords) => {
|
||||||
|
const hoveredBindableElement = getHoveredElementForBinding(
|
||||||
|
coords,
|
||||||
|
app.scene.getNonDeletedElements(),
|
||||||
|
app.scene.getNonDeletedElementsMap(),
|
||||||
|
app.state.zoom,
|
||||||
|
isElbowArrow(linearElement),
|
||||||
|
isElbowArrow(linearElement),
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
hoveredBindableElement != null &&
|
||||||
|
!isLinearElementSimpleAndAlreadyBound(
|
||||||
|
linearElement,
|
||||||
|
oppositeBindingBoundElement?.id,
|
||||||
|
hoveredBindableElement,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
acc.push(hoveredBindableElement);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
app.setState({ suggestedBindings });
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue