2021-05-31 19:13:43 +00:00
|
|
|
import useHandleEvents from 'hooks/useHandleEvents'
|
|
|
|
import { getShapeUtils } from 'lib/shape-utils'
|
|
|
|
import { useRef } from 'react'
|
|
|
|
import { useSelector } from 'state'
|
|
|
|
import styled from 'styles'
|
|
|
|
import { deepCompareArrays, getPage } from 'utils/utils'
|
|
|
|
import * as vec from 'utils/vec'
|
|
|
|
|
|
|
|
export default function Handles() {
|
|
|
|
const selectedIds = useSelector(
|
|
|
|
(s) => Array.from(s.values.selectedIds.values()),
|
|
|
|
deepCompareArrays
|
|
|
|
)
|
|
|
|
|
|
|
|
const shape = useSelector(
|
|
|
|
({ data }) =>
|
|
|
|
selectedIds.length === 1 && getPage(data).shapes[selectedIds[0]]
|
|
|
|
)
|
|
|
|
|
2021-06-05 06:36:39 +00:00
|
|
|
const isSelecting = useSelector((s) =>
|
|
|
|
s.isInAny('notPointing', 'pinching', 'translatingHandles')
|
|
|
|
)
|
2021-05-31 19:13:43 +00:00
|
|
|
|
2021-06-01 21:49:32 +00:00
|
|
|
if (!shape.handles || !isSelecting) return null
|
2021-05-31 19:13:43 +00:00
|
|
|
|
2021-06-05 07:42:17 +00:00
|
|
|
const center = getShapeUtils(shape).getCenter(shape)
|
|
|
|
|
2021-05-31 19:13:43 +00:00
|
|
|
return (
|
2021-06-05 07:42:17 +00:00
|
|
|
<g transform={`rotate(${shape.rotation * (180 / Math.PI)},${center})`}>
|
2021-05-31 19:13:43 +00:00
|
|
|
{Object.values(shape.handles).map((handle) => (
|
|
|
|
<Handle
|
|
|
|
key={handle.id}
|
|
|
|
shapeId={shape.id}
|
|
|
|
id={handle.id}
|
|
|
|
point={vec.add(handle.point, shape.point)}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</g>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
function Handle({
|
|
|
|
shapeId,
|
|
|
|
id,
|
|
|
|
point,
|
|
|
|
}: {
|
|
|
|
shapeId: string
|
|
|
|
id: string
|
|
|
|
point: number[]
|
|
|
|
}) {
|
|
|
|
const rGroup = useRef<SVGGElement>(null)
|
|
|
|
const events = useHandleEvents(id, rGroup)
|
|
|
|
|
|
|
|
return (
|
2021-06-05 06:36:39 +00:00
|
|
|
<StyledGroup
|
2021-05-31 19:13:43 +00:00
|
|
|
key={id}
|
2021-06-05 06:36:39 +00:00
|
|
|
className="handles"
|
2021-05-31 19:13:43 +00:00
|
|
|
ref={rGroup}
|
|
|
|
{...events}
|
|
|
|
pointerEvents="all"
|
|
|
|
transform={`translate(${point})`}
|
|
|
|
>
|
2021-06-01 21:49:32 +00:00
|
|
|
<HandleCircleOuter r={12} />
|
2021-06-05 06:36:39 +00:00
|
|
|
<use href="#handle" pointerEvents="none" />
|
|
|
|
</StyledGroup>
|
2021-05-31 19:13:43 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-06-05 06:36:39 +00:00
|
|
|
const StyledGroup = styled('g', {
|
|
|
|
'&:hover': {
|
|
|
|
cursor: 'pointer',
|
|
|
|
},
|
|
|
|
'&:active': {
|
|
|
|
cursor: 'none',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2021-05-31 19:13:43 +00:00
|
|
|
const HandleCircleOuter = styled('circle', {
|
|
|
|
fill: 'transparent',
|
2021-06-05 06:36:39 +00:00
|
|
|
stroke: 'none',
|
|
|
|
opacity: 0.2,
|
2021-05-31 19:13:43 +00:00
|
|
|
pointerEvents: 'all',
|
|
|
|
cursor: 'pointer',
|
2021-06-05 06:36:39 +00:00
|
|
|
transform: 'scale(var(--scale))',
|
|
|
|
'&:hover': {
|
|
|
|
fill: '$selected',
|
|
|
|
'& > *': {
|
|
|
|
stroke: '$selected',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'&:active': {
|
|
|
|
fill: '$selected',
|
|
|
|
},
|
2021-05-31 19:13:43 +00:00
|
|
|
})
|