parent
c3ff5a51bf
commit
d0cd924ca7
58 changed files with 521 additions and 523 deletions
|
@ -24,8 +24,6 @@
|
|||
"concurrently": "^7.0.0",
|
||||
"esbuild": "^0.14.54",
|
||||
"esbuild-serve": "^1.0.1",
|
||||
"mobx": "^6.3.13",
|
||||
"mobx-react-lite": "^3.2.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Renderer, TLPointerEventHandler, TLShapeUtilsMap } from '@tldraw/core'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Renderer, TLPointerEventHandler } from '@tldraw/core'
|
||||
import * as React from 'react'
|
||||
import { RectUtil, Shape } from './shapes'
|
||||
import { Page, PageState } from './stores'
|
||||
|
@ -23,11 +22,11 @@ const page = new Page({
|
|||
|
||||
const pageState = new PageState()
|
||||
|
||||
const shapeUtils: TLShapeUtilsMap<Shape> = {
|
||||
const shapeUtils = {
|
||||
rect: new RectUtil(),
|
||||
}
|
||||
|
||||
export default observer(function App() {
|
||||
export default function App() {
|
||||
const onHoverShape: TLPointerEventHandler = (e) => {
|
||||
pageState.setHoveredId(e.target)
|
||||
}
|
||||
|
@ -113,4 +112,4 @@ export default observer(function App() {
|
|||
/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { TLBounds, TLPage, TLPageState, Utils } from '@tldraw/core'
|
||||
import Vec from '@tldraw/vec'
|
||||
import { action, makeAutoObservable } from 'mobx'
|
||||
import type { Shape } from './shapes'
|
||||
|
||||
export class Page implements TLPage<Shape> {
|
||||
|
@ -15,10 +14,9 @@ export class Page implements TLPage<Shape> {
|
|||
this.name = name
|
||||
this.shapes = shapes
|
||||
this.bindings = bindings
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
|
||||
@action dragShape(id: string, point: number[]) {
|
||||
dragShape(id: string, point: number[]) {
|
||||
const shape = this.shapes[id]
|
||||
shape.point = Vec.sub(point, Vec.div(shape.size, 2))
|
||||
}
|
||||
|
@ -46,24 +44,23 @@ export class PageState implements TLPageState {
|
|||
this.id = id
|
||||
this.camera = camera
|
||||
this.selectedIds = selectedIds
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
|
||||
@action setHoveredId = (id: string | undefined) => {
|
||||
setHoveredId = (id: string | undefined) => {
|
||||
this.hoveredId = id
|
||||
}
|
||||
|
||||
@action setSelectedIds = (id: string) => {
|
||||
setSelectedIds = (id: string) => {
|
||||
if (!this.selectedIds.includes(id)) {
|
||||
this.selectedIds = [id]
|
||||
}
|
||||
}
|
||||
|
||||
@action clearSelectedIds = () => {
|
||||
clearSelectedIds = () => {
|
||||
this.selectedIds = []
|
||||
}
|
||||
|
||||
@action pan = (point: number[]) => {
|
||||
pan = (point: number[]) => {
|
||||
this.camera.point = Vec.add(this.camera.point, point)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
"init-package-json": "^2.0.5",
|
||||
"jest": "^27.4.7",
|
||||
"lint-staged": "^12.3.3",
|
||||
"mobx": "^6.3.8",
|
||||
"prettier": "^2.7.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"source-map-loader": "^3.0.1",
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
"@tldraw/intersect": "^1.7.1",
|
||||
"@tldraw/vec": "^1.7.1",
|
||||
"@use-gesture/react": "^10.2.14",
|
||||
"mobx-react-lite": "^3.2.3",
|
||||
"perfect-freehand": "^1.1.0",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
},
|
||||
|
@ -46,13 +45,12 @@
|
|||
"devDependencies": {
|
||||
"@swc-node/jest": "^1.4.3",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@tldraw/intersect": "*",
|
||||
"@tldraw/lfg": "latest",
|
||||
"@tldraw/vec": "*",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@tldraw/lfg": "latest",
|
||||
"mobx": "^6.3.8",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
|
|
@ -206,20 +206,22 @@ describe('When creating a realistic API around TLShapeUtil', () => {
|
|||
return <div ref={ref2}>{props.message}</div>
|
||||
})
|
||||
|
||||
render(<H message="Hello" />)
|
||||
expect(() => render(<H message="Hello" />)).not.toThrowError()
|
||||
|
||||
render(
|
||||
<Box.Component
|
||||
ref={ref}
|
||||
shape={box}
|
||||
bounds={Box.getBounds(box)}
|
||||
isEditing={false}
|
||||
isBinding={false}
|
||||
isHovered={false}
|
||||
isSelected={false}
|
||||
meta={meta}
|
||||
events={{} as any}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
render(
|
||||
<Box.Component
|
||||
ref={ref}
|
||||
shape={box}
|
||||
bounds={Box.getBounds(box)}
|
||||
isEditing={false}
|
||||
isBinding={false}
|
||||
isHovered={false}
|
||||
isSelected={false}
|
||||
meta={meta}
|
||||
events={{} as any}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -6,7 +6,13 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('binding', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
render(<Binding point={[0, 0]} type={'anchor'} />)
|
||||
expect(() =>
|
||||
render(
|
||||
<div>
|
||||
<Binding point={[0, 0]} type={'anchor'} />
|
||||
</div>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validate attributes rendered properly for anchor binding type', () => {
|
||||
render(<Binding point={[10, 20]} type={'anchor'} />)
|
||||
|
@ -17,7 +23,11 @@ describe('binding', () => {
|
|||
expect(use).toHaveAttribute('y', '20')
|
||||
})
|
||||
test('validate attributes rendered properly for center binding type', () => {
|
||||
render(<Binding point={[10, 20]} type={'center'} />)
|
||||
render(
|
||||
<div>
|
||||
<Binding point={[10, 20]} type={'center'} />
|
||||
</div>
|
||||
)
|
||||
|
||||
const circle = screen.getByLabelText('binding circle')
|
||||
expect(circle).toHaveAttribute('cx', '10')
|
||||
|
@ -25,7 +35,11 @@ describe('binding', () => {
|
|||
expect(circle).toHaveAttribute('r', '8')
|
||||
})
|
||||
test('validate no children should be rendered for pin binding type', () => {
|
||||
const { container } = render(<Binding point={[10, 20]} type={'pin'} />)
|
||||
const { container } = render(
|
||||
<div>
|
||||
<Binding point={[10, 20]} type={'pin'} />
|
||||
</div>
|
||||
)
|
||||
const group = container.querySelector('g')
|
||||
expect(group?.hasChildNodes()).toBe(false)
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { Container } from '~components/Container'
|
||||
import { SVGContainer } from '~components/SVGContainer'
|
||||
|
@ -10,7 +9,7 @@ import { EdgeHandle } from './EdgeHandle'
|
|||
import { LinkHandle } from './LinkHandle'
|
||||
import { RotateHandle } from './RotateHandle'
|
||||
|
||||
interface BoundsProps {
|
||||
export interface BoundsProps {
|
||||
zoom: number
|
||||
bounds: TLBounds
|
||||
rotation: number
|
||||
|
@ -24,7 +23,7 @@ interface BoundsProps {
|
|||
children?: React.ReactElement
|
||||
}
|
||||
|
||||
export const Bounds = observer<BoundsProps>(function Bounds({
|
||||
const _Bounds = function Bounds({
|
||||
zoom,
|
||||
bounds,
|
||||
viewportWidth,
|
||||
|
@ -137,4 +136,6 @@ export const Bounds = observer<BoundsProps>(function Bounds({
|
|||
</SVGContainer>
|
||||
</Container>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Bounds = React.memo(_Bounds)
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { Container } from '~components/Container'
|
||||
import { SVGContainer } from '~components/SVGContainer'
|
||||
import { useBoundsEvents } from '~hooks'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
interface BoundsBgProps {
|
||||
export interface BoundsBgProps {
|
||||
bounds: TLBounds
|
||||
rotation: number
|
||||
isHidden: boolean
|
||||
}
|
||||
|
||||
export const BoundsBg = observer<BoundsBgProps>(function BoundsBg({ bounds, rotation, isHidden }) {
|
||||
function _BoundsBg({ bounds, rotation, isHidden }: BoundsBgProps) {
|
||||
const events = useBoundsEvents()
|
||||
|
||||
return (
|
||||
|
@ -28,4 +27,6 @@ export const BoundsBg = observer<BoundsBgProps>(function BoundsBg({ bounds, rota
|
|||
</SVGContainer>
|
||||
</Container>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const BoundsBg = React.memo(_BoundsBg)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
|
@ -8,11 +7,7 @@ export interface CenterHandleProps {
|
|||
isHidden: boolean
|
||||
}
|
||||
|
||||
export const CenterHandle = observer<CenterHandleProps>(function CenterHandle({
|
||||
bounds,
|
||||
isLocked,
|
||||
isHidden,
|
||||
}) {
|
||||
function _CenterHandle({ bounds, isLocked, isHidden }: CenterHandleProps) {
|
||||
return (
|
||||
<rect
|
||||
className={['tl-bounds-center', isLocked ? 'tl-dashed' : ''].join(' ')}
|
||||
|
@ -25,4 +20,6 @@ export const CenterHandle = observer<CenterHandleProps>(function CenterHandle({
|
|||
aria-label="center handle"
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const CenterHandle = React.memo(_CenterHandle)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { useTLContext } from '~hooks'
|
||||
import type { TLBounds } from '~types'
|
||||
|
@ -21,12 +20,7 @@ export interface CloneButtonProps {
|
|||
side: 'top' | 'right' | 'bottom' | 'left' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
|
||||
}
|
||||
|
||||
export const CloneButton = observer<CloneButtonProps>(function CloneButton({
|
||||
bounds,
|
||||
side,
|
||||
targetSize,
|
||||
size,
|
||||
}: CloneButtonProps) {
|
||||
function _CloneButton({ bounds, side, targetSize, size }: CloneButtonProps) {
|
||||
const s = targetSize * 2
|
||||
const x = {
|
||||
left: -s,
|
||||
|
@ -78,4 +72,6 @@ export const CloneButton = observer<CloneButtonProps>(function CloneButton({
|
|||
</g>
|
||||
</g>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const CloneButton = React.memo(_CloneButton)
|
||||
|
|
|
@ -8,7 +8,7 @@ export interface CloneButtonsProps {
|
|||
size: number
|
||||
}
|
||||
|
||||
export function CloneButtons({ targetSize, size, bounds }: CloneButtonsProps) {
|
||||
function _CloneButtons({ targetSize, size, bounds }: CloneButtonsProps) {
|
||||
return (
|
||||
<>
|
||||
<CloneButton targetSize={targetSize} size={size} bounds={bounds} side="top" />
|
||||
|
@ -22,3 +22,5 @@ export function CloneButtons({ targetSize, size, bounds }: CloneButtonsProps) {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const CloneButtons = React.memo(_CloneButtons)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import { TLBounds, TLBoundsCorner } from '~types'
|
||||
|
@ -10,7 +9,7 @@ const cornerBgClassnames = {
|
|||
[TLBoundsCorner.BottomLeft]: 'tl-cursor-nesw',
|
||||
}
|
||||
|
||||
interface CornerHandleProps {
|
||||
export interface CornerHandleProps {
|
||||
size: number
|
||||
targetSize: number
|
||||
bounds: TLBounds
|
||||
|
@ -18,13 +17,7 @@ interface CornerHandleProps {
|
|||
isHidden?: boolean
|
||||
}
|
||||
|
||||
export const CornerHandle = observer(function CornerHandle({
|
||||
size,
|
||||
targetSize,
|
||||
isHidden,
|
||||
corner,
|
||||
bounds,
|
||||
}: CornerHandleProps) {
|
||||
function _CornerHandle({ size, targetSize, isHidden, corner, bounds }: CornerHandleProps) {
|
||||
const events = useBoundsHandleEvents(corner)
|
||||
|
||||
const isTop = corner === TLBoundsCorner.TopLeft || corner === TLBoundsCorner.TopRight
|
||||
|
@ -53,4 +46,6 @@ export const CornerHandle = observer(function CornerHandle({
|
|||
/>
|
||||
</g>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const CornerHandle = React.memo(_CornerHandle)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import { TLBounds, TLBoundsEdge } from '~types'
|
||||
|
@ -10,7 +9,7 @@ const edgeClassnames = {
|
|||
[TLBoundsEdge.Left]: 'tl-cursor-ew',
|
||||
}
|
||||
|
||||
interface EdgeHandleProps {
|
||||
export interface EdgeHandleProps {
|
||||
targetSize: number
|
||||
size: number
|
||||
bounds: TLBounds
|
||||
|
@ -18,12 +17,7 @@ interface EdgeHandleProps {
|
|||
isHidden: boolean
|
||||
}
|
||||
|
||||
export const EdgeHandle = observer<EdgeHandleProps>(function EdgeHandle({
|
||||
size,
|
||||
isHidden,
|
||||
bounds,
|
||||
edge,
|
||||
}: EdgeHandleProps) {
|
||||
function _EdgeHandle({ size, isHidden, bounds, edge }: EdgeHandleProps) {
|
||||
const events = useBoundsHandleEvents(edge)
|
||||
|
||||
const isHorizontal = edge === TLBoundsEdge.Top || edge === TLBoundsEdge.Bottom
|
||||
|
@ -44,4 +38,6 @@ export const EdgeHandle = observer<EdgeHandleProps>(function EdgeHandle({
|
|||
{...events}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const EdgeHandle = React.memo(_EdgeHandle)
|
||||
|
|
|
@ -2,14 +2,14 @@ import * as React from 'react'
|
|||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
interface LinkHandleProps {
|
||||
export interface LinkHandleProps {
|
||||
size: number
|
||||
targetSize: number
|
||||
isHidden: boolean
|
||||
bounds: TLBounds
|
||||
}
|
||||
|
||||
export function LinkHandle({ size, bounds, isHidden }: LinkHandleProps) {
|
||||
function _LinkHandle({ size, bounds, isHidden }: LinkHandleProps) {
|
||||
const leftEvents = useBoundsHandleEvents('left')
|
||||
const centerEvents = useBoundsHandleEvents('center')
|
||||
const rightEvents = useBoundsHandleEvents('right')
|
||||
|
@ -51,3 +51,5 @@ export function LinkHandle({ size, bounds, isHidden }: LinkHandleProps) {
|
|||
</g>
|
||||
)
|
||||
}
|
||||
|
||||
export const LinkHandle = React.memo(_LinkHandle)
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { useBoundsHandleEvents } from '~hooks'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
interface RotateHandleProps {
|
||||
export interface RotateHandleProps {
|
||||
bounds: TLBounds
|
||||
size: number
|
||||
targetSize: number
|
||||
isHidden: boolean
|
||||
}
|
||||
|
||||
export const RotateHandle = observer<RotateHandleProps>(function RotateHandle({
|
||||
bounds,
|
||||
targetSize,
|
||||
size,
|
||||
isHidden,
|
||||
}) {
|
||||
function _RotateHandle({ bounds, targetSize, size, isHidden }: RotateHandleProps) {
|
||||
const events = useBoundsHandleEvents('rotate')
|
||||
|
||||
return (
|
||||
|
@ -39,4 +33,6 @@ export const RotateHandle = observer<RotateHandleProps>(function RotateHandle({
|
|||
/>
|
||||
</g>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const RotateHandle = React.memo(_RotateHandle)
|
||||
|
|
|
@ -5,20 +5,22 @@ import { Bounds } from '../Bounds'
|
|||
|
||||
describe('bounds', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<Bounds
|
||||
zoom={1}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
rotation={0}
|
||||
viewportWidth={1000}
|
||||
isLocked={false}
|
||||
isHidden={false}
|
||||
hideBindingHandles={false}
|
||||
hideCloneHandles={false}
|
||||
hideRotateHandle={false}
|
||||
hideResizeHandles={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<Bounds
|
||||
zoom={1}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
rotation={0}
|
||||
viewportWidth={1000}
|
||||
isLocked={false}
|
||||
isHidden={false}
|
||||
hideBindingHandles={false}
|
||||
hideCloneHandles={false}
|
||||
hideRotateHandle={false}
|
||||
hideResizeHandles={false}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validate all attributes of bounds commponent', () => {
|
||||
renderWithContext(
|
||||
|
|
|
@ -6,13 +6,17 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('BoundsBg', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
render(
|
||||
<BoundsBg
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
rotation={45}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
render(
|
||||
<div>
|
||||
<BoundsBg
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
rotation={45}
|
||||
isHidden={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validate attributes for a bounds bg', () => {
|
||||
render(
|
||||
|
|
|
@ -6,21 +6,27 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('CenterHandle', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
render(
|
||||
<CenterHandle
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isLocked={false}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
render(
|
||||
<div>
|
||||
<CenterHandle
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isLocked={false}
|
||||
isHidden={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validate attributes for a center handle', () => {
|
||||
render(
|
||||
<CenterHandle
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isLocked={false}
|
||||
isHidden={false}
|
||||
/>
|
||||
<div>
|
||||
<CenterHandle
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isLocked={false}
|
||||
isHidden={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
const centerHandle = screen.getByLabelText('center handle')
|
||||
expect(centerHandle).toHaveAttribute('height', '102')
|
||||
|
@ -31,11 +37,13 @@ describe('CenterHandle', () => {
|
|||
})
|
||||
test('validate attributes for a hidden center handle', () => {
|
||||
render(
|
||||
<CenterHandle
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isLocked={false}
|
||||
isHidden={true}
|
||||
/>
|
||||
<div>
|
||||
<CenterHandle
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isLocked={false}
|
||||
isHidden={true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
const centerHandle = screen.getByLabelText('center handle')
|
||||
expect(centerHandle).toHaveAttribute('height', '102')
|
||||
|
|
|
@ -7,14 +7,16 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('CloneButton', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<CloneButton
|
||||
size={10}
|
||||
targetSize={20}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
side="top"
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<CloneButton
|
||||
size={10}
|
||||
targetSize={20}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
side="top"
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validate attributes for clone button', () => {
|
||||
renderWithContext(
|
||||
|
|
|
@ -8,14 +8,16 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('CenterHandle', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<CornerHandle
|
||||
size={10}
|
||||
targetSize={20}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
corner={TLBoundsCorner.TopLeft}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<CornerHandle
|
||||
size={10}
|
||||
targetSize={20}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
corner={TLBoundsCorner.TopLeft}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('top left corner > validate attributes', () => {
|
||||
renderWithContext(
|
||||
|
|
|
@ -8,15 +8,17 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('EdgeHandle', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<EdgeHandle
|
||||
targetSize={20}
|
||||
size={10}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
edge={TLBoundsEdge.Top}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<EdgeHandle
|
||||
targetSize={20}
|
||||
size={10}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
edge={TLBoundsEdge.Top}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('top edge > validate attributes', () => {
|
||||
renderWithContext(
|
||||
|
|
|
@ -7,14 +7,16 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('LinkHandle', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<LinkHandle
|
||||
targetSize={20}
|
||||
size={10}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<LinkHandle
|
||||
targetSize={20}
|
||||
size={10}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validate attributes for link handle component', () => {
|
||||
renderWithContext(
|
||||
|
|
|
@ -7,14 +7,16 @@ jest.spyOn(console, 'error').mockImplementation(() => void null)
|
|||
|
||||
describe('RotateHandle', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<RotateHandle
|
||||
targetSize={20}
|
||||
size={10}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<RotateHandle
|
||||
targetSize={20}
|
||||
size={10}
|
||||
bounds={{ minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 }}
|
||||
isHidden={false}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
test('validates all attributes of rotate handle', () => {
|
||||
renderWithContext(
|
||||
|
|
|
@ -5,20 +5,22 @@ import { Brush } from './Brush'
|
|||
|
||||
describe('brush', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithSvg(
|
||||
<Brush
|
||||
zoom={1}
|
||||
dashed={false}
|
||||
brush={{
|
||||
minX: 0,
|
||||
maxX: 100,
|
||||
minY: 0,
|
||||
maxY: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithSvg(
|
||||
<Brush
|
||||
zoom={1}
|
||||
dashed={false}
|
||||
brush={{
|
||||
minX: 0,
|
||||
maxX: 100,
|
||||
minY: 0,
|
||||
maxY: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { SVGContainer } from '~components'
|
||||
import { Container } from '~components/Container'
|
||||
import type { TLBounds } from '~types'
|
||||
import Utils from '~utils'
|
||||
|
||||
export const Brush = observer<{
|
||||
export interface BrushProps {
|
||||
brush: TLBounds
|
||||
zoom: number
|
||||
dashed: boolean | null | undefined
|
||||
}>(function Brush({ brush, zoom, dashed }) {
|
||||
}
|
||||
|
||||
function _Brush({ brush, zoom, dashed }: BrushProps) {
|
||||
return (
|
||||
<Container bounds={brush} rotation={0}>
|
||||
<SVGContainer>
|
||||
|
@ -45,7 +46,9 @@ export const Brush = observer<{
|
|||
</SVGContainer>
|
||||
</Container>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Brush = React.memo(_Brush)
|
||||
|
||||
interface PerfectDashLineProps {
|
||||
x1: number
|
||||
|
|
|
@ -4,24 +4,26 @@ import { Canvas } from './Canvas'
|
|||
|
||||
describe('page', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<Canvas
|
||||
page={mockDocument.page}
|
||||
pageState={mockDocument.pageState}
|
||||
hideBounds={false}
|
||||
hideGrid={false}
|
||||
hideIndicators={false}
|
||||
hideHandles={false}
|
||||
hideBindingHandles={false}
|
||||
hideResizeHandles={false}
|
||||
hideCloneHandles={false}
|
||||
hideRotateHandle={false}
|
||||
showDashedBrush={false}
|
||||
onBoundsChange={() => {
|
||||
// noop
|
||||
}}
|
||||
assets={{}}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<Canvas
|
||||
page={mockDocument.page}
|
||||
pageState={mockDocument.pageState}
|
||||
hideBounds={false}
|
||||
hideGrid={false}
|
||||
hideIndicators={false}
|
||||
hideHandles={false}
|
||||
hideBindingHandles={false}
|
||||
hideResizeHandles={false}
|
||||
hideCloneHandles={false}
|
||||
hideRotateHandle={false}
|
||||
showDashedBrush={false}
|
||||
onBoundsChange={() => {
|
||||
// noop
|
||||
}}
|
||||
assets={{}}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { Brush } from '~components/Brush'
|
||||
import { EraseLine } from '~components/EraseLine'
|
||||
|
@ -30,7 +29,7 @@ import type {
|
|||
TLUsers,
|
||||
} from '~types'
|
||||
|
||||
interface CanvasProps<T extends TLShape, M extends Record<string, unknown>> {
|
||||
export interface CanvasProps<T extends TLShape, M extends Record<string, unknown>> {
|
||||
page: TLPage<T, TLBinding>
|
||||
pageState: TLPageState
|
||||
assets: TLAssets
|
||||
|
@ -55,10 +54,7 @@ interface CanvasProps<T extends TLShape, M extends Record<string, unknown>> {
|
|||
onBoundsChange: (bounds: TLBounds) => void
|
||||
}
|
||||
|
||||
export const Canvas = observer(function _Canvas<
|
||||
T extends TLShape,
|
||||
M extends Record<string, unknown>
|
||||
>({
|
||||
function _Canvas<T extends TLShape, M extends Record<string, unknown>>({
|
||||
id,
|
||||
page,
|
||||
pageState,
|
||||
|
@ -141,4 +137,6 @@ export const Canvas = observer(function _Canvas<
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Canvas = React.memo(_Canvas)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import type { HTMLProps } from 'react'
|
||||
import * as React from 'react'
|
||||
import type { HTMLProps } from 'react'
|
||||
import { usePosition } from '~hooks'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
interface ContainerProps extends HTMLProps<HTMLDivElement> {
|
||||
export interface ContainerProps extends HTMLProps<HTMLDivElement> {
|
||||
id?: string
|
||||
bounds: TLBounds
|
||||
rotation?: number
|
||||
|
@ -13,7 +12,7 @@ interface ContainerProps extends HTMLProps<HTMLDivElement> {
|
|||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const Container = observer<ContainerProps>(function Container({
|
||||
function _Container({
|
||||
id,
|
||||
bounds,
|
||||
rotation = 0,
|
||||
|
@ -21,7 +20,7 @@ export const Container = observer<ContainerProps>(function Container({
|
|||
isSelected = false,
|
||||
children,
|
||||
...props
|
||||
}) {
|
||||
}: ContainerProps) {
|
||||
const rPositioned = usePosition(bounds, rotation)
|
||||
|
||||
return (
|
||||
|
@ -38,4 +37,6 @@ export const Container = observer<ContainerProps>(function Container({
|
|||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Container = React.memo(_Container)
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import * as React from 'react'
|
||||
import Utils from '~utils'
|
||||
|
||||
export interface UiEraseLintProps {
|
||||
export interface UiEraseLineProps {
|
||||
points: number[][]
|
||||
zoom: number
|
||||
}
|
||||
|
||||
export type UiEraseLineComponent = (props: UiEraseLintProps) => any | null
|
||||
export type UiEraseLineComponent = (props: UiEraseLineProps) => any | null
|
||||
|
||||
export const EraseLine = observer(function EraserLine({ points, zoom }: UiEraseLintProps) {
|
||||
function _EraseLine({ points, zoom }: UiEraseLineProps) {
|
||||
if (points.length === 0) return null
|
||||
|
||||
const d = Utils.getSvgPathFromStroke(
|
||||
|
@ -18,4 +17,6 @@ export const EraseLine = observer(function EraserLine({ points, zoom }: UiEraseL
|
|||
)
|
||||
|
||||
return <path d={d} className="tl-erase-line" />
|
||||
})
|
||||
}
|
||||
|
||||
export const EraseLine = React.memo(_EraseLine)
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
import { Observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
|
||||
interface HTMLContainerProps extends React.HTMLProps<HTMLDivElement> {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const HTMLContainer = React.forwardRef<HTMLDivElement, HTMLContainerProps>(
|
||||
function HTMLContainer({ children, className = '', ...rest }, ref) {
|
||||
export const HTMLContainer = React.memo(
|
||||
React.forwardRef<HTMLDivElement, HTMLContainerProps>(function HTMLContainer(
|
||||
{ children, className = '', ...rest },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<Observer>
|
||||
{() => (
|
||||
<div ref={ref} className={`tl-positioned-div ${className}`} draggable={false} {...rest}>
|
||||
<div className="tl-inner-div">{children}</div>
|
||||
</div>
|
||||
)}
|
||||
</Observer>
|
||||
<div ref={ref} className={`tl-positioned-div ${className}`} draggable={false} {...rest}>
|
||||
<div className="tl-inner-div">{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Handle } from './Handle'
|
|||
|
||||
describe('handle', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(<Handle id="123" point={[100, 200]} />)
|
||||
expect(() => renderWithContext(<Handle id="123" point={[100, 200]} />)).not.toThrowError()
|
||||
})
|
||||
test('validate attributes for handle component', () => {
|
||||
renderWithContext(<Handle id="123" point={[100, 200]} />)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { Container } from '~components/Container'
|
||||
import { SVGContainer } from '~components/SVGContainer'
|
||||
|
@ -10,7 +9,7 @@ interface HandleProps {
|
|||
point: number[]
|
||||
}
|
||||
|
||||
export const Handle = observer(function Handle({ id, point }: HandleProps) {
|
||||
function _Handle({ id, point }: HandleProps) {
|
||||
const events = useHandleEvents(id)
|
||||
|
||||
return (
|
||||
|
@ -35,4 +34,6 @@ export const Handle = observer(function Handle({ id, point }: HandleProps) {
|
|||
</SVGContainer>
|
||||
</Container>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Handle = React.memo(_Handle)
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Handles } from './Handles'
|
|||
|
||||
describe('handles', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(<Handles shape={boxShape} zoom={1} />)
|
||||
expect(() => renderWithContext(<Handles shape={boxShape} zoom={1} />)).not.toThrowError()
|
||||
})
|
||||
test('validate attributes for handles component', () => {
|
||||
const boxShapeWithHandles = {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Vec } from '@tldraw/vec'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLHandle, TLShape } from '~types'
|
||||
import { Handle } from './Handle'
|
||||
|
@ -9,7 +8,7 @@ interface HandlesProps {
|
|||
zoom: number
|
||||
}
|
||||
|
||||
export const Handles = observer(function Handles({ shape, zoom }: HandlesProps) {
|
||||
function _Handles({ shape, zoom }: HandlesProps) {
|
||||
if (shape.handles === undefined) {
|
||||
return null
|
||||
}
|
||||
|
@ -40,4 +39,6 @@ export const Handles = observer(function Handles({ shape, zoom }: HandlesProps)
|
|||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Handles = React.memo(_Handles)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
|
||||
type MyProps = {
|
||||
export type OverlayProps = {
|
||||
camera: { point: number[]; zoom: number }
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const Overlay = observer<MyProps>(function Overlay({ camera: { zoom, point }, children }) {
|
||||
function _Overlay({ camera: { zoom, point }, children }: OverlayProps) {
|
||||
const l = 2.5 / zoom
|
||||
return (
|
||||
<svg className="tl-overlay">
|
||||
|
@ -21,4 +20,6 @@ export const Overlay = observer<MyProps>(function Overlay({ camera: { zoom, poin
|
|||
<g transform={`scale(${zoom}) translate(${point})`}>{children}</g>
|
||||
</svg>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Overlay = React.memo(_Overlay)
|
||||
|
|
|
@ -4,19 +4,21 @@ import { Page } from './Page'
|
|||
|
||||
describe('page', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<Page
|
||||
page={mockDocument.page}
|
||||
pageState={mockDocument.pageState}
|
||||
assets={{}}
|
||||
hideBounds={false}
|
||||
hideIndicators={false}
|
||||
hideHandles={false}
|
||||
hideBindingHandles={false}
|
||||
hideCloneHandles={false}
|
||||
hideRotateHandle={false}
|
||||
hideResizeHandles={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<Page
|
||||
page={mockDocument.page}
|
||||
pageState={mockDocument.pageState}
|
||||
assets={{}}
|
||||
hideBounds={false}
|
||||
hideIndicators={false}
|
||||
hideHandles={false}
|
||||
hideBindingHandles={false}
|
||||
hideCloneHandles={false}
|
||||
hideRotateHandle={false}
|
||||
hideResizeHandles={false}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLShapeUtil } from '~TLShapeUtil'
|
||||
import { Bounds } from '~components/Bounds'
|
||||
|
@ -9,7 +8,7 @@ import { ShapeIndicator } from '~components/ShapeIndicator'
|
|||
import { useSelection, useShapeTree, useTLContext } from '~hooks'
|
||||
import type { TLAssets, TLBinding, TLPage, TLPageState, TLShape } from '~types'
|
||||
|
||||
interface PageProps<T extends TLShape, M extends Record<string, unknown>> {
|
||||
export interface PageProps<T extends TLShape, M extends Record<string, unknown>> {
|
||||
page: TLPage<T, TLBinding>
|
||||
pageState: TLPageState
|
||||
assets: TLAssets
|
||||
|
@ -26,7 +25,7 @@ interface PageProps<T extends TLShape, M extends Record<string, unknown>> {
|
|||
/**
|
||||
* The Page component renders the current page.
|
||||
*/
|
||||
export const Page = observer(function _Page<T extends TLShape, M extends Record<string, unknown>>({
|
||||
function _Page<T extends TLShape, M extends Record<string, unknown>>({
|
||||
page,
|
||||
pageState,
|
||||
assets,
|
||||
|
@ -112,4 +111,6 @@ export const Page = observer(function _Page<T extends TLShape, M extends Record<
|
|||
{!hideHandles && shapeWithHandles && <Handles shape={shapeWithHandles} zoom={zoom} />}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Page = React.memo(_Page)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { act, render } from '@testing-library/react'
|
||||
import Vec from '@tldraw/vec'
|
||||
import { action, makeAutoObservable } from 'mobx'
|
||||
import * as React from 'react'
|
||||
import type { BoxShape } from '~TLShapeUtil/TLShapeUtil.spec'
|
||||
import { mockDocument, mockUtils } from '~test'
|
||||
|
@ -10,118 +9,120 @@ import { Renderer } from './Renderer'
|
|||
|
||||
describe('renderer', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
render(
|
||||
<Renderer
|
||||
shapeUtils={mockUtils}
|
||||
page={mockDocument.page}
|
||||
pageState={mockDocument.pageState}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
render(
|
||||
<Renderer
|
||||
shapeUtils={mockUtils as any}
|
||||
page={mockDocument.page}
|
||||
pageState={mockDocument.pageState}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
||||
describe('When passing observables', () => {
|
||||
it('updates when the observables change', () => {
|
||||
class PageState implements TLPageState {
|
||||
id
|
||||
selectedIds
|
||||
camera
|
||||
brush?: TLBounds
|
||||
pointedId?: string
|
||||
hoveredId?: string
|
||||
editingId?: string
|
||||
bindingId?: string
|
||||
// describe('When passing observables', () => {
|
||||
// it('updates when the observables change', () => {
|
||||
// class PageState implements TLPageState {
|
||||
// id
|
||||
// selectedIds
|
||||
// camera
|
||||
// brush?: TLBounds
|
||||
// pointedId?: string
|
||||
// hoveredId?: string
|
||||
// editingId?: string
|
||||
// bindingId?: string
|
||||
|
||||
constructor(opts = {} as TLPageState) {
|
||||
const {
|
||||
id = Utils.uniqueId(),
|
||||
selectedIds = [],
|
||||
camera = {
|
||||
point: [0, 0],
|
||||
zoom: 1,
|
||||
},
|
||||
} = opts
|
||||
this.id = id
|
||||
this.camera = camera
|
||||
this.selectedIds = selectedIds
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
// constructor(opts = {} as TLPageState) {
|
||||
// const {
|
||||
// id = Utils.uniqueId(),
|
||||
// selectedIds = [],
|
||||
// camera = {
|
||||
// point: [0, 0],
|
||||
// zoom: 1,
|
||||
// },
|
||||
// } = opts
|
||||
// this.id = id
|
||||
// this.camera = camera
|
||||
// this.selectedIds = selectedIds
|
||||
// makeAutoObservable(this)
|
||||
// }
|
||||
|
||||
@action pan = (point: number[]) => {
|
||||
this.camera.point = Vec.add(this.camera.point, point)
|
||||
}
|
||||
}
|
||||
// @action pan = (point: number[]) => {
|
||||
// this.camera.point = Vec.add(this.camera.point, point)
|
||||
// }
|
||||
// }
|
||||
|
||||
class Page implements TLPage<BoxShape> {
|
||||
id = 'page1'
|
||||
shapes = {
|
||||
box1: {
|
||||
id: 'box1',
|
||||
type: 'box' as const,
|
||||
parentId: 'page1',
|
||||
name: 'Box',
|
||||
childIndex: 1,
|
||||
rotation: 0,
|
||||
point: [0, 0],
|
||||
size: [100, 100],
|
||||
},
|
||||
} as Record<string, BoxShape>
|
||||
bindings = {}
|
||||
// class Page implements TLPage<BoxShape> {
|
||||
// id = 'page1'
|
||||
// shapes = {
|
||||
// box1: {
|
||||
// id: 'box1',
|
||||
// type: 'box' as const,
|
||||
// parentId: 'page1',
|
||||
// name: 'Box',
|
||||
// childIndex: 1,
|
||||
// rotation: 0,
|
||||
// point: [0, 0],
|
||||
// size: [100, 100],
|
||||
// },
|
||||
// } as Record<string, BoxShape>
|
||||
// bindings = {}
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
// constructor() {
|
||||
// makeAutoObservable(this)
|
||||
// }
|
||||
|
||||
@action moveShape = (id: string, point: number[]) => {
|
||||
const shape = this.shapes[id]
|
||||
shape.point = point
|
||||
}
|
||||
}
|
||||
// @action moveShape = (id: string, point: number[]) => {
|
||||
// const shape = this.shapes[id]
|
||||
// shape.point = point
|
||||
// }
|
||||
// }
|
||||
|
||||
const page = new Page()
|
||||
const pageState = new PageState()
|
||||
// const page = new Page()
|
||||
// const pageState = new PageState()
|
||||
|
||||
const wrapper = render(<Renderer shapeUtils={mockUtils} page={page} pageState={pageState} />)
|
||||
// const wrapper = render(<Renderer shapeUtils={mockUtils} page={page} pageState={pageState} />)
|
||||
|
||||
expect(wrapper.getByTestId('layer')).toHaveProperty(
|
||||
'style.transform',
|
||||
`scale(1) translateX(0px) translateY(0px)`
|
||||
)
|
||||
// expect(wrapper.getByTestId('layer')).toHaveProperty(
|
||||
// 'style.transform',
|
||||
// `scale(1) translateX(0px) translateY(0px)`
|
||||
// )
|
||||
|
||||
act(() => {
|
||||
// PageState
|
||||
pageState.pan([10, 10])
|
||||
})
|
||||
// act(() => {
|
||||
// // PageState
|
||||
// pageState.pan([10, 10])
|
||||
// })
|
||||
|
||||
expect(wrapper.getByTestId('layer')).toHaveProperty(
|
||||
'style.transform',
|
||||
`scale(1) translateX(10px) translateY(10px)`
|
||||
)
|
||||
// expect(wrapper.getByTestId('layer')).toHaveProperty(
|
||||
// 'style.transform',
|
||||
// `scale(1) translateX(10px) translateY(10px)`
|
||||
// )
|
||||
|
||||
// Page
|
||||
// // Page
|
||||
|
||||
expect(wrapper.getByTestId('container')).toHaveProperty(
|
||||
'style.transform',
|
||||
`
|
||||
translate(
|
||||
calc(0px - var(--tl-padding)),
|
||||
calc(0px - var(--tl-padding))
|
||||
)
|
||||
rotate(0rad)`
|
||||
)
|
||||
// expect(wrapper.getByTestId('container')).toHaveProperty(
|
||||
// 'style.transform',
|
||||
// `
|
||||
// translate(
|
||||
// calc(0px - var(--tl-padding)),
|
||||
// calc(0px - var(--tl-padding))
|
||||
// )
|
||||
// rotate(0rad)`
|
||||
// )
|
||||
|
||||
act(() => {
|
||||
page.moveShape('box1', [10, 10])
|
||||
})
|
||||
// act(() => {
|
||||
// page.moveShape('box1', [10, 10])
|
||||
// })
|
||||
|
||||
expect(wrapper.getByTestId('container')).toHaveProperty(
|
||||
'style.transform',
|
||||
`
|
||||
translate(
|
||||
calc(10px - var(--tl-padding)),
|
||||
calc(10px - var(--tl-padding))
|
||||
)
|
||||
rotate(0rad)`
|
||||
)
|
||||
})
|
||||
})
|
||||
// expect(wrapper.getByTestId('container')).toHaveProperty(
|
||||
// 'style.transform',
|
||||
// `
|
||||
// translate(
|
||||
// calc(10px - var(--tl-padding)),
|
||||
// calc(10px - var(--tl-padding))
|
||||
// )
|
||||
// rotate(0rad)`
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { Canvas } from '~/components/Canvas'
|
||||
import type { TLShapeUtilsMap } from '~TLShapeUtil'
|
||||
|
@ -18,11 +17,15 @@ import type {
|
|||
TLUsers,
|
||||
} from '~types'
|
||||
|
||||
export interface RendererProps<T extends TLShape, M = any> extends Partial<TLCallbacks<T>> {
|
||||
const EMPTY_OBJECT = {} as TLAssets
|
||||
|
||||
export type RendererProps<T extends TLShape, M = any> = Partial<TLCallbacks<T>> & {
|
||||
/**
|
||||
* An object containing instances of your shape classes.
|
||||
*/
|
||||
shapeUtils: TLShapeUtilsMap<T>
|
||||
shapeUtils: {
|
||||
[K in T['type']]: any
|
||||
}
|
||||
/**
|
||||
* The current page, containing shapes and bindings.
|
||||
*/
|
||||
|
@ -130,10 +133,7 @@ export interface RendererProps<T extends TLShape, M = any> extends Partial<TLCal
|
|||
* @param props
|
||||
* @returns
|
||||
*/
|
||||
export const Renderer = observer(function _Renderer<
|
||||
T extends TLShape,
|
||||
M extends Record<string, unknown>
|
||||
>({
|
||||
function _Renderer<T extends TLShape, M extends Record<string, unknown>>({
|
||||
id = 'tl',
|
||||
shapeUtils,
|
||||
page,
|
||||
|
@ -220,6 +220,6 @@ export const Renderer = observer(function _Renderer<
|
|||
/>
|
||||
</TLContext.Provider>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const EMPTY_OBJECT = {} as TLAssets
|
||||
export const Renderer = React.memo(_Renderer)
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
import { Observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
|
||||
interface SvgContainerProps extends React.SVGProps<SVGSVGElement> {
|
||||
export interface SvgContainerProps extends React.SVGProps<SVGSVGElement> {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const SVGContainer = React.forwardRef<SVGSVGElement, SvgContainerProps>(
|
||||
function SVGContainer({ id, className = '', children, ...rest }, ref) {
|
||||
export const SVGContainer = React.memo(
|
||||
React.forwardRef<SVGSVGElement, SvgContainerProps>(function SVGContainer(
|
||||
{ id, className = '', children, ...rest },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<Observer>
|
||||
{() => (
|
||||
<svg ref={ref} className={`tl-positioned-svg ${className}`} {...rest}>
|
||||
<g id={id} className="tl-centered-g">
|
||||
{children}
|
||||
</g>
|
||||
</svg>
|
||||
)}
|
||||
</Observer>
|
||||
<svg ref={ref} className={`tl-positioned-svg ${className}`} {...rest}>
|
||||
<g id={id} className="tl-centered-g">
|
||||
{children}
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLShapeUtil } from '~TLShapeUtil'
|
||||
import type { TLComponentProps, TLShape } from '~types'
|
||||
|
@ -9,12 +8,12 @@ interface RenderedShapeProps<T extends TLShape, E extends Element, M>
|
|||
utils: TLShapeUtil<T, E, M>
|
||||
}
|
||||
|
||||
const _RenderedShape = observer(function RenderedShape<T extends TLShape, E extends Element, M>(
|
||||
function _RenderedShape<T extends TLShape, E extends Element, M>(
|
||||
props: RenderedShapeProps<T, E, M>
|
||||
) {
|
||||
const ref = props.utils.getRef(props.shape)
|
||||
return <props.utils.Component ref={ref} {...props} />
|
||||
})
|
||||
}
|
||||
|
||||
export const RenderedShape = React.memo(_RenderedShape, (prev, next) => {
|
||||
// If these have changed, then definitely render
|
||||
|
|
|
@ -7,17 +7,19 @@ import { Shape } from './Shape'
|
|||
|
||||
describe('shape', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithContext(
|
||||
<Shape
|
||||
shape={boxShape}
|
||||
utils={new BoxUtil() as unknown as TLShapeUtil<TLShape>}
|
||||
isEditing={false}
|
||||
isBinding={false}
|
||||
isHovered={false}
|
||||
isSelected={false}
|
||||
isGhost={false}
|
||||
isChildOfSelected={false}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithContext(
|
||||
<Shape
|
||||
shape={boxShape}
|
||||
utils={new BoxUtil() as unknown as TLShapeUtil<TLShape>}
|
||||
isEditing={false}
|
||||
isBinding={false}
|
||||
isHovered={false}
|
||||
isSelected={false}
|
||||
isGhost={false}
|
||||
isChildOfSelected={false}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLShapeUtil } from '~TLShapeUtil'
|
||||
import { Container } from '~components/Container'
|
||||
|
@ -7,11 +6,11 @@ import { useTLContext } from '~hooks'
|
|||
import type { IShapeTreeNode, TLShape } from '~types'
|
||||
import { RenderedShape } from './RenderedShape'
|
||||
|
||||
interface ShapeProps<T extends TLShape, E extends Element, M> extends IShapeTreeNode<T, M> {
|
||||
export interface ShapeProps<T extends TLShape, E extends Element, M> extends IShapeTreeNode<T, M> {
|
||||
utils: TLShapeUtil<T, E, M>
|
||||
}
|
||||
|
||||
export const Shape = observer(function Shape<T extends TLShape, E extends Element, M>({
|
||||
function _Shape<T extends TLShape, E extends Element, M>({
|
||||
shape,
|
||||
utils,
|
||||
meta,
|
||||
|
@ -42,4 +41,6 @@ export const Shape = observer(function Shape<T extends TLShape, E extends Elemen
|
|||
/>
|
||||
</Container>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const Shape = React.memo(_Shape)
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLShapeUtilsMap } from '~TLShapeUtil'
|
||||
import type { IShapeTreeNode, TLShape } from '~types'
|
||||
import { Shape } from './Shape'
|
||||
|
||||
interface ShapeNodeProps<T extends TLShape> extends IShapeTreeNode<T> {
|
||||
utils: TLShapeUtilsMap<TLShape>
|
||||
export interface ShapeNodeProps<T extends TLShape> extends IShapeTreeNode<T> {
|
||||
utils: TLShapeUtilsMap<T>
|
||||
}
|
||||
|
||||
export const ShapeNode = observer(function ShapeNode<T extends TLShape>({
|
||||
function _ShapeNode<T extends TLShape>({
|
||||
shape,
|
||||
utils,
|
||||
meta,
|
||||
|
@ -17,11 +16,13 @@ export const ShapeNode = observer(function ShapeNode<T extends TLShape>({
|
|||
}: ShapeNodeProps<T>) {
|
||||
return (
|
||||
<>
|
||||
<Shape shape={shape} utils={utils[shape.type as T['type']]} meta={meta} {...rest} />
|
||||
<Shape shape={shape} utils={(utils as any)[shape.type]} meta={meta} {...rest} />
|
||||
{children &&
|
||||
children.map((childNode) => (
|
||||
<ShapeNode key={childNode.shape.id} utils={utils} {...childNode} />
|
||||
<ShapeNode key={childNode.shape.id} utils={utils as any} {...childNode} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const ShapeNode = React.memo(_ShapeNode)
|
||||
|
|
|
@ -5,14 +5,16 @@ import { ShapeIndicator } from './ShapeIndicator'
|
|||
|
||||
describe('shape indicator', () => {
|
||||
test('mounts component without crashing', () => {
|
||||
renderWithSvg(
|
||||
<ShapeIndicator
|
||||
shape={boxShape}
|
||||
isSelected={true}
|
||||
isHovered={false}
|
||||
isEditing={false}
|
||||
meta={undefined}
|
||||
/>
|
||||
)
|
||||
expect(() =>
|
||||
renderWithSvg(
|
||||
<ShapeIndicator
|
||||
shape={boxShape}
|
||||
isSelected={true}
|
||||
isHovered={false}
|
||||
isEditing={false}
|
||||
meta={undefined}
|
||||
/>
|
||||
)
|
||||
).not.toThrowError()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { usePosition, useTLContext } from '~hooks'
|
||||
import type { TLShape, TLUser } from '~types'
|
||||
|
||||
interface IndicatorProps<T extends TLShape, M = unknown> {
|
||||
export interface IndicatorProps<T extends TLShape, M = unknown> {
|
||||
shape: T
|
||||
meta: M extends unknown ? M : undefined
|
||||
isSelected?: boolean
|
||||
|
@ -12,7 +11,7 @@ interface IndicatorProps<T extends TLShape, M = unknown> {
|
|||
user?: TLUser<T>
|
||||
}
|
||||
|
||||
export const ShapeIndicator = observer(function ShapeIndicator<T extends TLShape, M>({
|
||||
function _ShapeIndicator<T extends TLShape, M>({
|
||||
isHovered = false,
|
||||
isSelected = false,
|
||||
isEditing = false,
|
||||
|
@ -51,4 +50,6 @@ export const ShapeIndicator = observer(function ShapeIndicator<T extends TLShape
|
|||
</svg>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const ShapeIndicator = React.memo(_ShapeIndicator)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import type { TLSnapLine } from '~types'
|
||||
import Utils from '~utils'
|
||||
|
||||
export const SnapLines = observer<{ snapLines: TLSnapLine[] }>(function SnapLines({ snapLines }) {
|
||||
function _SnapLines({ snapLines }: { snapLines: TLSnapLine[] }) {
|
||||
return (
|
||||
<>
|
||||
{snapLines.map((snapLine, i) => (
|
||||
|
@ -11,9 +10,9 @@ export const SnapLines = observer<{ snapLines: TLSnapLine[] }>(function SnapLine
|
|||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const SnapLine = observer<{ snapLine: TLSnapLine }>(function SnapLine({ snapLine }) {
|
||||
function _SnapLine({ snapLine }: { snapLine: TLSnapLine }) {
|
||||
const bounds = Utils.getBoundsFromPoints(snapLine)
|
||||
|
||||
return (
|
||||
|
@ -30,4 +29,7 @@ export const SnapLine = observer<{ snapLine: TLSnapLine }>(function SnapLine({ s
|
|||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const SnapLine = React.memo(_SnapLine)
|
||||
export const SnapLines = React.memo(_SnapLines)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { autorun } from 'mobx'
|
||||
import * as React from 'react'
|
||||
import type { TLPageState } from '~types'
|
||||
|
||||
|
@ -12,36 +11,34 @@ export function useCameraCss(
|
|||
const rPoint = React.useRef<number[]>()
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
return autorun(() => {
|
||||
const { zoom, point } = pageState.camera
|
||||
const { zoom, point } = pageState.camera
|
||||
|
||||
const didZoom = zoom !== rZoom.current
|
||||
const didPan = point !== rPoint.current
|
||||
const didZoom = zoom !== rZoom.current
|
||||
const didPan = point !== rPoint.current
|
||||
|
||||
rZoom.current = zoom
|
||||
rPoint.current = point
|
||||
rZoom.current = zoom
|
||||
rPoint.current = point
|
||||
|
||||
if (didZoom || didPan) {
|
||||
const layer = layerRef.current
|
||||
if (containerRef && 'current' in containerRef) {
|
||||
const container = containerRef.current
|
||||
if (didZoom || didPan) {
|
||||
const layer = layerRef.current
|
||||
if (containerRef && 'current' in containerRef) {
|
||||
const container = containerRef.current
|
||||
|
||||
// If we zoomed, set the CSS variable for the zoom
|
||||
if (didZoom) {
|
||||
if (container) {
|
||||
container.style.setProperty('--tl-zoom', zoom.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Either way, position the layer
|
||||
if (layer) {
|
||||
layer.style.setProperty(
|
||||
'transform',
|
||||
`scale(${zoom}) translateX(${point[0]}px) translateY(${point[1]}px)`
|
||||
)
|
||||
// If we zoomed, set the CSS variable for the zoom
|
||||
if (didZoom) {
|
||||
if (container) {
|
||||
container.style.setProperty('--tl-zoom', zoom.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Either way, position the layer
|
||||
if (layer) {
|
||||
layer.style.setProperty(
|
||||
'transform',
|
||||
`scale(${zoom}) translateX(${point[0]}px) translateY(${point[1]}px)`
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}, [pageState])
|
||||
}
|
||||
}, [pageState.camera.zoom, pageState.camera.point])
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { autorun } from 'mobx'
|
||||
import * as React from 'react'
|
||||
import type { TLBounds } from '~types'
|
||||
|
||||
|
@ -7,24 +6,19 @@ export function usePosition(bounds: TLBounds, rotation = 0) {
|
|||
|
||||
// Update the transform
|
||||
React.useLayoutEffect(() => {
|
||||
return autorun(() => {
|
||||
const elm = rBounds.current!
|
||||
const transform = `
|
||||
const elm = rBounds.current!
|
||||
const transform = `
|
||||
translate(
|
||||
calc(${bounds.minX}px - var(--tl-padding)),
|
||||
calc(${bounds.minY}px - var(--tl-padding))
|
||||
)
|
||||
rotate(${rotation + (bounds.rotation || 0)}rad)`
|
||||
elm.style.setProperty('transform', transform)
|
||||
elm.style.setProperty(
|
||||
'width',
|
||||
`calc(${Math.floor(bounds.width)}px + (var(--tl-padding) * 2))`
|
||||
)
|
||||
elm.style.setProperty(
|
||||
'height',
|
||||
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
|
||||
)
|
||||
})
|
||||
elm.style.setProperty('transform', transform)
|
||||
elm.style.setProperty('width', `calc(${Math.floor(bounds.width)}px + (var(--tl-padding) * 2))`)
|
||||
elm.style.setProperty(
|
||||
'height',
|
||||
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
|
||||
)
|
||||
}, [bounds, rotation])
|
||||
|
||||
return rBounds
|
||||
|
|
|
@ -3,5 +3,9 @@ import * as React from 'react'
|
|||
import { ContextWrapper } from './ContextWrapper'
|
||||
|
||||
export const renderWithContext = (children: React.ReactNode) => {
|
||||
return render(<ContextWrapper>{children}</ContextWrapper>)
|
||||
return render(
|
||||
<div>
|
||||
<ContextWrapper>{children}</ContextWrapper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ export type TLPinchEventHandler = (
|
|||
| PointerEventInit
|
||||
) => void
|
||||
|
||||
export type TLShapeChangeHandler<T, K = any> = (
|
||||
export type TLShapeChangeHandler<T extends TLShape, K = any> = (
|
||||
shape: { id: string } & Partial<T>,
|
||||
info?: K
|
||||
) => void
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
"@types/react-dom": "^18.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||
"@typescript-eslint/parser": "^5.10.2",
|
||||
"mobx": "^6.3.8",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"typescript": "^4.7.3"
|
||||
|
|
|
@ -598,41 +598,6 @@ const OneOff = React.memo(function OneOff({
|
|||
return null
|
||||
})
|
||||
|
||||
const Wrapper = styled('div', {
|
||||
variants: {
|
||||
isForcingPanning: {
|
||||
true: {},
|
||||
false: {},
|
||||
},
|
||||
isPointerDown: {
|
||||
true: {},
|
||||
false: {},
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
isForcingPanning: true,
|
||||
isPointerDown: false,
|
||||
css: {
|
||||
cursor: 'grab',
|
||||
},
|
||||
},
|
||||
{
|
||||
isForcingPanning: false,
|
||||
css: {
|
||||
cursor: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
isPointerDown: true,
|
||||
isForcingPanning: true,
|
||||
css: {
|
||||
cursor: 'grabbing',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const StyledLayout = styled('div', {
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
|
|
|
@ -42,7 +42,7 @@ interface ContextMenuProps {
|
|||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export const ContextMenu = ({ onBlur, children }: ContextMenuProps) => {
|
||||
export const _ContextMenu = ({ onBlur, children }: ContextMenuProps) => {
|
||||
const container = useContainer()
|
||||
|
||||
return (
|
||||
|
@ -524,3 +524,5 @@ export const CMSubTriggerButton = ({ id, ...rest }: RowButtonProps) => {
|
|||
</RadixContextMenu.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
export const ContextMenu = React.memo(_ContextMenu)
|
||||
|
|
|
@ -19,7 +19,7 @@ interface TopPanelProps {
|
|||
showMultiplayerMenu: boolean
|
||||
}
|
||||
|
||||
export function TopPanel({
|
||||
export function _TopPanel({
|
||||
readOnly,
|
||||
showPages,
|
||||
showMenu,
|
||||
|
@ -91,3 +91,5 @@ const ReadOnlyLabel = styled('div', {
|
|||
paddingRight: '$1',
|
||||
userSelect: 'none',
|
||||
})
|
||||
|
||||
export const TopPanel = React.memo(_TopPanel)
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
TLPageState,
|
||||
TLPinchEventHandler,
|
||||
TLPointerEventHandler,
|
||||
TLShape,
|
||||
TLShapeCloneHandler,
|
||||
TLWheelEventHandler,
|
||||
Utils,
|
||||
|
@ -3993,7 +3994,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
this.currentTool.onReleaseHandle?.(info, e)
|
||||
}
|
||||
|
||||
onShapeChange = (shape: { id: string } & Partial<TDShape>) => {
|
||||
onShapeChange = (shape: { id: string } & Partial<TLShape>) => {
|
||||
const pageShapes = this.document.pages[this.currentPageId].shapes
|
||||
const shapeToUpdate = { ...(pageShapes[shape.id] as any), ...shape }
|
||||
const patch = Commands.updateShapes(this, [shapeToUpdate], this.currentPageId).after
|
||||
|
|
19
yarn.lock
19
yarn.lock
|
@ -2663,6 +2663,15 @@
|
|||
"@testing-library/dom" "^8.5.0"
|
||||
"@types/react-dom" "^18.0.0"
|
||||
|
||||
"@testing-library/react@^13.4.0":
|
||||
version "13.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966"
|
||||
integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@testing-library/dom" "^8.5.0"
|
||||
"@types/react-dom" "^18.0.0"
|
||||
|
||||
"@tldraw/lfg@latest":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@tldraw/lfg/-/lfg-1.1.3.tgz#23b36664dc5057902f7f2ad0b68f5cfd5b9cc133"
|
||||
|
@ -7292,16 +7301,6 @@ mkdirp@^0.5.5:
|
|||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
mobx-react-lite@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.2.3.tgz#83d2b32ebf4383cd0dc0d397acbf53a8e9c66765"
|
||||
integrity sha512-7exWp1FV0M9dP08H9PIeHlJqDw4IdkQVRMfLYaZFMmlbzSS6ZU6p/kx392KN+rVf81hH3IQYewvRGQ70oiwmbw==
|
||||
|
||||
mobx@^6.3.13, mobx@^6.3.8:
|
||||
version "6.3.13"
|
||||
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.13.tgz#93e56a57ee72369f850cf3d6398fd36ee8ef062e"
|
||||
integrity sha512-zDDKDhYUk9QCHQUdLG+wb4Jv/nXutSLt/P8kkwHyjdbrJO4OZS6QTEsrOnrKM39puqXSrJZHdB6+yRys2NBFFA==
|
||||
|
||||
mocha@^9.1.1:
|
||||
version "9.2.2"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9"
|
||||
|
|
Loading…
Reference in a new issue