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.

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