|
|
|
@ -7,6 +7,7 @@ import {
|
|
|
|
|
useLayoutEffect,
|
|
|
|
|
useRef,
|
|
|
|
|
useState,
|
|
|
|
|
MouseEvent as ReactMouseEvent,
|
|
|
|
|
} from "react"
|
|
|
|
|
import {
|
|
|
|
|
add,
|
|
|
|
@ -115,6 +116,24 @@ export default function BendableArrow({
|
|
|
|
|
|
|
|
|
|
const styleWidth = style?.width ?? ArrowStyleDefaults.width
|
|
|
|
|
|
|
|
|
|
const computeInternalSegments = useCallback((segments: Segment[]) => {
|
|
|
|
|
return segments.map((segment, idx) => {
|
|
|
|
|
if (idx == 0) {
|
|
|
|
|
return {
|
|
|
|
|
start: startPos,
|
|
|
|
|
controlPoint: segment.controlPoint ?? null,
|
|
|
|
|
end: segment.next,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const start = segments[idx - 1].next
|
|
|
|
|
return {
|
|
|
|
|
start,
|
|
|
|
|
controlPoint: segment.controlPoint ?? null,
|
|
|
|
|
end: segment.next,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}, [segments, startPos])
|
|
|
|
|
|
|
|
|
|
// Cache the segments so that when the user is changing the segments (it moves an ArrowPoint),
|
|
|
|
|
// it does not unwind to this arrow's component parent until validated.
|
|
|
|
|
// The changes are validated (meaning that onSegmentsChanges is called) when the
|
|
|
|
@ -125,30 +144,14 @@ export default function BendableArrow({
|
|
|
|
|
// If the (original) segments changes, overwrite the current ones.
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
|
setInternalSegments(computeInternalSegments(segments))
|
|
|
|
|
}, [startPos, segments])
|
|
|
|
|
}, [startPos, segments, computeInternalSegments])
|
|
|
|
|
|
|
|
|
|
const [isSelected, setIsSelected] = useState(false)
|
|
|
|
|
|
|
|
|
|
const headRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
const tailRef = useRef<HTMLDivElement>(null)
|
|
|
|
|
|
|
|
|
|
function computeInternalSegments(segments: Segment[]): FullSegment[] {
|
|
|
|
|
return segments.map((segment, idx) => {
|
|
|
|
|
if (idx == 0) {
|
|
|
|
|
return {
|
|
|
|
|
start: startPos,
|
|
|
|
|
controlPoint: segment.controlPoint ?? null,
|
|
|
|
|
end: segment.next,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const start = segments[idx - 1].next
|
|
|
|
|
return {
|
|
|
|
|
start,
|
|
|
|
|
controlPoint: segment.controlPoint ?? null,
|
|
|
|
|
end: segment.next,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Computes and return the segments edition points
|
|
|
|
@ -398,14 +401,12 @@ export default function BendableArrow({
|
|
|
|
|
}
|
|
|
|
|
}, [update, containerRef])
|
|
|
|
|
|
|
|
|
|
// Inserts a segment where the mouse double clicks on the arrow
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (forceStraight) return
|
|
|
|
|
|
|
|
|
|
const addSegment = (e: MouseEvent) => {
|
|
|
|
|
const addSegment = useCallback(
|
|
|
|
|
(e: ReactMouseEvent) => {
|
|
|
|
|
if (forceStraight) return
|
|
|
|
|
const parentBase = area.current!.getBoundingClientRect()
|
|
|
|
|
|
|
|
|
|
const clickAbsolutePos: Pos = { x: e.x, y: e.y }
|
|
|
|
|
const clickAbsolutePos: Pos = { x: e.pageX, y: e.pageY }
|
|
|
|
|
const clickPosBaseRatio = ratioWithinBase(
|
|
|
|
|
clickAbsolutePos,
|
|
|
|
|
parentBase,
|
|
|
|
@ -473,14 +474,9 @@ export default function BendableArrow({
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pathRef?.current?.addEventListener("dblclick", addSegment)
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
pathRef?.current?.removeEventListener("dblclick", addSegment)
|
|
|
|
|
}
|
|
|
|
|
}, [pathRef, segments, onSegmentsChanges])
|
|
|
|
|
},
|
|
|
|
|
[area, forceStraight, onSegmentsChanges, segments, startPos],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
@ -503,6 +499,7 @@ export default function BendableArrow({
|
|
|
|
|
}
|
|
|
|
|
fill="none"
|
|
|
|
|
tabIndex={0}
|
|
|
|
|
onDoubleClick={addSegment}
|
|
|
|
|
onKeyUp={(e) => {
|
|
|
|
|
if (onDeleteRequested && e.key == "Delete")
|
|
|
|
|
onDeleteRequested()
|
|
|
|
|