mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: Orthogonal (elbow) arrows for diagramming (#8299)
Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
parent
a133a70e87
commit
15e019706d
69 changed files with 5415 additions and 1144 deletions
|
@ -48,6 +48,8 @@ import {
|
|||
} from "./helpers";
|
||||
import oc from "open-color";
|
||||
import {
|
||||
isArrowElement,
|
||||
isElbowArrow,
|
||||
isFrameLikeElement,
|
||||
isLinearElement,
|
||||
isTextElement,
|
||||
|
@ -67,6 +69,7 @@ import type {
|
|||
InteractiveSceneRenderConfig,
|
||||
RenderableElementsMap,
|
||||
} from "../scene/types";
|
||||
import { getCornerRadius } from "../math";
|
||||
|
||||
const renderLinearElementPointHighlight = (
|
||||
context: CanvasRenderingContext2D,
|
||||
|
@ -212,13 +215,18 @@ const renderBindingHighlightForBindableElement = (
|
|||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
||||
const width = x2 - x1;
|
||||
const height = y2 - y1;
|
||||
const threshold = maxBindingGap(element, width, height);
|
||||
const thickness = 10;
|
||||
|
||||
// So that we don't overlap the element itself
|
||||
const strokeOffset = 4;
|
||||
context.strokeStyle = "rgba(0,0,0,.05)";
|
||||
context.lineWidth = threshold - strokeOffset;
|
||||
const padding = strokeOffset / 2 + threshold / 2;
|
||||
context.lineWidth = thickness - strokeOffset;
|
||||
const padding = strokeOffset / 2 + thickness / 2;
|
||||
|
||||
const radius = getCornerRadius(
|
||||
Math.min(element.width, element.height),
|
||||
element,
|
||||
);
|
||||
|
||||
switch (element.type) {
|
||||
case "rectangle":
|
||||
|
@ -237,6 +245,8 @@ const renderBindingHighlightForBindableElement = (
|
|||
x1 + width / 2,
|
||||
y1 + height / 2,
|
||||
element.angle,
|
||||
undefined,
|
||||
radius,
|
||||
);
|
||||
break;
|
||||
case "diamond":
|
||||
|
@ -474,6 +484,10 @@ const renderLinearPointHandles = (
|
|||
? POINT_HANDLE_SIZE
|
||||
: POINT_HANDLE_SIZE / 2;
|
||||
points.forEach((point, idx) => {
|
||||
if (isElbowArrow(element) && idx !== 0 && idx !== points.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSelected =
|
||||
!!appState.editingLinearElement?.selectedPointsIndices?.includes(idx);
|
||||
|
||||
|
@ -727,7 +741,13 @@ const _renderInteractiveScene = ({
|
|||
|
||||
if (
|
||||
appState.selectedLinearElement &&
|
||||
appState.selectedLinearElement.hoverPointIndex >= 0
|
||||
appState.selectedLinearElement.hoverPointIndex >= 0 &&
|
||||
!(
|
||||
isElbowArrow(selectedElements[0]) &&
|
||||
appState.selectedLinearElement.hoverPointIndex > 0 &&
|
||||
appState.selectedLinearElement.hoverPointIndex <
|
||||
selectedElements[0].points.length - 1
|
||||
)
|
||||
) {
|
||||
renderLinearElementPointHighlight(context, appState, elementsMap);
|
||||
}
|
||||
|
@ -771,27 +791,39 @@ const _renderInteractiveScene = ({
|
|||
|
||||
for (const element of elementsMap.values()) {
|
||||
const selectionColors = [];
|
||||
// local user
|
||||
if (
|
||||
locallySelectedIds.has(element.id) &&
|
||||
!isSelectedViaGroup(appState, element)
|
||||
) {
|
||||
selectionColors.push(selectionColor);
|
||||
}
|
||||
// remote users
|
||||
const remoteClients = renderConfig.remoteSelectedElementIds.get(
|
||||
element.id,
|
||||
);
|
||||
if (remoteClients) {
|
||||
selectionColors.push(
|
||||
...remoteClients.map((socketId) => {
|
||||
const background = getClientColor(
|
||||
socketId,
|
||||
appState.collaborators.get(socketId),
|
||||
);
|
||||
return background;
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!(
|
||||
// Elbow arrow elements cannot be selected when bound on either end
|
||||
(
|
||||
isSingleLinearElementSelected &&
|
||||
isArrowElement(element) &&
|
||||
isElbowArrow(element) &&
|
||||
(element.startBinding || element.endBinding)
|
||||
)
|
||||
)
|
||||
) {
|
||||
// local user
|
||||
if (
|
||||
locallySelectedIds.has(element.id) &&
|
||||
!isSelectedViaGroup(appState, element)
|
||||
) {
|
||||
selectionColors.push(selectionColor);
|
||||
}
|
||||
// remote users
|
||||
if (remoteClients) {
|
||||
selectionColors.push(
|
||||
...remoteClients.map((socketId) => {
|
||||
const background = getClientColor(
|
||||
socketId,
|
||||
appState.collaborators.get(socketId),
|
||||
);
|
||||
return background;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionColors.length) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue