From 552c8457efde63edaa0de8d648d82917afcb0a95 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Fri, 9 Jul 2021 20:43:18 +0100 Subject: [PATCH] Adjust pointer events for shapes --- components/canvas/defs.tsx | 2 +- components/canvas/page.tsx | 11 +- components/canvas/shape.tsx | 209 -------------------------------- state/shape-utils/arrow.tsx | 31 ++--- state/shape-utils/draw.tsx | 45 ++++--- state/shape-utils/ellipse.tsx | 7 +- state/shape-utils/polyline.tsx | 30 ++--- state/shape-utils/rectangle.tsx | 41 ++++--- 8 files changed, 80 insertions(+), 296 deletions(-) delete mode 100644 components/canvas/shape.tsx diff --git a/components/canvas/defs.tsx b/components/canvas/defs.tsx index 111bc197e..b26154bed 100644 --- a/components/canvas/defs.tsx +++ b/components/canvas/defs.tsx @@ -19,7 +19,7 @@ function ExpandDef() { const zoom = useSelector((s) => tld.getCurrentCamera(s.data).zoom) return ( - + ) } diff --git a/components/canvas/page.tsx b/components/canvas/page.tsx index 84f6d5da6..9430e60f3 100644 --- a/components/canvas/page.tsx +++ b/components/canvas/page.tsx @@ -127,8 +127,9 @@ const TranslatedShape = memo( }: TranslatedShapeProps) => { const rGroup = useRef(null) const events = useShapeEvents(shape.id, isCurrentParent, rGroup) + const utils = getShapeUtils(shape) - const center = getShapeUtils(shape).getCenter(shape) + const center = utils.getCenter(shape) const rotation = shape.rotation * (180 / Math.PI) const transform = ` rotate(${rotation}, ${center}) @@ -136,7 +137,13 @@ const TranslatedShape = memo( ` return ( - + {isEditing && shape.type === ShapeType.Text ? ( ) : ( diff --git a/components/canvas/shape.tsx b/components/canvas/shape.tsx deleted file mode 100644 index 2b78f77ec..000000000 --- a/components/canvas/shape.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import React, { useRef, memo, useEffect } from 'react' -import state, { useSelector } from 'state' -import styled from 'styles' -import { getShapeUtils } from 'state/shape-utils' -import { deepCompareArrays } from 'utils' -import tld from 'utils/tld' -import useShapeEvents from 'hooks/useShapeEvents' -import useShape from 'hooks/useShape' -import vec from 'utils/vec' -import { getShapeStyle } from 'state/shape-styles' -import { Shape as _Shape } from 'types' - -interface ShapeProps { - shape: _Shape - parent?: _Shape -} - -function Shape({ shape, parent }: ShapeProps): JSX.Element { - const rGroup = useRef(null) - - const { id, isHidden, children } = shape - const style = getShapeStyle(shape.style) - const { strokeWidth } = style - - const center = getShapeUtils(shape).getCenter(shape) - const rotation = shape.rotation * (180 / Math.PI) - const parentPoint = parent?.point || [0, 0] - - const transform = ` - translate(${vec.neg(parentPoint)}) - rotate(${rotation}, ${center}) - translate(${shape.point}) - ` - - const isCurrentParent = false - - const events = useShapeEvents(shape.id, isCurrentParent, rGroup) - - // From here on, not reactive—if we're here, we can trust that the - // shape in state is a shape with changes that we need to render. - - const { isParent, isForeignObject, canStyleFill } = getShapeUtils(shape) - - return ( - - {isForeignObject ? ( - - ) : ( - - )} - - {!isHidden && - (isForeignObject ? ( - - ) : ( - - ))} - - {isParent && - children.map((shapeId) => ( - - ))} - - ) -} - -export default memo(Shape) - -// function Def({ id }: { id: string }) { -// const shape = useShape(id) -// if (!shape) return null -// return getShapeUtils(shape).render(shape, { isEditing: false }) -// } - -interface RealShapeProps { - id: string - isParent: boolean - strokeWidth: number - shape: _Shape -} - -const RealShape = memo( - function RealShape({ shape }: RealShapeProps) { - return getShapeUtils(shape).render(shape, { isEditing: false }) - }, - (prev, next) => { - return ( - prev.shape && - next.shape && - next.shape !== prev.shape && - getShapeUtils(next.shape).shouldRender(next.shape, prev.shape) - ) - } -) - -const ForeignObjectHover = memo(function ForeignObjectHover({ - id, -}: { - id: string -}) { - const size = useSelector((s) => { - const shape = tld.getPage(s.data).shapes[id] - if (shape === undefined) return [0, 0] - const bounds = getShapeUtils(shape).getBounds(shape) - - return [bounds.width, bounds.height] - }, deepCompareArrays) - - return ( - - ) -}) - -const ForeignObjectRender = memo(function ForeignObjectRender({ - id, -}: { - id: string -}) { - const shape = useShape(id) - - const rFocusable = useRef(null) - - const isEditing = useSelector((s) => s.data.editingId === id) - - useEffect(() => { - if (isEditing) { - setTimeout(() => { - const elm = rFocusable.current - if (!elm) return - elm.focus() - }, 0) - } - }, [isEditing]) - - if (shape === undefined) return null - - return getShapeUtils(shape).render(shape, { isEditing, ref: rFocusable }) -}) - -const EventSoak = styled('use', { - opacity: 0, - strokeLinecap: 'round', - strokeLinejoin: 'round', - variants: { - variant: { - ghost: { - pointerEvents: 'all', - filter: 'none', - opacity: 0, - }, - hollow: { - pointerEvents: 'stroke', - }, - filled: { - pointerEvents: 'all', - }, - }, - }, -}) - -const StyledGroup = styled('g', { - outline: 'none', - - '& > *[data-shy=true]': { - opacity: 0, - }, - - '&:hover': { - '& > *[data-shy=true]': { - opacity: 1, - }, - }, - - variants: { - isCurrentParent: { - true: { - '& > *[data-shy=true]': { - opacity: 1, - }, - }, - }, - }, -}) diff --git a/state/shape-utils/arrow.tsx b/state/shape-utils/arrow.tsx index 5b0f5d443..70375812a 100644 --- a/state/shape-utils/arrow.tsx +++ b/state/shape-utils/arrow.tsx @@ -7,7 +7,6 @@ import { getBoundsFromPoints, translateBounds, pointInBounds, - pointInCircle, circleFromThreePoints, isAngleBetween, getPerfectDashProps, @@ -102,8 +101,8 @@ const arrow = registerShapeUtils({ return shape.handles !== prev.handles || shape.style !== prev.style }, - render(shape, { isHovered }) { - const { id, bend, handles, style } = shape + render(shape) { + const { bend, handles, style } = shape const { start, end, bend: _bend } = handles const isStraightLine = @@ -146,11 +145,11 @@ const arrow = registerShapeUtils({ ({ strokeDasharray={strokeDasharray} strokeDashoffset={strokeDashoffset} strokeLinecap="round" + strokeLinejoin="round" /> ) @@ -206,6 +206,7 @@ const arrow = registerShapeUtils({ strokeDasharray="none" strokeDashoffset="none" strokeLinecap="round" + strokeLinejoin="round" /> ({ strokeDasharray={strokeDasharray} strokeDashoffset={strokeDashoffset} strokeLinecap="round" + strokeLinejoin="round" /> ) @@ -223,7 +225,7 @@ const arrow = registerShapeUtils({ const sw = strokeWidth * 1.618 return ( - + {shaftPath} {shape.decorations.start === Decoration.Arrow && ( ({ strokeDasharray="none" strokeLinecap="round" strokeLinejoin="round" + pointerEvents="stroke" /> )} {shape.decorations.end === Decoration.Arrow && ( @@ -247,6 +250,7 @@ const arrow = registerShapeUtils({ strokeDasharray="none" strokeLinecap="round" strokeLinejoin="round" + pointerEvents="stroke" /> )} @@ -308,21 +312,8 @@ const arrow = registerShapeUtils({ return vec.add(shape.point, vec.med(start.point, end.point)) }, - hitTest(shape, point) { - const { start, end } = shape.handles - if (shape.bend === 0) { - return ( - vec.distanceToLineSegment( - start.point, - end.point, - vec.sub(point, shape.point) - ) < 4 - ) - } - - const [cx, cy, r] = getCtp(shape) - - return !pointInCircle(point, vec.add(shape.point, [cx, cy]), r - 4) + hitTest() { + return true }, hitTestBounds(this, shape, brushBounds) { diff --git a/state/shape-utils/draw.tsx b/state/shape-utils/draw.tsx index 238f6e19f..9dc5f240a 100644 --- a/state/shape-utils/draw.tsx +++ b/state/shape-utils/draw.tsx @@ -41,13 +41,14 @@ const draw = registerShapeUtils({ }, render(shape, { isHovered }) { - const { id, points, style } = shape + const { points, style } = shape const styles = getShapeStyle(style) const strokeWidth = +styles.strokeWidth const shouldFill = + style.isFilled && points.length > 3 && vec.dist(points[0], points[points.length - 1]) < +styles.strokeWidth * 2 @@ -58,11 +59,11 @@ const draw = registerShapeUtils({ return ( ) @@ -80,15 +81,15 @@ const draw = registerShapeUtils({ }) return ( - + <> {shouldFill && ( )} ({ strokeWidth={strokeWidth} strokeLinejoin="round" strokeLinecap="round" + pointerEvents="all" filter={isHovered ? 'url(#expand)' : 'none'} /> - + ) } @@ -129,29 +131,29 @@ const draw = registerShapeUtils({ const sw = strokeWidth * 1.618 return ( - - {style.dash !== DashStyle.Solid && ( - - )} + <> + - + ) }, @@ -174,13 +176,8 @@ const draw = registerShapeUtils({ return getBoundsCenter(this.getBounds(shape)) }, - hitTest(shape, point) { - const pt = vec.sub(point, shape.point) - const min = +getShapeStyle(shape.style).strokeWidth - return shape.points.some( - (curr, i) => - i > 0 && vec.distanceToLineSegment(shape.points[i - 1], curr, pt) < min - ) + hitTest() { + return true }, hitTestBounds(this, shape, brushBounds) { diff --git a/state/shape-utils/ellipse.tsx b/state/shape-utils/ellipse.tsx index d812a2148..8e7412ad5 100644 --- a/state/shape-utils/ellipse.tsx +++ b/state/shape-utils/ellipse.tsx @@ -58,16 +58,16 @@ const ellipse = registerShapeUtils({ const path = pathCache.get(shape) return ( - + {style.isFilled && ( )} ({ fill={styles.stroke} stroke={styles.stroke} strokeWidth={strokeWidth} + pointerEvents="all" filter={isHovered ? 'url(#expand)' : 'none'} /> @@ -97,7 +98,6 @@ const ellipse = registerShapeUtils({ return ( ({ strokeWidth={sw} strokeDasharray={strokeDasharray} strokeDashoffset={strokeDashoffset} + pointerEvents={style.isFilled ? 'all' : 'stroke'} filter={isHovered ? 'url(#expand)' : 'none'} /> ) diff --git a/state/shape-utils/polyline.tsx b/state/shape-utils/polyline.tsx index 133a84b87..4571f57e0 100644 --- a/state/shape-utils/polyline.tsx +++ b/state/shape-utils/polyline.tsx @@ -28,19 +28,20 @@ const polyline = registerShapeUtils({ shouldRender(shape, prev) { return shape.points !== prev.points || shape.style !== prev.style }, - render(shape, { isHovered }) { - const { id, points } = shape + render(shape) { + const { points, style } = shape - const styles = getShapeStyle(shape.style) + const styles = getShapeStyle(style) return ( ) }, @@ -62,19 +63,8 @@ const polyline = registerShapeUtils({ return [bounds.minX + bounds.width / 2, bounds.minY + bounds.height / 2] }, - hitTest(shape, point) { - const pt = vec.sub(point, shape.point) - let prev = shape.points[0] - - for (let i = 1; i < shape.points.length; i++) { - const curr = shape.points[i] - if (vec.distanceToLineSegment(prev, curr, pt) < 4) { - return true - } - prev = curr - } - - return false + hitTest() { + return true }, hitTestBounds(this, shape, brushBounds) { @@ -126,7 +116,7 @@ const polyline = registerShapeUtils({ canTransform: true, canChangeAspectRatio: true, - canStyleFill: false, + canStyleFill: true, }) export default polyline diff --git a/state/shape-utils/rectangle.tsx b/state/shape-utils/rectangle.tsx index 2508479ff..31ef55c10 100644 --- a/state/shape-utils/rectangle.tsx +++ b/state/shape-utils/rectangle.tsx @@ -39,26 +39,29 @@ const rectangle = registerShapeUtils({ }) return ( - - + <> + {style.isFilled && ( + + )} - + ) } @@ -98,17 +101,21 @@ const rectangle = registerShapeUtils({ }) return ( - + <> - {paths} - + + {paths} + + ) },