You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import useHandleEvents from 'hooks/useHandleEvents'
  2. import { getShapeUtils } from 'state/shape-utils'
  3. import { useRef } from 'react'
  4. import { useSelector } from 'state'
  5. import styled from 'styles'
  6. import tld from 'utils/tld'
  7. import vec from 'utils/vec'
  8. export default function Handles(): JSX.Element {
  9. const shape = useSelector(
  10. (s) =>
  11. s.values.selectedIds.length === 1 &&
  12. tld.getPage(s.data).shapes[s.values.selectedIds[0]]
  13. )
  14. const isSelecting = useSelector((s) =>
  15. s.isInAny('notPointing', 'pinching', 'translatingHandles')
  16. )
  17. if (!shape || !shape.handles || !isSelecting) return null
  18. const center = getShapeUtils(shape).getCenter(shape)
  19. return (
  20. <g transform={`rotate(${shape.rotation * (180 / Math.PI)},${center})`}>
  21. {Object.values(shape.handles).map((handle) => (
  22. <Handle
  23. key={handle.id}
  24. id={handle.id}
  25. point={vec.add(handle.point, shape.point)}
  26. />
  27. ))}
  28. </g>
  29. )
  30. }
  31. function Handle({ id, point }: { id: string; point: number[] }) {
  32. const rGroup = useRef<SVGGElement>(null)
  33. const events = useHandleEvents(id, rGroup)
  34. return (
  35. <StyledGroup
  36. key={id}
  37. className="handles"
  38. ref={rGroup}
  39. {...events}
  40. pointerEvents="all"
  41. transform={`translate(${point})`}
  42. >
  43. <HandleCircleOuter r={12} />
  44. <use href="#handle" pointerEvents="none" />
  45. </StyledGroup>
  46. )
  47. }
  48. const StyledGroup = styled('g', {
  49. '&:hover': {
  50. cursor: 'pointer',
  51. },
  52. '&:active': {
  53. cursor: 'none',
  54. },
  55. })
  56. const HandleCircleOuter = styled('circle', {
  57. fill: 'transparent',
  58. stroke: 'none',
  59. opacity: 0.2,
  60. pointerEvents: 'all',
  61. cursor: 'pointer',
  62. transform: 'scale(var(--scale))',
  63. '&:hover': {
  64. fill: '$selected',
  65. '& > *': {
  66. stroke: '$selected',
  67. },
  68. },
  69. '&:active': {
  70. fill: '$selected',
  71. },
  72. })