Move SVG container to shape implementations

This commit is contained in:
Steve Ruiz 2021-09-11 17:21:10 +01:00
parent 4c41d98c8e
commit 5359e92771
10 changed files with 138 additions and 106 deletions

View file

@ -1,2 +1,3 @@
export * from './renderer'
export { brushUpdater } from './brush'
export * from './svg-container'

View file

@ -1,11 +1,10 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as React from 'react'
import { usePosition, useShapeEvents } from '+hooks'
import type { IShapeTreeNode, TLBounds, TLShape, TLShapeUtil } from '+types'
import { useShapeEvents } from '+hooks'
import type { IShapeTreeNode, TLShape, TLShapeUtil } from '+types'
import { RenderedShape } from './rendered-shape'
import { EditingTextShape } from './editing-text-shape'
import { Container } from '+components/container'
import { SVGContainer } from '+components/svg-container'
// function setTransform(elm: HTMLDivElement, bounds: TLBounds, rotation = 0) {
// const transform = `
@ -43,33 +42,31 @@ export const Shape = <
bounds={bounds}
rotation={shape.rotation}
>
<SVGContainer>
{isEditing && utils.isEditableText ? (
<EditingTextShape
shape={shape}
isBinding={false}
isCurrentParent={false}
isEditing={true}
isHovered={isHovered}
isSelected={isSelected}
utils={utils as any}
meta={meta as any}
events={events}
/>
) : (
<RenderedShape
shape={shape}
isBinding={isBinding}
isCurrentParent={isCurrentParent}
isEditing={isEditing}
isHovered={isHovered}
isSelected={isSelected}
utils={utils as any}
meta={meta as any}
events={events}
/>
)}
</SVGContainer>
{isEditing && utils.isEditableText ? (
<EditingTextShape
shape={shape}
isBinding={false}
isCurrentParent={false}
isEditing={true}
isHovered={isHovered}
isSelected={isSelected}
utils={utils as any}
meta={meta as any}
events={events}
/>
) : (
<RenderedShape
shape={shape}
isBinding={isBinding}
isCurrentParent={isCurrentParent}
isEditing={isEditing}
isHovered={isHovered}
isSelected={isSelected}
utils={utils as any}
meta={meta as any}
events={events}
/>
)}
</Container>
)
}

View file

@ -1,13 +1,15 @@
import * as React from 'react'
interface SvgContainerProps {
interface SvgContainerProps extends React.SVGProps<SVGSVGElement> {
children: React.ReactNode
}
export const SVGContainer = React.memo(({ children }: SvgContainerProps) => {
return (
<svg className="tl-positioned-svg">
<g className="tl-centered-g">{children}</g>
</svg>
)
})
export const SVGContainer = React.memo(
React.forwardRef<SVGSVGElement, SvgContainerProps>(({ children, ...rest }, ref) => {
return (
<svg ref={ref} className="tl-positioned-svg" {...rest}>
<g className="tl-centered-g">{children}</g>
</svg>
)
})
)

View file

@ -151,7 +151,6 @@ const tlcss = css`
.tl-positioned-svg {
width: 100%;
height: 100%;
pointer-events: none;
}
.tl-layer {

View file

@ -1,5 +1,6 @@
import * as React from 'react'
import {
SVGContainer,
TLBounds,
Utils,
Vec,
@ -20,10 +21,9 @@ import {
DashStyle,
TLDrawShape,
ArrowBinding,
TLDrawRenderInfo,
} from '~types'
export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGGElement> {
export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGSVGElement> {
type = TLDrawShapeType.Arrow as const
toolType = TLDrawToolType.Handle
canStyleFill = false
@ -71,7 +71,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGGElement> {
return next.handles !== prev.handles || next.style !== prev.style
}
render = React.forwardRef<SVGGElement, TLShapeProps<ArrowShape, SVGGElement>>(
render = React.forwardRef<SVGSVGElement, TLShapeProps<ArrowShape, SVGSVGElement>>(
({ shape, meta, events }, ref) => {
const {
handles: { start, bend, end },
@ -220,7 +220,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGGElement> {
const sw = strokeWidth * 1.618
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
<g pointerEvents="none">
{shaftPath}
{startArrowHead && (
@ -250,7 +250,7 @@ export class Arrow extends TLDrawShapeUtil<ArrowShape, SVGGElement> {
/>
)}
</g>
</g>
</SVGContainer>
)
}
)

View file

@ -1,10 +1,18 @@
import * as React from 'react'
import { TLBounds, Utils, Vec, TLTransformInfo, Intersect, TLShapeProps } from '@tldraw/core'
import {
SVGContainer,
TLBounds,
Utils,
Vec,
TLTransformInfo,
Intersect,
TLShapeProps,
} from '@tldraw/core'
import getStroke, { getStrokePoints } from 'perfect-freehand'
import { defaultStyle, getShapeStyle } from '~shape/shape-styles'
import { DrawShape, DashStyle, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType } from '~types'
export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
export class Draw extends TLDrawShapeUtil<DrawShape, SVGSVGElement> {
type = TLDrawShapeType.Draw as const
toolType = TLDrawToolType.Draw
@ -30,7 +38,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
return next.points !== prev.points || next.style !== prev.style
}
render = React.forwardRef<SVGGElement, TLShapeProps<DrawShape, SVGGElement>>(
render = React.forwardRef<SVGSVGElement, TLShapeProps<DrawShape, SVGSVGElement>>(
({ shape, meta, events, isEditing }, ref) => {
const { points, style } = shape
@ -47,7 +55,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
const sw = strokeWidth * 0.618
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
<circle
r={strokeWidth * 0.618}
fill={styles.stroke}
@ -55,7 +63,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
strokeWidth={sw}
pointerEvents="all"
/>
</g>
</SVGContainer>
)
}
@ -76,7 +84,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
: Utils.getFromCache(this.drawPathCache, points, () => getDrawStrokePath(shape, false))
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
{shouldFill && (
<path
d={polygonPathData}
@ -96,7 +104,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
strokeLinecap="round"
pointerEvents="all"
/>
</g>
</SVGContainer>
)
}
@ -121,7 +129,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
const sw = strokeWidth * 1.618
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
<path
d={path}
fill={shouldFill ? styles.fill : 'none'}
@ -142,7 +150,7 @@ export class Draw extends TLDrawShapeUtil<DrawShape, SVGGElement> {
strokeLinecap="round"
pointerEvents="stroke"
/>
</g>
</SVGContainer>
)
}
)

View file

@ -1,10 +1,17 @@
import * as React from 'react'
import { Utils, TLTransformInfo, TLBounds, Intersect, TLShapeProps, Vec } from '@tldraw/core'
import {
SVGContainer,
Utils,
TLTransformInfo,
TLBounds,
Intersect,
TLShapeProps,
Vec,
} from '@tldraw/core'
import {
ArrowShape,
DashStyle,
EllipseShape,
TLDrawRenderInfo,
TLDrawShapeType,
TLDrawShapeUtil,
TLDrawToolType,
@ -15,7 +22,7 @@ import getStroke from 'perfect-freehand'
// TODO
// [ ] Improve indicator shape for drawn shapes
export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGSVGElement> {
type = TLDrawShapeType.Ellipse as const
toolType = TLDrawToolType.Bounds
pathCache = new WeakMap<EllipseShape, string>([])
@ -37,7 +44,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
return next.radius !== prev.radius || next.style !== prev.style
}
render = React.forwardRef<SVGGElement, TLShapeProps<EllipseShape, SVGGElement>>(
render = React.forwardRef<SVGSVGElement, TLShapeProps<EllipseShape, SVGSVGElement>>(
({ shape, meta, isBinding, events }, ref) => {
const {
radius: [radiusX, radiusY],
@ -56,7 +63,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
)
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
{isBinding && (
<ellipse
className="tl-binding-indicator"
@ -84,7 +91,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
</SVGContainer>
)
}
@ -102,7 +109,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
const sw = strokeWidth * 1.618
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
{isBinding && (
<ellipse
className="tl-binding-indicator"
@ -126,7 +133,7 @@ export class Ellipse extends TLDrawShapeUtil<EllipseShape, SVGGElement> {
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
</SVGContainer>
)
}
)

View file

@ -1,5 +1,5 @@
import * as React from 'react'
import { TLBounds, Utils, Vec, Intersect, TLShapeProps } from '@tldraw/core'
import { SVGContainer, TLBounds, Utils, Vec, Intersect, TLShapeProps } from '@tldraw/core'
import { defaultStyle, getPerfectDashProps } from '~shape/shape-styles'
import {
GroupShape,
@ -14,7 +14,7 @@ import {
// TODO
// [ ] - Find bounds based on common bounds of descendants
export class Group extends TLDrawShapeUtil<GroupShape, SVGGElement> {
export class Group extends TLDrawShapeUtil<GroupShape, SVGSVGElement> {
type = TLDrawShapeType.Group as const
toolType = TLDrawToolType.Bounds
canBind = true
@ -38,7 +38,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGGElement> {
return next.size !== prev.size || next.style !== prev.style
}
render = React.forwardRef<SVGGElement, TLShapeProps<GroupShape, SVGGElement>>(
render = React.forwardRef<SVGSVGElement, TLShapeProps<GroupShape, SVGSVGElement>>(
({ shape, isBinding, isHovered, isSelected, events }, ref) => {
const { id, size } = shape
@ -77,7 +77,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGGElement> {
})
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
{isBinding && (
<rect
className="tl-binding-indicator"
@ -96,7 +96,7 @@ export class Group extends TLDrawShapeUtil<GroupShape, SVGGElement> {
pointerEvents="all"
/>
<g pointerEvents="stroke">{paths}</g>
</g>
</SVGContainer>
)
}
)

View file

@ -1,5 +1,13 @@
import * as React from 'react'
import { TLBounds, Utils, Vec, TLTransformInfo, Intersect, TLShapeProps } from '@tldraw/core'
import {
TLBounds,
Utils,
Vec,
TLTransformInfo,
Intersect,
TLShapeProps,
SVGContainer,
} from '@tldraw/core'
import getStroke from 'perfect-freehand'
import { getPerfectDashProps, defaultStyle, getShapeStyle } from '~shape/shape-styles'
import {
@ -14,7 +22,7 @@ import {
// TODO
// [ ] - Make sure that fill does not extend drawn shape at corners
export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGGElement> {
export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGSVGElement> {
type = TLDrawShapeType.Rectangle as const
toolType = TLDrawToolType.Bounds
canBind = true
@ -36,7 +44,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGGElement> {
return next.size !== prev.size || next.style !== prev.style
}
render = React.forwardRef<SVGGElement, TLShapeProps<RectangleShape, SVGGElement>>(
render = React.forwardRef<SVGSVGElement, TLShapeProps<RectangleShape, SVGSVGElement>>(
({ shape, isBinding, meta, events }, ref) => {
const { id, size, style } = shape
const styles = getShapeStyle(style, meta.isDarkMode)
@ -46,7 +54,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGGElement> {
const pathData = Utils.getFromCache(this.pathCache, shape.size, () => renderPath(shape))
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
{isBinding && (
<rect
className="tl-binding-indicator"
@ -72,7 +80,7 @@ export class Rectangle extends TLDrawShapeUtil<RectangleShape, SVGGElement> {
strokeWidth={styles.strokeWidth}
pointerEvents="all"
/>
</g>
</SVGContainer>
)
}

View file

@ -1,5 +1,13 @@
import * as React from 'react'
import { TLBounds, Utils, Vec, TLTransformInfo, Intersect, TLShapeProps } from '@tldraw/core'
import {
SVGContainer,
TLBounds,
Utils,
Vec,
TLTransformInfo,
Intersect,
TLShapeProps,
} from '@tldraw/core'
import { getShapeStyle, getFontSize, getFontStyle, defaultStyle } from '~shape/shape-styles'
import { TextShape, TLDrawShapeUtil, TLDrawShapeType, TLDrawToolType, ArrowShape } from '~types'
import styled from '~styles'
@ -49,7 +57,7 @@ if (typeof window !== 'undefined') {
melm = getMeasurementDiv()
}
export class Text extends TLDrawShapeUtil<TextShape, SVGGElement> {
export class Text extends TLDrawShapeUtil<TextShape, SVGSVGElement> {
type = TLDrawShapeType.Text as const
toolType = TLDrawToolType.Text
isAspectRatioLocked = true
@ -83,7 +91,7 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGGElement> {
)
}
render = React.forwardRef<SVGGElement, TLShapeProps<TextShape, SVGGElement>>(
render = React.forwardRef<SVGSVGElement, TLShapeProps<TextShape, SVGSVGElement>>(
({ shape, meta, isEditing, isBinding, events }, ref) => {
const rInput = React.useRef<HTMLTextAreaElement>(null)
const { id, text, style } = shape
@ -151,7 +159,7 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGGElement> {
if (!isEditing) {
return (
<g ref={ref} {...events}>
<SVGContainer ref={ref} {...events}>
{isBinding && (
<rect
className="tl-binding-indicator"
@ -183,41 +191,43 @@ export class Text extends TLDrawShapeUtil<TextShape, SVGGElement> {
{str}
</text>
))}
</g>
</SVGContainer>
)
}
return (
<foreignObject
width={bounds.width}
height={bounds.height}
pointerEvents="none"
onPointerDown={(e) => e.stopPropagation()}
>
<StyledTextArea
ref={rInput}
style={{
font,
color: styles.stroke,
}}
name="text"
defaultValue={text}
tabIndex={-1}
autoComplete="false"
autoCapitalize="false"
autoCorrect="false"
autoSave="false"
placeholder=""
color={styles.stroke}
autoFocus={true}
onFocus={handleFocus}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onChange={handleChange}
onPointerDown={handlePointerDown}
/>
</foreignObject>
<SVGContainer ref={ref} {...events}>
<foreignObject
width={bounds.width}
height={bounds.height}
pointerEvents="none"
onPointerDown={(e) => e.stopPropagation()}
>
<StyledTextArea
ref={rInput}
style={{
font,
color: styles.stroke,
}}
name="text"
defaultValue={text}
tabIndex={-1}
autoComplete="false"
autoCapitalize="false"
autoCorrect="false"
autoSave="false"
placeholder=""
color={styles.stroke}
autoFocus={true}
onFocus={handleFocus}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onChange={handleChange}
onPointerDown={handlePointerDown}
/>
</foreignObject>
</SVGContainer>
)
}
)