fix: arrow beginning und ending
This commit is contained in:
parent
590178b8f2
commit
6890a38546
2 changed files with 35 additions and 26 deletions
|
|
@ -263,10 +263,10 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.35rem 0.5rem;
|
padding: 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--border-color, #e0e0e0);
|
border: none;
|
||||||
background: var(--bg-secondary, #f8f9fa);
|
background: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -966,6 +966,8 @@
|
||||||
|
|
||||||
.handleWrapper:has(.handleOutput) {
|
.handleWrapper:has(.handleOutput) {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
/* Bottom handles: keep circle math aligned with wires even when a label grows row height. */
|
||||||
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.handleWrapper:has(.handleInput) {
|
.handleWrapper:has(.handleInput) {
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,8 @@ export function getStickyNotePaletteEntry(colorId?: string) {
|
||||||
|
|
||||||
const NODE_WIDTH = 200;
|
const NODE_WIDTH = 200;
|
||||||
const NODE_HEIGHT = 72;
|
const NODE_HEIGHT = 72;
|
||||||
|
/** Must match `.canvasNode { border: … solid }` — handles sit in the padding box. */
|
||||||
|
const NODE_BORDER = 2;
|
||||||
|
|
||||||
export const FLOW_CANVAS_MIN_ZOOM = 0.25;
|
export const FLOW_CANVAS_MIN_ZOOM = 0.25;
|
||||||
export const FLOW_CANVAS_MAX_ZOOM = 4;
|
export const FLOW_CANVAS_MAX_ZOOM = 4;
|
||||||
|
|
@ -646,7 +648,7 @@ function feedbackConnectionPathD(
|
||||||
const sx = src.x;
|
const sx = src.x;
|
||||||
const sy = src.y;
|
const sy = src.y;
|
||||||
const tx = tgt.x;
|
const tx = tgt.x;
|
||||||
const tyIn = tgt.y - HANDLE_OFFSET;
|
const tyIn = tgt.y;
|
||||||
|
|
||||||
const minNx = allNodes.length
|
const minNx = allNodes.length
|
||||||
? Math.min(...allNodes.map((n) => n.x))
|
? Math.min(...allNodes.map((n) => n.x))
|
||||||
|
|
@ -687,10 +689,14 @@ function connectionPathD(
|
||||||
tgtNode: CanvasNode,
|
tgtNode: CanvasNode,
|
||||||
feedback: boolean,
|
feedback: boolean,
|
||||||
allNodes: CanvasNode[],
|
allNodes: CanvasNode[],
|
||||||
|
/** Trennt überlagernde Kanten in der Kurvenmitte — Endpunkt bleibt am Handle-Mittelpunkt. */
|
||||||
|
lateralBias = 0,
|
||||||
): string {
|
): string {
|
||||||
if (!feedback) {
|
if (!feedback) {
|
||||||
const dy = tgt.y - src.y;
|
const dy = tgt.y - src.y;
|
||||||
return `M ${src.x} ${src.y} C ${src.x} ${src.y + Math.abs(dy) / 2}, ${tgt.x} ${tgt.y - Math.abs(dy) / 2}, ${tgt.x} ${tgt.y}`;
|
const mx = Math.abs(dy) / 2;
|
||||||
|
const b = lateralBias;
|
||||||
|
return `M ${src.x} ${src.y} C ${src.x + b} ${src.y + mx}, ${tgt.x + b} ${tgt.y - mx}, ${tgt.x} ${tgt.y}`;
|
||||||
}
|
}
|
||||||
return feedbackConnectionPathD(src, tgt, srcNode, tgtNode, allNodes);
|
return feedbackConnectionPathD(src, tgt, srcNode, tgtNode, allNodes);
|
||||||
}
|
}
|
||||||
|
|
@ -1039,19 +1045,20 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
const ioIndex = isOutput ? handleIndex - node.inputs : handleIndex;
|
const ioIndex = isOutput ? handleIndex - node.inputs : handleIndex;
|
||||||
const ioCount = isOutput ? node.outputs : node.inputs;
|
const ioCount = isOutput ? node.outputs : node.inputs;
|
||||||
|
|
||||||
const w = NODE_WIDTH;
|
const innerLeft = node.x + NODE_BORDER;
|
||||||
const h = NODE_HEIGHT;
|
const innerTop = node.y + NODE_BORDER;
|
||||||
const centerX = node.x + w / 2;
|
const innerBottom = node.y + NODE_HEIGHT - NODE_BORDER;
|
||||||
|
const innerWidth = NODE_WIDTH - 2 * NODE_BORDER;
|
||||||
|
const centerX = innerLeft + innerWidth / 2;
|
||||||
|
|
||||||
if (isOutput) {
|
if (isOutput) {
|
||||||
if (ioCount === 1) return { x: centerX, y: node.y + h + HANDLE_OFFSET, side: 'bottom' };
|
if (ioCount === 1) return { x: centerX, y: innerBottom, side: 'bottom' };
|
||||||
const step = w / (ioCount + 1);
|
const step = innerWidth / (ioCount + 1);
|
||||||
return { x: node.x + step * (ioIndex + 1), y: node.y + h + HANDLE_OFFSET, side: 'bottom' };
|
return { x: innerLeft + step * (ioIndex + 1), y: innerBottom, side: 'bottom' };
|
||||||
} else {
|
|
||||||
if (ioCount === 1) return { x: centerX, y: node.y, side: 'top' };
|
|
||||||
const step = w / (ioCount + 1);
|
|
||||||
return { x: node.x + step * (ioIndex + 1), y: node.y, side: 'top' };
|
|
||||||
}
|
}
|
||||||
|
if (ioCount === 1) return { x: centerX, y: innerTop, side: 'top' };
|
||||||
|
const step = innerWidth / (ioCount + 1);
|
||||||
|
return { x: innerLeft + step * (ioIndex + 1), y: innerTop, side: 'top' };
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
@ -1062,7 +1069,7 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
return used;
|
return used;
|
||||||
}, [connections]);
|
}, [connections]);
|
||||||
|
|
||||||
/** Mehrere Kanten auf denselben Eingang: leicht versetzte Ziel-X für sichtbare, getrennte Enden. */
|
/** Mehrere Kanten auf denselben Eingang: Kurven seitlich versetzen (Endpunkt = Handle-Mitte). */
|
||||||
const inboundStacksByTarget = useMemo(() => {
|
const inboundStacksByTarget = useMemo(() => {
|
||||||
const m = new Map<string, CanvasConnection[]>();
|
const m = new Map<string, CanvasConnection[]>();
|
||||||
for (const c of connections) {
|
for (const c of connections) {
|
||||||
|
|
@ -1746,9 +1753,10 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
<defs>
|
<defs>
|
||||||
<marker
|
<marker
|
||||||
id="arrowhead"
|
id="arrowhead"
|
||||||
|
markerUnits="userSpaceOnUse"
|
||||||
markerWidth="10"
|
markerWidth="10"
|
||||||
markerHeight="7"
|
markerHeight="7"
|
||||||
refX="9"
|
refX="10"
|
||||||
refY="3.5"
|
refY="3.5"
|
||||||
orient="auto"
|
orient="auto"
|
||||||
>
|
>
|
||||||
|
|
@ -1756,9 +1764,10 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
</marker>
|
</marker>
|
||||||
<marker
|
<marker
|
||||||
id="arrowhead-selected"
|
id="arrowhead-selected"
|
||||||
|
markerUnits="userSpaceOnUse"
|
||||||
markerWidth="10"
|
markerWidth="10"
|
||||||
markerHeight="7"
|
markerHeight="7"
|
||||||
refX="9"
|
refX="10"
|
||||||
refY="3.5"
|
refY="3.5"
|
||||||
orient="auto"
|
orient="auto"
|
||||||
>
|
>
|
||||||
|
|
@ -1766,9 +1775,10 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
</marker>
|
</marker>
|
||||||
<marker
|
<marker
|
||||||
id="arrowhead-warning"
|
id="arrowhead-warning"
|
||||||
|
markerUnits="userSpaceOnUse"
|
||||||
markerWidth="10"
|
markerWidth="10"
|
||||||
markerHeight="7"
|
markerHeight="7"
|
||||||
refX="9"
|
refX="10"
|
||||||
refY="3.5"
|
refY="3.5"
|
||||||
orient="auto"
|
orient="auto"
|
||||||
>
|
>
|
||||||
|
|
@ -1783,13 +1793,10 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
const tgtBase = getHandlePosition(tgtNode, c.targetHandle);
|
const tgtBase = getHandlePosition(tgtNode, c.targetHandle);
|
||||||
const stack = inboundStacksByTarget.get(`${c.targetId}-${c.targetHandle}`) ?? [c];
|
const stack = inboundStacksByTarget.get(`${c.targetId}-${c.targetHandle}`) ?? [c];
|
||||||
const si = stack.findIndex((x) => x.id === c.id);
|
const si = stack.findIndex((x) => x.id === c.id);
|
||||||
const spread = 12;
|
const lateralBias =
|
||||||
const tgt =
|
stack.length > 1 ? (si - (stack.length - 1) / 2) * 14 : 0;
|
||||||
stack.length > 1
|
|
||||||
? { ...tgtBase, x: tgtBase.x + (si - (stack.length - 1) / 2) * spread }
|
|
||||||
: tgtBase;
|
|
||||||
const feedback = isLoopFeedbackEdge(c, srcNode, tgtNode);
|
const feedback = isLoopFeedbackEdge(c, srcNode, tgtNode);
|
||||||
const pathD = connectionPathD(src, tgt, srcNode, tgtNode, feedback, nodes);
|
const pathD = connectionPathD(src, tgtBase, srcNode, tgtNode, feedback, nodes, lateralBias);
|
||||||
const isSelected = selectedConnectionId === c.id;
|
const isSelected = selectedConnectionId === c.id;
|
||||||
const isWarning = connectionWarnings[c.id];
|
const isWarning = connectionWarnings[c.id];
|
||||||
const strokeColor = isSelected
|
const strokeColor = isSelected
|
||||||
|
|
@ -1971,7 +1978,7 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
||||||
style={{
|
style={{
|
||||||
top: pos.side === 'top' ? -HANDLE_OFFSET : undefined,
|
top: pos.side === 'top' ? -HANDLE_OFFSET : undefined,
|
||||||
bottom: pos.side === 'bottom' ? -HANDLE_OFFSET : undefined,
|
bottom: pos.side === 'bottom' ? -HANDLE_OFFSET : undefined,
|
||||||
left: pos.x - node.x - HANDLE_OFFSET,
|
left: pos.x - node.x - NODE_BORDER - HANDLE_OFFSET,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{outputLabel && pos.side === 'bottom' && isOutput ? (
|
{outputLabel && pos.side === 'bottom' && isOutput ? (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue