tldraw/components/canvas/shape.tsx

150 lines
3.1 KiB
TypeScript
Raw Normal View History

2021-07-09 20:04:41 +00:00
import useShapeEvents from 'hooks/useShapeEvents'
import { Shape as _Shape, ShapeType, TextShape } from 'types'
import { getShapeUtils } from 'state/shape-utils'
import { shallowEqual } from 'utils'
import { memo, useRef } from 'react'
2021-07-09 22:35:38 +00:00
import styled from 'styles'
2021-07-09 20:04:41 +00:00
interface ShapeProps {
shape: _Shape
isEditing: boolean
isHovered: boolean
isSelected: boolean
2021-07-10 20:39:29 +00:00
isDarkMode: boolean
2021-07-09 20:04:41 +00:00
isCurrentParent: boolean
}
const Shape = memo(
({
shape,
isEditing,
isHovered,
isSelected,
2021-07-10 20:39:29 +00:00
isDarkMode,
2021-07-09 20:04:41 +00:00
isCurrentParent,
}: ShapeProps) => {
const rGroup = useRef<SVGGElement>(null)
const events = useShapeEvents(shape.id, isCurrentParent, rGroup)
const utils = getShapeUtils(shape)
const center = utils.getCenter(shape)
const rotation = shape.rotation * (180 / Math.PI)
2021-07-09 22:35:38 +00:00
const transform = `rotate(${rotation}, ${center}) translate(${shape.point})`
2021-07-09 20:04:41 +00:00
return (
2021-07-09 22:35:38 +00:00
<ShapeGroup
2021-07-09 20:04:41 +00:00
ref={rGroup}
id={shape.id}
transform={transform}
2021-07-09 22:35:38 +00:00
isCurrentParent={isCurrentParent}
2021-07-09 20:04:41 +00:00
filter={isHovered ? 'url(#expand)' : 'none'}
{...events}
>
{isEditing && shape.type === ShapeType.Text ? (
2021-07-10 20:39:29 +00:00
<EditingTextShape shape={shape} isDarkMode={isDarkMode} />
2021-07-09 20:04:41 +00:00
) : (
<RenderedShape
shape={shape}
isEditing={isEditing}
isHovered={isHovered}
isSelected={isSelected}
2021-07-10 20:39:29 +00:00
isDarkMode={isDarkMode}
2021-07-09 20:04:41 +00:00
isCurrentParent={isCurrentParent}
/>
)}
2021-07-09 22:35:38 +00:00
</ShapeGroup>
2021-07-09 20:04:41 +00:00
)
},
shallowEqual
)
export default Shape
interface RenderedShapeProps {
shape: _Shape
isEditing: boolean
isHovered: boolean
isSelected: boolean
2021-07-10 20:39:29 +00:00
isDarkMode: boolean
2021-07-09 20:04:41 +00:00
isCurrentParent: boolean
}
const RenderedShape = memo(
function RenderedShape({
shape,
isEditing,
isHovered,
isSelected,
2021-07-10 20:39:29 +00:00
isDarkMode,
2021-07-09 20:04:41 +00:00
isCurrentParent,
}: RenderedShapeProps) {
return getShapeUtils(shape).render(shape, {
isEditing,
isHovered,
isSelected,
2021-07-10 20:39:29 +00:00
isDarkMode,
2021-07-09 20:04:41 +00:00
isCurrentParent,
})
},
(prev, next) => {
if (
prev.isEditing !== next.isEditing ||
prev.isHovered !== next.isHovered ||
prev.isSelected !== next.isSelected ||
2021-07-10 20:39:29 +00:00
prev.isDarkMode !== next.isDarkMode ||
2021-07-09 20:04:41 +00:00
prev.isCurrentParent !== next.isCurrentParent
) {
return false
}
if (next.shape !== prev.shape) {
return !getShapeUtils(next.shape).shouldRender(next.shape, prev.shape)
}
return true
}
)
2021-07-10 20:39:29 +00:00
function EditingTextShape({
shape,
isDarkMode,
}: {
shape: TextShape
isDarkMode: boolean
}) {
2021-07-09 20:04:41 +00:00
const ref = useRef<HTMLTextAreaElement>(null)
return getShapeUtils(shape).render(shape, {
ref,
isEditing: true,
isHovered: false,
isSelected: false,
2021-07-10 20:39:29 +00:00
isDarkMode,
2021-07-09 20:04:41 +00:00
isCurrentParent: false,
})
}
2021-07-09 22:35:38 +00:00
const ShapeGroup = styled('g', {
outline: 'none',
'& > *[data-shy=true]': {
opacity: 0,
},
'&:hover': {
'& > *[data-shy=true]': {
opacity: 1,
},
},
variants: {
isCurrentParent: {
true: {
'& > *[data-shy=true]': {
opacity: 1,
},
},
},
},
})