feat: Orthogonal (elbow) arrows for diagramming (#8299)

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Márk Tolmács 2024-08-01 18:39:03 +02:00 committed by GitHub
parent a133a70e87
commit 15e019706d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 5415 additions and 1144 deletions

View file

@ -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) {