import React, { useRef, memo } from 'react' import { useSelector } from 'state' import styled from 'styles' import { getShapeUtils } from 'lib/shape-utils' import { getPage } from 'utils/utils' import { ShapeStyles, ShapeType } from 'types' import useShapeEvents from 'hooks/useShapeEvents' import * as vec from 'utils/vec' import { getShapeStyle } from 'lib/shape-styles' interface ShapeProps { id: string isSelecting: boolean parentPoint: number[] } function Shape({ id, isSelecting, parentPoint }: ShapeProps) { const shape = useSelector((s) => getPage(s.data).shapes[id]) const rGroup = useRef(null) const events = useShapeEvents(id, shape?.type === ShapeType.Group, rGroup) // This is a problem with deleted shapes. The hooks in this component // may sometimes run before the hook in the Page component, which means // a deleted shape will still be pulled here before the page component // detects the change and pulls this component. if (!shape) return null const isGroup = shape.type === ShapeType.Group const center = getShapeUtils(shape).getCenter(shape) const transform = ` rotate(${shape.rotation * (180 / Math.PI)}, ${vec.sub(center, parentPoint)}) translate(${vec.sub(shape.point, parentPoint)}) ` const style = getShapeStyle(shape.style) return ( {isSelecting && !isGroup && ( )} {!shape.isHidden && } {isGroup && shape.children.map((shapeId) => ( ))} ) } interface RealShapeProps { isGroup: boolean id: string style: Partial> } const RealShape = memo(function RealShape({ isGroup, id, style, }: RealShapeProps) { return }) const StyledShape = styled('path', { strokeLinecap: 'round', strokeLinejoin: 'round', pointerEvents: 'none', }) const HoverIndicator = styled('path', { stroke: '$selected', strokeLinecap: 'round', strokeLinejoin: 'round', transform: 'all .2s', fill: 'transparent', filter: 'url(#expand)', variants: { variant: { hollow: { pointerEvents: 'stroke', }, filled: { pointerEvents: 'all', }, }, }, }) const StyledGroup = styled('g', { outline: 'none', [`& *[data-shy="true"]`]: { opacity: '0', }, [`& ${HoverIndicator}`]: { opacity: '0', }, [`&:hover ${HoverIndicator}`]: { opacity: '0.16', }, [`&:hover *[data-shy="true"]`]: { opacity: '1', }, variants: { isSelected: { true: { [`& *[data-shy="true"]`]: { opacity: '1', }, [`& ${HoverIndicator}`]: { opacity: '0.2', }, [`&:hover ${HoverIndicator}`]: { opacity: '0.3', }, [`&:active ${HoverIndicator}`]: { opacity: '0.3', }, }, false: { [`& ${HoverIndicator}`]: { opacity: '0', }, }, }, }, }) function Label({ children }: { children: React.ReactNode }) { return ( {children} ) } function pp(n: number[]) { return '[' + n.map((v) => v.toFixed(1)).join(', ') + ']' } export { HoverIndicator } export default memo(Shape)