Spaces:
Building
Building
// Copyright (c) Meta Platforms, Inc. and affiliates. | |
// This source code is licensed under the MIT license found in the | |
// LICENSE file in the root directory of this source tree. | |
import React from "react"; | |
const Circle = ({ | |
cx, | |
cy, | |
xBounds = [-Infinity, Infinity], | |
yBounds = [-Infinity, Infinity], | |
onPositionUpdate, | |
onHover, | |
...props | |
}) => { | |
// credit: https://gist.github.com/hashrock/0e8f10d9a233127c5e33b09ca6883ff4 | |
// | |
const [position, setPositionRaw] = React.useState({ | |
x: cx, | |
y: cy, | |
active: false, | |
offset: {}, | |
}); | |
let [minX, maxX] = xBounds; | |
let [minY, maxY] = yBounds; | |
const setPosition = React.useCallback( | |
(pos) => { | |
onPositionUpdate(pos); | |
setPositionRaw(pos); | |
}, | |
[setPositionRaw, onPositionUpdate] | |
); | |
const handlePointerDown = (e) => { | |
const el = e.target; | |
const bbox = e.target.getBoundingClientRect(); | |
const x = e.clientX - bbox.left; | |
const y = e.clientY - bbox.top; | |
el.setPointerCapture(e.pointerId); | |
setPosition({ | |
...position, | |
active: true, | |
offset: { | |
x: Math.min(Math.max(x, minX), maxX), | |
y: Math.min(Math.max(y, minY), maxY), | |
}, | |
}); | |
}; | |
const handlePointerMove = (e) => { | |
const bbox = e.target.getBoundingClientRect(); | |
const x = e.clientX - bbox.left; | |
const y = e.clientY - bbox.top; | |
const newX = position.x - (position.offset.x - x); | |
const newY = position.y - (position.offset.y - y); | |
const movePosition = { | |
...position, | |
x: Math.min(Math.max(newX, minX), maxX), | |
y: Math.min(Math.max(newY, minY), maxY), | |
}; | |
if (position.active) { | |
setPosition(movePosition); | |
} | |
}; | |
const handlePointerEnter = () => { | |
onHover(true); | |
}; | |
const handlePointerLeave = () => { | |
onHover(false); | |
}; | |
const handlePointerUp = (e) => { | |
setPosition({ | |
...position, | |
active: false, | |
}); | |
}; | |
return ( | |
<circle | |
cx={position.x} | |
cy={position.y} | |
onPointerDown={handlePointerDown} | |
onPointerUp={handlePointerUp} | |
onPointerMove={handlePointerMove} | |
onPointerOut={handlePointerLeave} | |
onPointerEnter={handlePointerEnter} | |
{...props} | |
fill={position.active ? "red" : "#aaa"} | |
/> | |
); | |
}; | |
export default Circle; | |