Move SVG container to shape implementations
This commit is contained in:
parent
4c41d98c8e
commit
5359e92771
10 changed files with 138 additions and 106 deletions
|
@ -1,2 +1,3 @@
|
|||
export * from './renderer'
|
||||
export { brushUpdater } from './brush'
|
||||
export * from './svg-container'
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
|
|
@ -151,7 +151,6 @@ const tlcss = css`
|
|||
.tl-positioned-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tl-layer {
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue