Adjusts small example, makes inputs unique to each instance

This commit is contained in:
Steve Ruiz 2021-09-08 17:18:43 +01:00
parent 2653f396bf
commit 8154ed5a2a
25 changed files with 190 additions and 81 deletions

View file

@ -48,6 +48,7 @@
"lerna": "^3.15.0", "lerna": "^3.15.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"resize-observer-polyfill": "^1.5.1",
"ts-jest": "^27.0.5", "ts-jest": "^27.0.5",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"typedoc": "^0.21.9", "typedoc": "^0.21.9",
@ -114,4 +115,4 @@
"\\+(.*)": "<rootDir>/packages/core/src/$1" "\\+(.*)": "<rootDir>/packages/core/src/$1"
} }
} }
} }

View file

@ -12,6 +12,7 @@ import { ErrorBoundary } from '+components/error-boundary'
import { Brush } from '+components/brush' import { Brush } from '+components/brush'
import { Defs } from '+components/defs' import { Defs } from '+components/defs'
import { Page } from '+components/page' import { Page } from '+components/page'
import { useResizeObserver } from '+hooks/useResizeObserver'
function resetError() { function resetError() {
void null void null
@ -35,10 +36,13 @@ export function Canvas<T extends TLShape>({
hideIndicators = false, hideIndicators = false,
}: CanvasProps<T>): JSX.Element { }: CanvasProps<T>): JSX.Element {
const rCanvas = React.useRef<SVGSVGElement>(null) const rCanvas = React.useRef<SVGSVGElement>(null)
const rContainer = React.useRef<HTMLDivElement>(null)
const rGroup = useCameraCss(pageState) const rGroup = useCameraCss(pageState)
useZoomEvents() useResizeObserver(rCanvas)
useZoomEvents(rCanvas)
useSafariFocusOutFix() useSafariFocusOutFix()
@ -47,7 +51,7 @@ export function Canvas<T extends TLShape>({
const events = useCanvasEvents() const events = useCanvasEvents()
return ( return (
<div className="tl-container"> <div className="tl-container" ref={rContainer}>
<svg id="canvas" className="tl-canvas" ref={rCanvas} {...events}> <svg id="canvas" className="tl-canvas" ref={rCanvas} {...events}>
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={resetError}> <ErrorBoundary FallbackComponent={ErrorFallback} onReset={resetError}>
<Defs zoom={pageState.camera.zoom} /> <Defs zoom={pageState.camera.zoom} />

View file

@ -10,6 +10,7 @@ import type {
TLBinding, TLBinding,
} from '../../types' } from '../../types'
import { Canvas } from '../canvas' import { Canvas } from '../canvas'
import { Inputs } from '../../inputs'
import { useTLTheme, TLContext, TLContextType } from '../../hooks' import { useTLTheme, TLContext, TLContextType } from '../../hooks'
export interface RendererProps<T extends TLShape, M extends Record<string, unknown>> export interface RendererProps<T extends TLShape, M extends Record<string, unknown>>
@ -87,6 +88,7 @@ export function Renderer<T extends TLShape, M extends Record<string, unknown>>({
shapeUtils, shapeUtils,
rScreenBounds, rScreenBounds,
rPageState, rPageState,
inputs: new Inputs(),
})) }))
return ( return (

View file

@ -1,9 +1,8 @@
import * as React from 'react' import * as React from 'react'
import { inputs } from '+inputs'
import { useTLContext } from './useTLContext' import { useTLContext } from './useTLContext'
export function useBoundsEvents() { export function useBoundsEvents() {
const { callbacks } = useTLContext() const { callbacks, inputs } = useTLContext()
const onPointerDown = React.useCallback( const onPointerDown = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
@ -15,7 +14,7 @@ export function useBoundsEvents() {
callbacks.onPointBounds?.(info, e) callbacks.onPointBounds?.(info, e)
callbacks.onPointerDown?.(info, e) callbacks.onPointerDown?.(info, e)
}, },
[callbacks] [callbacks, inputs]
) )
const onPointerUp = React.useCallback( const onPointerUp = React.useCallback(
@ -36,7 +35,7 @@ export function useBoundsEvents() {
callbacks.onReleaseBounds?.(info, e) callbacks.onReleaseBounds?.(info, e)
callbacks.onPointerUp?.(info, e) callbacks.onPointerUp?.(info, e)
}, },
[callbacks] [callbacks, inputs]
) )
const onPointerMove = React.useCallback( const onPointerMove = React.useCallback(
@ -49,21 +48,21 @@ export function useBoundsEvents() {
const info = inputs.pointerMove(e, 'bounds') const info = inputs.pointerMove(e, 'bounds')
callbacks.onPointerMove?.(info, e) callbacks.onPointerMove?.(info, e)
}, },
[callbacks] [callbacks, inputs]
) )
const onPointerEnter = React.useCallback( const onPointerEnter = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
callbacks.onHoverBounds?.(inputs.pointerEnter(e, 'bounds'), e) callbacks.onHoverBounds?.(inputs.pointerEnter(e, 'bounds'), e)
}, },
[callbacks] [callbacks, inputs]
) )
const onPointerLeave = React.useCallback( const onPointerLeave = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
callbacks.onUnhoverBounds?.(inputs.pointerEnter(e, 'bounds'), e) callbacks.onUnhoverBounds?.(inputs.pointerEnter(e, 'bounds'), e)
}, },
[callbacks] [callbacks, inputs]
) )
const onTouchStart = React.useCallback((e: React.TouchEvent) => { const onTouchStart = React.useCallback((e: React.TouchEvent) => {

View file

@ -1,10 +1,9 @@
import * as React from 'react' import * as React from 'react'
import { inputs } from '+inputs'
import type { TLBoundsEdge, TLBoundsCorner } from '+types' import type { TLBoundsEdge, TLBoundsCorner } from '+types'
import { useTLContext } from './useTLContext' import { useTLContext } from './useTLContext'
export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotate') { export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotate') {
const { callbacks } = useTLContext() const { callbacks, inputs } = useTLContext()
const onPointerDown = React.useCallback( const onPointerDown = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
@ -16,7 +15,7 @@ export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotat
callbacks.onPointBoundsHandle?.(info, e) callbacks.onPointBoundsHandle?.(info, e)
callbacks.onPointerDown?.(info, e) callbacks.onPointerDown?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerUp = React.useCallback( const onPointerUp = React.useCallback(
@ -37,7 +36,7 @@ export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotat
callbacks.onReleaseBoundsHandle?.(info, e) callbacks.onReleaseBoundsHandle?.(info, e)
callbacks.onPointerUp?.(info, e) callbacks.onPointerUp?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerMove = React.useCallback( const onPointerMove = React.useCallback(
@ -48,21 +47,21 @@ export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotat
const info = inputs.pointerMove(e, id) const info = inputs.pointerMove(e, id)
callbacks.onPointerMove?.(info, e) callbacks.onPointerMove?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerEnter = React.useCallback( const onPointerEnter = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
callbacks.onHoverBoundsHandle?.(inputs.pointerEnter(e, id), e) callbacks.onHoverBoundsHandle?.(inputs.pointerEnter(e, id), e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerLeave = React.useCallback( const onPointerLeave = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
callbacks.onUnhoverBoundsHandle?.(inputs.pointerEnter(e, id), e) callbacks.onUnhoverBoundsHandle?.(inputs.pointerEnter(e, id), e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onTouchStart = React.useCallback((e: React.TouchEvent) => { const onTouchStart = React.useCallback((e: React.TouchEvent) => {

View file

@ -1,9 +1,8 @@
import * as React from 'react' import * as React from 'react'
import { useTLContext } from './useTLContext' import { useTLContext } from './useTLContext'
import { inputs } from '+inputs'
export function useCanvasEvents() { export function useCanvasEvents() {
const { callbacks } = useTLContext() const { callbacks, inputs } = useTLContext()
const onPointerDown = React.useCallback( const onPointerDown = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
@ -16,7 +15,7 @@ export function useCanvasEvents() {
callbacks.onPointerDown?.(info, e) callbacks.onPointerDown?.(info, e)
} }
}, },
[callbacks] [callbacks, inputs]
) )
const onPointerMove = React.useCallback( const onPointerMove = React.useCallback(
@ -28,7 +27,7 @@ export function useCanvasEvents() {
const info = inputs.pointerMove(e, 'canvas') const info = inputs.pointerMove(e, 'canvas')
callbacks.onPointerMove?.(info, e) callbacks.onPointerMove?.(info, e)
}, },
[callbacks] [callbacks, inputs]
) )
const onPointerUp = React.useCallback( const onPointerUp = React.useCallback(
@ -47,7 +46,7 @@ export function useCanvasEvents() {
callbacks.onReleaseCanvas?.(info, e) callbacks.onReleaseCanvas?.(info, e)
callbacks.onPointerUp?.(info, e) callbacks.onPointerUp?.(info, e)
}, },
[callbacks] [callbacks, inputs]
) )
return { return {

View file

@ -1,9 +1,8 @@
import * as React from 'react' import * as React from 'react'
import { inputs } from '+inputs'
import { useTLContext } from './useTLContext' import { useTLContext } from './useTLContext'
export function useHandleEvents(id: string) { export function useHandleEvents(id: string) {
const { callbacks } = useTLContext() const { inputs, callbacks } = useTLContext()
const onPointerDown = React.useCallback( const onPointerDown = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
@ -15,7 +14,7 @@ export function useHandleEvents(id: string) {
callbacks.onPointHandle?.(info, e) callbacks.onPointHandle?.(info, e)
callbacks.onPointerDown?.(info, e) callbacks.onPointerDown?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerUp = React.useCallback( const onPointerUp = React.useCallback(
@ -36,7 +35,7 @@ export function useHandleEvents(id: string) {
} }
callbacks.onPointerUp?.(info, e) callbacks.onPointerUp?.(info, e)
}, },
[callbacks] [inputs, callbacks]
) )
const onPointerMove = React.useCallback( const onPointerMove = React.useCallback(
@ -48,7 +47,7 @@ export function useHandleEvents(id: string) {
const info = inputs.pointerMove(e, id) const info = inputs.pointerMove(e, id)
callbacks.onPointerMove?.(info, e) callbacks.onPointerMove?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerEnter = React.useCallback( const onPointerEnter = React.useCallback(
@ -56,7 +55,7 @@ export function useHandleEvents(id: string) {
const info = inputs.pointerEnter(e, id) const info = inputs.pointerEnter(e, id)
callbacks.onHoverHandle?.(info, e) callbacks.onHoverHandle?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onPointerLeave = React.useCallback( const onPointerLeave = React.useCallback(
@ -64,7 +63,7 @@ export function useHandleEvents(id: string) {
const info = inputs.pointerEnter(e, id) const info = inputs.pointerEnter(e, id)
callbacks.onUnhoverHandle?.(info, e) callbacks.onUnhoverHandle?.(info, e)
}, },
[callbacks, id] [inputs, callbacks, id]
) )
const onTouchStart = React.useCallback((e: React.TouchEvent) => { const onTouchStart = React.useCallback((e: React.TouchEvent) => {

View file

@ -0,0 +1,41 @@
import { useTLContext } from '+hooks'
import * as React from 'react'
export function useResizeObserver<T extends HTMLElement | SVGElement>(ref: React.RefObject<T>) {
const { inputs } = useTLContext()
React.useEffect(() => {
function handleScroll() {
const rect = ref.current?.getBoundingClientRect()
if (rect) {
inputs.offset = [rect.left, rect.top]
}
}
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [inputs])
React.useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
if (inputs.isPinching) return
if (entries[0].contentRect) {
const rect = ref.current?.getBoundingClientRect()
if (rect) {
inputs.offset = [rect.left, rect.top]
}
}
})
if (ref.current) {
resizeObserver.observe(ref.current)
}
return () => {
resizeObserver.disconnect()
}
}, [ref, inputs])
}

View file

@ -1,10 +1,9 @@
import * as React from 'react' import * as React from 'react'
import { inputs } from '+inputs'
import { Utils } from '+utils' import { Utils } from '+utils'
import { TLContext } from '+hooks' import { TLContext } from '+hooks'
export function useShapeEvents(id: string, disable = false) { export function useShapeEvents(id: string, disable = false) {
const { rPageState, rScreenBounds, callbacks } = React.useContext(TLContext) const { rPageState, rScreenBounds, callbacks, inputs } = React.useContext(TLContext)
const onPointerDown = React.useCallback( const onPointerDown = React.useCallback(
(e: React.PointerEvent) => { (e: React.PointerEvent) => {
@ -38,7 +37,7 @@ export function useShapeEvents(id: string, disable = false) {
callbacks.onPointShape?.(info, e) callbacks.onPointShape?.(info, e)
callbacks.onPointerDown?.(info, e) callbacks.onPointerDown?.(info, e)
}, },
[callbacks, id, disable] [inputs, callbacks, id, disable]
) )
const onPointerUp = React.useCallback( const onPointerUp = React.useCallback(
@ -60,7 +59,7 @@ export function useShapeEvents(id: string, disable = false) {
callbacks.onReleaseShape?.(info, e) callbacks.onReleaseShape?.(info, e)
callbacks.onPointerUp?.(info, e) callbacks.onPointerUp?.(info, e)
}, },
[callbacks, id, disable] [inputs, callbacks, id, disable]
) )
const onPointerMove = React.useCallback( const onPointerMove = React.useCallback(
@ -77,7 +76,7 @@ export function useShapeEvents(id: string, disable = false) {
callbacks.onPointerMove?.(info, e) callbacks.onPointerMove?.(info, e)
}, },
[callbacks, id, disable] [inputs, callbacks, id, disable]
) )
const onPointerEnter = React.useCallback( const onPointerEnter = React.useCallback(
@ -86,7 +85,7 @@ export function useShapeEvents(id: string, disable = false) {
const info = inputs.pointerEnter(e, id) const info = inputs.pointerEnter(e, id)
callbacks.onHoverShape?.(info, e) callbacks.onHoverShape?.(info, e)
}, },
[callbacks, id, disable] [inputs, callbacks, id, disable]
) )
const onPointerLeave = React.useCallback( const onPointerLeave = React.useCallback(
@ -95,7 +94,7 @@ export function useShapeEvents(id: string, disable = false) {
const info = inputs.pointerEnter(e, id) const info = inputs.pointerEnter(e, id)
callbacks.onUnhoverShape?.(info, e) callbacks.onUnhoverShape?.(info, e)
}, },
[callbacks, id, disable] [inputs, callbacks, id, disable]
) )
const onTouchStart = React.useCallback((e: React.TouchEvent) => { const onTouchStart = React.useCallback((e: React.TouchEvent) => {

View file

@ -185,14 +185,13 @@ const tlcss = css`
pointer-events: none; pointer-events: none;
} }
.tl-canvas { .tl-canvas {
position: fixed; position: absolute;
overflow: hidden; overflow: hidden;
top: 0px; top: 0px;
left: 0px; left: 0px;
width: 100%; width: 100%;
height: 100%; height: 100%;
touch-action: none; touch-action: none;
z-index: 100;
pointer-events: all; pointer-events: all;
} }
.tl-container { .tl-container {

View file

@ -1,4 +1,5 @@
import * as React from 'react' import * as React from 'react'
import type { Inputs } from '+inputs'
import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '+types' import type { TLCallbacks, TLShape, TLBounds, TLPageState, TLShapeUtils } from '+types'
export interface TLContextType { export interface TLContextType {
@ -7,6 +8,7 @@ export interface TLContextType {
shapeUtils: TLShapeUtils<TLShape> shapeUtils: TLShapeUtils<TLShape>
rPageState: React.MutableRefObject<TLPageState> rPageState: React.MutableRefObject<TLPageState>
rScreenBounds: React.MutableRefObject<TLBounds | null> rScreenBounds: React.MutableRefObject<TLBounds | null>
inputs: Inputs
} }
export const TLContext = React.createContext<TLContextType>({} as TLContextType) export const TLContext = React.createContext<TLContextType>({} as TLContextType)

View file

@ -1,20 +1,22 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { useRef } from 'react' import * as React from 'react'
import { useTLContext } from './useTLContext' import { useTLContext } from './useTLContext'
import { Vec } from '+utils' import { Vec } from '+utils'
import { useWheel, usePinch } from 'react-use-gesture' import { useWheel, usePinch } from 'react-use-gesture'
import { inputs } from '+inputs'
// Capture zoom gestures (pinches, wheels and pans) // Capture zoom gestures (pinches, wheels and pans)
export function useZoomEvents() { export function useZoomEvents<T extends HTMLElement | SVGElement>(ref: React.RefObject<T>) {
const rPinchDa = useRef<number[] | undefined>(undefined) const rPinchDa = React.useRef<number[] | undefined>(undefined)
const rOriginPoint = useRef<number[] | undefined>(undefined) const rOriginPoint = React.useRef<number[] | undefined>(undefined)
const rPinchPoint = useRef<number[] | undefined>(undefined) const rPinchPoint = React.useRef<number[] | undefined>(undefined)
const { callbacks } = useTLContext() const { inputs, callbacks } = useTLContext()
useWheel( useWheel(
({ event: e, delta }) => { ({ event: e, delta }) => {
const elm = ref.current
if (!(e.target === elm || elm?.contains(e.target as Node))) return
e.preventDefault() e.preventDefault()
if (Vec.isEqual(delta, [0, 0])) return if (Vec.isEqual(delta, [0, 0])) return
@ -24,15 +26,20 @@ export function useZoomEvents() {
callbacks.onPan?.(info, e) callbacks.onPan?.(info, e)
}, },
{ {
domTarget: typeof document === 'undefined' ? undefined : document.body, domTarget: window,
eventOptions: { passive: false }, eventOptions: { passive: false },
} }
) )
usePinch( usePinch(
({ pinching, da, origin, event: e }) => { ({ pinching, da, origin, event: e }) => {
const elm = ref.current
if (!(e.target === elm || elm?.contains(e.target as Node))) return
const info = inputs.pinch(origin, origin)
if (!pinching) { if (!pinching) {
const info = inputs.pinch(origin, origin) inputs.isPinching = false
callbacks.onPinchEnd?.( callbacks.onPinchEnd?.(
info, info,
e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
@ -44,14 +51,14 @@ export function useZoomEvents() {
} }
if (rPinchPoint.current === undefined) { if (rPinchPoint.current === undefined) {
const info = inputs.pinch(origin, origin) inputs.isPinching = true
callbacks.onPinchStart?.( callbacks.onPinchStart?.(
info, info,
e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
) )
rPinchDa.current = da rPinchDa.current = da
rPinchPoint.current = origin rPinchPoint.current = info.point
rOriginPoint.current = origin rOriginPoint.current = info.point
} }
if (!rPinchDa.current) throw Error('No pinch direction!') if (!rPinchDa.current) throw Error('No pinch direction!')
@ -59,8 +66,6 @@ export function useZoomEvents() {
const [distanceDelta] = Vec.sub(rPinchDa.current, da) const [distanceDelta] = Vec.sub(rPinchDa.current, da)
const info = inputs.pinch(rPinchPoint.current, origin)
callbacks.onPinch?.( callbacks.onPinch?.(
{ {
...info, ...info,
@ -75,7 +80,7 @@ export function useZoomEvents() {
rPinchPoint.current = origin rPinchPoint.current = origin
}, },
{ {
domTarget: typeof document === 'undefined' ? undefined : document.body, domTarget: window,
eventOptions: { passive: false }, eventOptions: { passive: false },
} }
) )

View file

@ -4,10 +4,13 @@ import { Vec, Utils } from './utils'
const DOUBLE_CLICK_DURATION = 250 const DOUBLE_CLICK_DURATION = 250
class Inputs { export class Inputs {
pointer?: TLPointerInfo<string> pointer?: TLPointerInfo<string>
keyboard?: TLKeyboardInfo keyboard?: TLKeyboardInfo
keys: Record<string, boolean> = {} keys: Record<string, boolean> = {}
isPinching = false
offset = [0, 0]
pointerUpTime = 0 pointerUpTime = 0
@ -69,7 +72,7 @@ class Inputs {
pointerDown<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> { pointerDown<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
const { shiftKey, ctrlKey, metaKey, altKey } = e const { shiftKey, ctrlKey, metaKey, altKey } = e
const point = Inputs.getPoint(e) const point = Inputs.getPoint(e, this.offset)
const info: TLPointerInfo<T> = { const info: TLPointerInfo<T> = {
target, target,
@ -95,7 +98,7 @@ class Inputs {
): TLPointerInfo<T> { ): TLPointerInfo<T> {
const { shiftKey, ctrlKey, metaKey, altKey } = e const { shiftKey, ctrlKey, metaKey, altKey } = e
const point = Inputs.getPoint(e) const point = Inputs.getPoint(e, this.offset)
const info: TLPointerInfo<T> = { const info: TLPointerInfo<T> = {
target, target,
@ -120,7 +123,7 @@ class Inputs {
const prev = this.pointer const prev = this.pointer
const point = Inputs.getPoint(e) const point = Inputs.getPoint(e, this.offset)
const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0] const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
@ -148,7 +151,7 @@ class Inputs {
const prev = this.pointer const prev = this.pointer
const point = Inputs.getPoint(e) const point = Inputs.getPoint(e, this.offset)
const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0] const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
@ -182,7 +185,7 @@ class Inputs {
origin: this.pointer?.origin || [0, 0], origin: this.pointer?.origin || [0, 0],
delta: [0, 0], delta: [0, 0],
pressure: 0.5, pressure: 0.5,
point: Inputs.getPoint(e), point: Inputs.getPoint(e, this.offset),
shiftKey, shiftKey,
ctrlKey, ctrlKey,
metaKey, metaKey,
@ -203,7 +206,7 @@ class Inputs {
const prev = this.pointer const prev = this.pointer
const point = Inputs.getPoint(e) const point = Inputs.getPoint(e, this.offset)
const info: TLPointerInfo<'wheel'> = { const info: TLPointerInfo<'wheel'> = {
...prev, ...prev,
@ -281,9 +284,9 @@ class Inputs {
const info: TLPointerInfo<'pinch'> = { const info: TLPointerInfo<'pinch'> = {
pointerId: 0, pointerId: 0,
target: 'pinch', target: 'pinch',
origin: prev?.origin || Vec.round(point), origin: prev?.origin || Vec.sub(Vec.round(point), this.offset),
delta: delta, delta: delta,
point: Vec.round(point), point: Vec.sub(Vec.round(point), this.offset),
pressure: 0.5, pressure: 0.5,
shiftKey, shiftKey,
ctrlKey, ctrlKey,
@ -304,9 +307,13 @@ class Inputs {
} }
static getPoint( static getPoint(
e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent,
offset = [0, 0]
): number[] { ): number[] {
return [Number(e.clientX.toPrecision(5)), Number(e.clientY.toPrecision(5))] return [
Number(e.clientX.toPrecision(5)) - offset[0],
Number(e.clientY.toPrecision(5)) - offset[1],
]
} }
static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) { static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) {

View file

@ -3,6 +3,7 @@ import type { TLPageState, TLBounds } from '../types'
import { mockDocument } from './mockDocument' import { mockDocument } from './mockDocument'
import { mockUtils } from './mockUtils' import { mockUtils } from './mockUtils'
import { useTLTheme, TLContext } from '../hooks' import { useTLTheme, TLContext } from '../hooks'
import { Inputs } from '+inputs'
export const ContextWrapper: React.FC = ({ children }) => { export const ContextWrapper: React.FC = ({ children }) => {
useTLTheme() useTLTheme()
@ -14,6 +15,7 @@ export const ContextWrapper: React.FC = ({ children }) => {
shapeUtils: mockUtils, shapeUtils: mockUtils,
rScreenBounds, rScreenBounds,
rPageState, rPageState,
inputs: new Inputs(),
})) }))
return <TLContext.Provider value={context}>{children}</TLContext.Provider> return <TLContext.Provider value={context}>{children}</TLContext.Provider>

View file

@ -9,6 +9,10 @@ if (!fs.existsSync('./dist')) {
fs.mkdirSync('./dist') fs.mkdirSync('./dist')
} }
fs.copyFile('./src/styles.css', './dist/styles.css', (err) => {
if (err) throw err
})
fs.copyFile('./src/index.html', './dist/index.html', (err) => { fs.copyFile('./src/index.html', './dist/index.html', (err) => {
if (err) throw err if (err) throw err
}) })

View file

@ -2,7 +2,8 @@ import * as React from 'react'
import Basic from './basic' import Basic from './basic'
import Controlled from './controlled' import Controlled from './controlled'
import Imperative from './imperative' import Imperative from './imperative'
import Small from './small'
export default function App(): JSX.Element { export default function App(): JSX.Element {
return <Basic /> return <Small />
} }

View file

@ -2,7 +2,8 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="favicon.ico" />
<link rel="stylesheet" href="styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>tldraw</title> <title>tldraw</title>
</head> </head>

View file

@ -0,0 +1,30 @@
import * as React from 'react'
import Editor from './components/editor'
export default function BasicUsage(): JSX.Element {
return (
<div>
<div
style={{
position: 'relative',
margin: '5%',
width: 'calc(100% - 100px)',
height: '500px',
}}
>
<Editor id="small1" />
</div>
<div
style={{
position: 'relative',
margin: '5%',
width: 'calc(100% - 100px)',
height: '500px',
}}
>
<Editor id="small2" />
</div>
</div>
)
}

View file

@ -0,0 +1,8 @@
html,
* {
box-sizing: border-box;
}
body {
overscroll-behavior: none;
}

View file

@ -5,7 +5,7 @@ import styled from '~styles'
/* -------------------------------------------------- */ /* -------------------------------------------------- */
export const DialogContent = styled('div', { export const DialogContent = styled('div', {
position: 'fixed', position: 'absolute',
top: '50%', top: '50%',
left: '50%', left: '50%',
transform: 'translate(-50%, -50%)', transform: 'translate(-50%, -50%)',

View file

@ -227,17 +227,16 @@ const MenuButtons = styled('div', {
gap: 8, gap: 8,
}) })
const Layout = styled('main', { const Layout = styled('div', {
position: 'fixed',
overflow: 'hidden', overflow: 'hidden',
top: 0, position: 'absolute',
left: 0,
bottom: 0,
right: 0,
height: '100%', height: '100%',
width: '100%', width: '100%',
top: 0,
right: 0,
bottom: 0,
left: 0,
padding: '8px 8px 0 8px', padding: '8px 8px 0 8px',
zIndex: 200,
display: 'flex', display: 'flex',
alignItems: 'flex-start', alignItems: 'flex-start',
justifyContent: 'flex-start', justifyContent: 'flex-start',
@ -253,5 +252,7 @@ const Layout = styled('main', {
position: 'absolute', position: 'absolute',
top: 0, top: 0,
left: 0, left: 0,
width: '100%',
height: '100%',
}, },
}) })

View file

@ -139,7 +139,7 @@ export const ToolsPanel = React.memo((): JSX.Element => {
}) })
const ToolsPanelContainer = styled('div', { const ToolsPanelContainer = styled('div', {
position: 'fixed', position: 'absolute',
bottom: 0, bottom: 0,
left: 0, left: 0,
right: 0, right: 0,

View file

@ -2231,11 +2231,11 @@ export class TLDrawState extends StateManager<Data> {
} }
onPinchEnd: TLPinchEventHandler = () => { onPinchEnd: TLPinchEventHandler = () => {
if (this.state.settings.isZoomSnap) { // if (this.state.settings.isZoomSnap) {
const i = Math.round((this.pageState.camera.zoom * 100) / 25) // const i = Math.round((this.pageState.camera.zoom * 100) / 25)
const nextZoom = TLDR.getCameraZoom(i * 0.25) // const nextZoom = TLDR.getCameraZoom(i * 0.25)
this.zoomTo(nextZoom, inputs.pointer?.point) // this.zoomTo(nextZoom, inputs.pointer?.point)
} // }
this.setStatus(this.appState.status.previous) this.setStatus(this.appState.status.previous)
} }

View file

@ -1,2 +1,3 @@
import '@testing-library/jest-dom/extend-expect' import '@testing-library/jest-dom/extend-expect'
import "fake-indexeddb/auto" import 'fake-indexeddb/auto'
global.ResizeObserver = require('resize-observer-polyfill')

View file

@ -11417,6 +11417,11 @@ require_optional@^1.0.1:
resolve-from "^2.0.0" resolve-from "^2.0.0"
semver "^5.1.0" semver "^5.1.0"
resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-cwd@^2.0.0: resolve-cwd@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"