Browse Source

Adjusts small example, makes inputs unique to each instance

main
Steve Ruiz 3 years ago
parent
commit
8154ed5a2a

+ 2
- 1
package.json View File

48
     "lerna": "^3.15.0",
48
     "lerna": "^3.15.0",
49
     "react": "^17.0.2",
49
     "react": "^17.0.2",
50
     "react-dom": "^17.0.2",
50
     "react-dom": "^17.0.2",
51
+    "resize-observer-polyfill": "^1.5.1",
51
     "ts-jest": "^27.0.5",
52
     "ts-jest": "^27.0.5",
52
     "tslib": "^2.3.0",
53
     "tslib": "^2.3.0",
53
     "typedoc": "^0.21.9",
54
     "typedoc": "^0.21.9",
114
       "\\+(.*)": "<rootDir>/packages/core/src/$1"
115
       "\\+(.*)": "<rootDir>/packages/core/src/$1"
115
     }
116
     }
116
   }
117
   }
117
-}
118
+}

+ 6
- 2
packages/core/src/components/canvas/canvas.tsx View File

12
 import { Brush } from '+components/brush'
12
 import { Brush } from '+components/brush'
13
 import { Defs } from '+components/defs'
13
 import { Defs } from '+components/defs'
14
 import { Page } from '+components/page'
14
 import { Page } from '+components/page'
15
+import { useResizeObserver } from '+hooks/useResizeObserver'
15
 
16
 
16
 function resetError() {
17
 function resetError() {
17
   void null
18
   void null
35
   hideIndicators = false,
36
   hideIndicators = false,
36
 }: CanvasProps<T>): JSX.Element {
37
 }: CanvasProps<T>): JSX.Element {
37
   const rCanvas = React.useRef<SVGSVGElement>(null)
38
   const rCanvas = React.useRef<SVGSVGElement>(null)
39
+  const rContainer = React.useRef<HTMLDivElement>(null)
38
 
40
 
39
   const rGroup = useCameraCss(pageState)
41
   const rGroup = useCameraCss(pageState)
40
 
42
 
41
-  useZoomEvents()
43
+  useResizeObserver(rCanvas)
44
+
45
+  useZoomEvents(rCanvas)
42
 
46
 
43
   useSafariFocusOutFix()
47
   useSafariFocusOutFix()
44
 
48
 
47
   const events = useCanvasEvents()
51
   const events = useCanvasEvents()
48
 
52
 
49
   return (
53
   return (
50
-    <div className="tl-container">
54
+    <div className="tl-container" ref={rContainer}>
51
       <svg id="canvas" className="tl-canvas" ref={rCanvas} {...events}>
55
       <svg id="canvas" className="tl-canvas" ref={rCanvas} {...events}>
52
         <ErrorBoundary FallbackComponent={ErrorFallback} onReset={resetError}>
56
         <ErrorBoundary FallbackComponent={ErrorFallback} onReset={resetError}>
53
           <Defs zoom={pageState.camera.zoom} />
57
           <Defs zoom={pageState.camera.zoom} />

+ 2
- 0
packages/core/src/components/renderer/renderer.tsx View File

10
   TLBinding,
10
   TLBinding,
11
 } from '../../types'
11
 } from '../../types'
12
 import { Canvas } from '../canvas'
12
 import { Canvas } from '../canvas'
13
+import { Inputs } from '../../inputs'
13
 import { useTLTheme, TLContext, TLContextType } from '../../hooks'
14
 import { useTLTheme, TLContext, TLContextType } from '../../hooks'
14
 
15
 
15
 export interface RendererProps<T extends TLShape, M extends Record<string, unknown>>
16
 export interface RendererProps<T extends TLShape, M extends Record<string, unknown>>
87
     shapeUtils,
88
     shapeUtils,
88
     rScreenBounds,
89
     rScreenBounds,
89
     rPageState,
90
     rPageState,
91
+    inputs: new Inputs(),
90
   }))
92
   }))
91
 
93
 
92
   return (
94
   return (

+ 6
- 7
packages/core/src/hooks/useBoundsEvents.tsx View File

1
 import * as React from 'react'
1
 import * as React from 'react'
2
-import { inputs } from '+inputs'
3
 import { useTLContext } from './useTLContext'
2
 import { useTLContext } from './useTLContext'
4
 
3
 
5
 export function useBoundsEvents() {
4
 export function useBoundsEvents() {
6
-  const { callbacks } = useTLContext()
5
+  const { callbacks, inputs } = useTLContext()
7
 
6
 
8
   const onPointerDown = React.useCallback(
7
   const onPointerDown = React.useCallback(
9
     (e: React.PointerEvent) => {
8
     (e: React.PointerEvent) => {
15
       callbacks.onPointBounds?.(info, e)
14
       callbacks.onPointBounds?.(info, e)
16
       callbacks.onPointerDown?.(info, e)
15
       callbacks.onPointerDown?.(info, e)
17
     },
16
     },
18
-    [callbacks]
17
+    [callbacks, inputs]
19
   )
18
   )
20
 
19
 
21
   const onPointerUp = React.useCallback(
20
   const onPointerUp = React.useCallback(
36
       callbacks.onReleaseBounds?.(info, e)
35
       callbacks.onReleaseBounds?.(info, e)
37
       callbacks.onPointerUp?.(info, e)
36
       callbacks.onPointerUp?.(info, e)
38
     },
37
     },
39
-    [callbacks]
38
+    [callbacks, inputs]
40
   )
39
   )
41
 
40
 
42
   const onPointerMove = React.useCallback(
41
   const onPointerMove = React.useCallback(
49
       const info = inputs.pointerMove(e, 'bounds')
48
       const info = inputs.pointerMove(e, 'bounds')
50
       callbacks.onPointerMove?.(info, e)
49
       callbacks.onPointerMove?.(info, e)
51
     },
50
     },
52
-    [callbacks]
51
+    [callbacks, inputs]
53
   )
52
   )
54
 
53
 
55
   const onPointerEnter = React.useCallback(
54
   const onPointerEnter = React.useCallback(
56
     (e: React.PointerEvent) => {
55
     (e: React.PointerEvent) => {
57
       callbacks.onHoverBounds?.(inputs.pointerEnter(e, 'bounds'), e)
56
       callbacks.onHoverBounds?.(inputs.pointerEnter(e, 'bounds'), e)
58
     },
57
     },
59
-    [callbacks]
58
+    [callbacks, inputs]
60
   )
59
   )
61
 
60
 
62
   const onPointerLeave = React.useCallback(
61
   const onPointerLeave = React.useCallback(
63
     (e: React.PointerEvent) => {
62
     (e: React.PointerEvent) => {
64
       callbacks.onUnhoverBounds?.(inputs.pointerEnter(e, 'bounds'), e)
63
       callbacks.onUnhoverBounds?.(inputs.pointerEnter(e, 'bounds'), e)
65
     },
64
     },
66
-    [callbacks]
65
+    [callbacks, inputs]
67
   )
66
   )
68
 
67
 
69
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {
68
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {

+ 6
- 7
packages/core/src/hooks/useBoundsHandleEvents.tsx View File

1
 import * as React from 'react'
1
 import * as React from 'react'
2
-import { inputs } from '+inputs'
3
 import type { TLBoundsEdge, TLBoundsCorner } from '+types'
2
 import type { TLBoundsEdge, TLBoundsCorner } from '+types'
4
 import { useTLContext } from './useTLContext'
3
 import { useTLContext } from './useTLContext'
5
 
4
 
6
 export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotate') {
5
 export function useBoundsHandleEvents(id: TLBoundsCorner | TLBoundsEdge | 'rotate') {
7
-  const { callbacks } = useTLContext()
6
+  const { callbacks, inputs } = useTLContext()
8
 
7
 
9
   const onPointerDown = React.useCallback(
8
   const onPointerDown = React.useCallback(
10
     (e: React.PointerEvent) => {
9
     (e: React.PointerEvent) => {
16
       callbacks.onPointBoundsHandle?.(info, e)
15
       callbacks.onPointBoundsHandle?.(info, e)
17
       callbacks.onPointerDown?.(info, e)
16
       callbacks.onPointerDown?.(info, e)
18
     },
17
     },
19
-    [callbacks, id]
18
+    [inputs, callbacks, id]
20
   )
19
   )
21
 
20
 
22
   const onPointerUp = React.useCallback(
21
   const onPointerUp = React.useCallback(
37
       callbacks.onReleaseBoundsHandle?.(info, e)
36
       callbacks.onReleaseBoundsHandle?.(info, e)
38
       callbacks.onPointerUp?.(info, e)
37
       callbacks.onPointerUp?.(info, e)
39
     },
38
     },
40
-    [callbacks, id]
39
+    [inputs, callbacks, id]
41
   )
40
   )
42
 
41
 
43
   const onPointerMove = React.useCallback(
42
   const onPointerMove = React.useCallback(
48
       const info = inputs.pointerMove(e, id)
47
       const info = inputs.pointerMove(e, id)
49
       callbacks.onPointerMove?.(info, e)
48
       callbacks.onPointerMove?.(info, e)
50
     },
49
     },
51
-    [callbacks, id]
50
+    [inputs, callbacks, id]
52
   )
51
   )
53
 
52
 
54
   const onPointerEnter = React.useCallback(
53
   const onPointerEnter = React.useCallback(
55
     (e: React.PointerEvent) => {
54
     (e: React.PointerEvent) => {
56
       callbacks.onHoverBoundsHandle?.(inputs.pointerEnter(e, id), e)
55
       callbacks.onHoverBoundsHandle?.(inputs.pointerEnter(e, id), e)
57
     },
56
     },
58
-    [callbacks, id]
57
+    [inputs, callbacks, id]
59
   )
58
   )
60
 
59
 
61
   const onPointerLeave = React.useCallback(
60
   const onPointerLeave = React.useCallback(
62
     (e: React.PointerEvent) => {
61
     (e: React.PointerEvent) => {
63
       callbacks.onUnhoverBoundsHandle?.(inputs.pointerEnter(e, id), e)
62
       callbacks.onUnhoverBoundsHandle?.(inputs.pointerEnter(e, id), e)
64
     },
63
     },
65
-    [callbacks, id]
64
+    [inputs, callbacks, id]
66
   )
65
   )
67
 
66
 
68
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {
67
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {

+ 4
- 5
packages/core/src/hooks/useCanvasEvents.tsx View File

1
 import * as React from 'react'
1
 import * as React from 'react'
2
 import { useTLContext } from './useTLContext'
2
 import { useTLContext } from './useTLContext'
3
-import { inputs } from '+inputs'
4
 
3
 
5
 export function useCanvasEvents() {
4
 export function useCanvasEvents() {
6
-  const { callbacks } = useTLContext()
5
+  const { callbacks, inputs } = useTLContext()
7
 
6
 
8
   const onPointerDown = React.useCallback(
7
   const onPointerDown = React.useCallback(
9
     (e: React.PointerEvent) => {
8
     (e: React.PointerEvent) => {
16
         callbacks.onPointerDown?.(info, e)
15
         callbacks.onPointerDown?.(info, e)
17
       }
16
       }
18
     },
17
     },
19
-    [callbacks]
18
+    [callbacks, inputs]
20
   )
19
   )
21
 
20
 
22
   const onPointerMove = React.useCallback(
21
   const onPointerMove = React.useCallback(
28
       const info = inputs.pointerMove(e, 'canvas')
27
       const info = inputs.pointerMove(e, 'canvas')
29
       callbacks.onPointerMove?.(info, e)
28
       callbacks.onPointerMove?.(info, e)
30
     },
29
     },
31
-    [callbacks]
30
+    [callbacks, inputs]
32
   )
31
   )
33
 
32
 
34
   const onPointerUp = React.useCallback(
33
   const onPointerUp = React.useCallback(
47
       callbacks.onReleaseCanvas?.(info, e)
46
       callbacks.onReleaseCanvas?.(info, e)
48
       callbacks.onPointerUp?.(info, e)
47
       callbacks.onPointerUp?.(info, e)
49
     },
48
     },
50
-    [callbacks]
49
+    [callbacks, inputs]
51
   )
50
   )
52
 
51
 
53
   return {
52
   return {

+ 6
- 7
packages/core/src/hooks/useHandleEvents.tsx View File

1
 import * as React from 'react'
1
 import * as React from 'react'
2
-import { inputs } from '+inputs'
3
 import { useTLContext } from './useTLContext'
2
 import { useTLContext } from './useTLContext'
4
 
3
 
5
 export function useHandleEvents(id: string) {
4
 export function useHandleEvents(id: string) {
6
-  const { callbacks } = useTLContext()
5
+  const { inputs, callbacks } = useTLContext()
7
 
6
 
8
   const onPointerDown = React.useCallback(
7
   const onPointerDown = React.useCallback(
9
     (e: React.PointerEvent) => {
8
     (e: React.PointerEvent) => {
15
       callbacks.onPointHandle?.(info, e)
14
       callbacks.onPointHandle?.(info, e)
16
       callbacks.onPointerDown?.(info, e)
15
       callbacks.onPointerDown?.(info, e)
17
     },
16
     },
18
-    [callbacks, id]
17
+    [inputs, callbacks, id]
19
   )
18
   )
20
 
19
 
21
   const onPointerUp = React.useCallback(
20
   const onPointerUp = React.useCallback(
36
       }
35
       }
37
       callbacks.onPointerUp?.(info, e)
36
       callbacks.onPointerUp?.(info, e)
38
     },
37
     },
39
-    [callbacks]
38
+    [inputs, callbacks]
40
   )
39
   )
41
 
40
 
42
   const onPointerMove = React.useCallback(
41
   const onPointerMove = React.useCallback(
48
       const info = inputs.pointerMove(e, id)
47
       const info = inputs.pointerMove(e, id)
49
       callbacks.onPointerMove?.(info, e)
48
       callbacks.onPointerMove?.(info, e)
50
     },
49
     },
51
-    [callbacks, id]
50
+    [inputs, callbacks, id]
52
   )
51
   )
53
 
52
 
54
   const onPointerEnter = React.useCallback(
53
   const onPointerEnter = React.useCallback(
56
       const info = inputs.pointerEnter(e, id)
55
       const info = inputs.pointerEnter(e, id)
57
       callbacks.onHoverHandle?.(info, e)
56
       callbacks.onHoverHandle?.(info, e)
58
     },
57
     },
59
-    [callbacks, id]
58
+    [inputs, callbacks, id]
60
   )
59
   )
61
 
60
 
62
   const onPointerLeave = React.useCallback(
61
   const onPointerLeave = React.useCallback(
64
       const info = inputs.pointerEnter(e, id)
63
       const info = inputs.pointerEnter(e, id)
65
       callbacks.onUnhoverHandle?.(info, e)
64
       callbacks.onUnhoverHandle?.(info, e)
66
     },
65
     },
67
-    [callbacks, id]
66
+    [inputs, callbacks, id]
68
   )
67
   )
69
 
68
 
70
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {
69
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {

+ 41
- 0
packages/core/src/hooks/useResizeObserver.ts View File

1
+import { useTLContext } from '+hooks'
2
+import * as React from 'react'
3
+
4
+export function useResizeObserver<T extends HTMLElement | SVGElement>(ref: React.RefObject<T>) {
5
+  const { inputs } = useTLContext()
6
+
7
+  React.useEffect(() => {
8
+    function handleScroll() {
9
+      const rect = ref.current?.getBoundingClientRect()
10
+      if (rect) {
11
+        inputs.offset = [rect.left, rect.top]
12
+      }
13
+    }
14
+
15
+    window.addEventListener('scroll', handleScroll)
16
+    return () => {
17
+      window.removeEventListener('scroll', handleScroll)
18
+    }
19
+  }, [inputs])
20
+
21
+  React.useEffect(() => {
22
+    const resizeObserver = new ResizeObserver((entries) => {
23
+      if (inputs.isPinching) return
24
+
25
+      if (entries[0].contentRect) {
26
+        const rect = ref.current?.getBoundingClientRect()
27
+        if (rect) {
28
+          inputs.offset = [rect.left, rect.top]
29
+        }
30
+      }
31
+    })
32
+
33
+    if (ref.current) {
34
+      resizeObserver.observe(ref.current)
35
+    }
36
+
37
+    return () => {
38
+      resizeObserver.disconnect()
39
+    }
40
+  }, [ref, inputs])
41
+}

+ 6
- 7
packages/core/src/hooks/useShapeEvents.tsx View File

1
 import * as React from 'react'
1
 import * as React from 'react'
2
-import { inputs } from '+inputs'
3
 import { Utils } from '+utils'
2
 import { Utils } from '+utils'
4
 import { TLContext } from '+hooks'
3
 import { TLContext } from '+hooks'
5
 
4
 
6
 export function useShapeEvents(id: string, disable = false) {
5
 export function useShapeEvents(id: string, disable = false) {
7
-  const { rPageState, rScreenBounds, callbacks } = React.useContext(TLContext)
6
+  const { rPageState, rScreenBounds, callbacks, inputs } = React.useContext(TLContext)
8
 
7
 
9
   const onPointerDown = React.useCallback(
8
   const onPointerDown = React.useCallback(
10
     (e: React.PointerEvent) => {
9
     (e: React.PointerEvent) => {
38
       callbacks.onPointShape?.(info, e)
37
       callbacks.onPointShape?.(info, e)
39
       callbacks.onPointerDown?.(info, e)
38
       callbacks.onPointerDown?.(info, e)
40
     },
39
     },
41
-    [callbacks, id, disable]
40
+    [inputs, callbacks, id, disable]
42
   )
41
   )
43
 
42
 
44
   const onPointerUp = React.useCallback(
43
   const onPointerUp = React.useCallback(
60
       callbacks.onReleaseShape?.(info, e)
59
       callbacks.onReleaseShape?.(info, e)
61
       callbacks.onPointerUp?.(info, e)
60
       callbacks.onPointerUp?.(info, e)
62
     },
61
     },
63
-    [callbacks, id, disable]
62
+    [inputs, callbacks, id, disable]
64
   )
63
   )
65
 
64
 
66
   const onPointerMove = React.useCallback(
65
   const onPointerMove = React.useCallback(
77
 
76
 
78
       callbacks.onPointerMove?.(info, e)
77
       callbacks.onPointerMove?.(info, e)
79
     },
78
     },
80
-    [callbacks, id, disable]
79
+    [inputs, callbacks, id, disable]
81
   )
80
   )
82
 
81
 
83
   const onPointerEnter = React.useCallback(
82
   const onPointerEnter = React.useCallback(
86
       const info = inputs.pointerEnter(e, id)
85
       const info = inputs.pointerEnter(e, id)
87
       callbacks.onHoverShape?.(info, e)
86
       callbacks.onHoverShape?.(info, e)
88
     },
87
     },
89
-    [callbacks, id, disable]
88
+    [inputs, callbacks, id, disable]
90
   )
89
   )
91
 
90
 
92
   const onPointerLeave = React.useCallback(
91
   const onPointerLeave = React.useCallback(
95
       const info = inputs.pointerEnter(e, id)
94
       const info = inputs.pointerEnter(e, id)
96
       callbacks.onUnhoverShape?.(info, e)
95
       callbacks.onUnhoverShape?.(info, e)
97
     },
96
     },
98
-    [callbacks, id, disable]
97
+    [inputs, callbacks, id, disable]
99
   )
98
   )
100
 
99
 
101
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {
100
   const onTouchStart = React.useCallback((e: React.TouchEvent) => {

+ 1
- 2
packages/core/src/hooks/useStyle.tsx View File

185
     pointer-events: none;
185
     pointer-events: none;
186
   }
186
   }
187
   .tl-canvas {
187
   .tl-canvas {
188
-    position: fixed;
188
+    position: absolute;
189
     overflow: hidden;
189
     overflow: hidden;
190
     top: 0px;
190
     top: 0px;
191
     left: 0px;
191
     left: 0px;
192
     width: 100%;
192
     width: 100%;
193
     height: 100%;
193
     height: 100%;
194
     touch-action: none;
194
     touch-action: none;
195
-    z-index: 100;
196
     pointer-events: all;
195
     pointer-events: all;
197
   }
196
   }
198
   .tl-container {
197
   .tl-container {

+ 2
- 0
packages/core/src/hooks/useTLContext.tsx View File

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

+ 20
- 15
packages/core/src/hooks/useZoomEvents.ts View File

1
 /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
1
 /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
-import { useRef } from 'react'
2
+import * as React from 'react'
3
 import { useTLContext } from './useTLContext'
3
 import { useTLContext } from './useTLContext'
4
 import { Vec } from '+utils'
4
 import { Vec } from '+utils'
5
 import { useWheel, usePinch } from 'react-use-gesture'
5
 import { useWheel, usePinch } from 'react-use-gesture'
6
-import { inputs } from '+inputs'
7
 
6
 
8
 // Capture zoom gestures (pinches, wheels and pans)
7
 // Capture zoom gestures (pinches, wheels and pans)
9
-export function useZoomEvents() {
10
-  const rPinchDa = useRef<number[] | undefined>(undefined)
11
-  const rOriginPoint = useRef<number[] | undefined>(undefined)
12
-  const rPinchPoint = useRef<number[] | undefined>(undefined)
8
+export function useZoomEvents<T extends HTMLElement | SVGElement>(ref: React.RefObject<T>) {
9
+  const rPinchDa = React.useRef<number[] | undefined>(undefined)
10
+  const rOriginPoint = React.useRef<number[] | undefined>(undefined)
11
+  const rPinchPoint = React.useRef<number[] | undefined>(undefined)
13
 
12
 
14
-  const { callbacks } = useTLContext()
13
+  const { inputs, callbacks } = useTLContext()
15
 
14
 
16
   useWheel(
15
   useWheel(
17
     ({ event: e, delta }) => {
16
     ({ event: e, delta }) => {
17
+      const elm = ref.current
18
+      if (!(e.target === elm || elm?.contains(e.target as Node))) return
19
+
18
       e.preventDefault()
20
       e.preventDefault()
19
 
21
 
20
       if (Vec.isEqual(delta, [0, 0])) return
22
       if (Vec.isEqual(delta, [0, 0])) return
24
       callbacks.onPan?.(info, e)
26
       callbacks.onPan?.(info, e)
25
     },
27
     },
26
     {
28
     {
27
-      domTarget: typeof document === 'undefined' ? undefined : document.body,
29
+      domTarget: window,
28
       eventOptions: { passive: false },
30
       eventOptions: { passive: false },
29
     }
31
     }
30
   )
32
   )
31
 
33
 
32
   usePinch(
34
   usePinch(
33
     ({ pinching, da, origin, event: e }) => {
35
     ({ pinching, da, origin, event: e }) => {
36
+      const elm = ref.current
37
+      if (!(e.target === elm || elm?.contains(e.target as Node))) return
38
+
39
+      const info = inputs.pinch(origin, origin)
40
+
34
       if (!pinching) {
41
       if (!pinching) {
35
-        const info = inputs.pinch(origin, origin)
42
+        inputs.isPinching = false
36
         callbacks.onPinchEnd?.(
43
         callbacks.onPinchEnd?.(
37
           info,
44
           info,
38
           e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
45
           e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
44
       }
51
       }
45
 
52
 
46
       if (rPinchPoint.current === undefined) {
53
       if (rPinchPoint.current === undefined) {
47
-        const info = inputs.pinch(origin, origin)
54
+        inputs.isPinching = true
48
         callbacks.onPinchStart?.(
55
         callbacks.onPinchStart?.(
49
           info,
56
           info,
50
           e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
57
           e as React.WheelEvent<Element> | WheelEvent | React.TouchEvent<Element> | TouchEvent
51
         )
58
         )
52
         rPinchDa.current = da
59
         rPinchDa.current = da
53
-        rPinchPoint.current = origin
54
-        rOriginPoint.current = origin
60
+        rPinchPoint.current = info.point
61
+        rOriginPoint.current = info.point
55
       }
62
       }
56
 
63
 
57
       if (!rPinchDa.current) throw Error('No pinch direction!')
64
       if (!rPinchDa.current) throw Error('No pinch direction!')
59
 
66
 
60
       const [distanceDelta] = Vec.sub(rPinchDa.current, da)
67
       const [distanceDelta] = Vec.sub(rPinchDa.current, da)
61
 
68
 
62
-      const info = inputs.pinch(rPinchPoint.current, origin)
63
-
64
       callbacks.onPinch?.(
69
       callbacks.onPinch?.(
65
         {
70
         {
66
           ...info,
71
           ...info,
75
       rPinchPoint.current = origin
80
       rPinchPoint.current = origin
76
     },
81
     },
77
     {
82
     {
78
-      domTarget: typeof document === 'undefined' ? undefined : document.body,
83
+      domTarget: window,
79
       eventOptions: { passive: false },
84
       eventOptions: { passive: false },
80
     }
85
     }
81
   )
86
   )

+ 18
- 11
packages/core/src/inputs.ts View File

4
 
4
 
5
 const DOUBLE_CLICK_DURATION = 250
5
 const DOUBLE_CLICK_DURATION = 250
6
 
6
 
7
-class Inputs {
7
+export class Inputs {
8
   pointer?: TLPointerInfo<string>
8
   pointer?: TLPointerInfo<string>
9
   keyboard?: TLKeyboardInfo
9
   keyboard?: TLKeyboardInfo
10
   keys: Record<string, boolean> = {}
10
   keys: Record<string, boolean> = {}
11
+  isPinching = false
12
+
13
+  offset = [0, 0]
11
 
14
 
12
   pointerUpTime = 0
15
   pointerUpTime = 0
13
 
16
 
69
   pointerDown<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
72
   pointerDown<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
70
     const { shiftKey, ctrlKey, metaKey, altKey } = e
73
     const { shiftKey, ctrlKey, metaKey, altKey } = e
71
 
74
 
72
-    const point = Inputs.getPoint(e)
75
+    const point = Inputs.getPoint(e, this.offset)
73
 
76
 
74
     const info: TLPointerInfo<T> = {
77
     const info: TLPointerInfo<T> = {
75
       target,
78
       target,
95
   ): TLPointerInfo<T> {
98
   ): TLPointerInfo<T> {
96
     const { shiftKey, ctrlKey, metaKey, altKey } = e
99
     const { shiftKey, ctrlKey, metaKey, altKey } = e
97
 
100
 
98
-    const point = Inputs.getPoint(e)
101
+    const point = Inputs.getPoint(e, this.offset)
99
 
102
 
100
     const info: TLPointerInfo<T> = {
103
     const info: TLPointerInfo<T> = {
101
       target,
104
       target,
120
 
123
 
121
     const prev = this.pointer
124
     const prev = this.pointer
122
 
125
 
123
-    const point = Inputs.getPoint(e)
126
+    const point = Inputs.getPoint(e, this.offset)
124
 
127
 
125
     const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
128
     const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
126
 
129
 
148
 
151
 
149
     const prev = this.pointer
152
     const prev = this.pointer
150
 
153
 
151
-    const point = Inputs.getPoint(e)
154
+    const point = Inputs.getPoint(e, this.offset)
152
 
155
 
153
     const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
156
     const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
154
 
157
 
182
       origin: this.pointer?.origin || [0, 0],
185
       origin: this.pointer?.origin || [0, 0],
183
       delta: [0, 0],
186
       delta: [0, 0],
184
       pressure: 0.5,
187
       pressure: 0.5,
185
-      point: Inputs.getPoint(e),
188
+      point: Inputs.getPoint(e, this.offset),
186
       shiftKey,
189
       shiftKey,
187
       ctrlKey,
190
       ctrlKey,
188
       metaKey,
191
       metaKey,
203
 
206
 
204
     const prev = this.pointer
207
     const prev = this.pointer
205
 
208
 
206
-    const point = Inputs.getPoint(e)
209
+    const point = Inputs.getPoint(e, this.offset)
207
 
210
 
208
     const info: TLPointerInfo<'wheel'> = {
211
     const info: TLPointerInfo<'wheel'> = {
209
       ...prev,
212
       ...prev,
281
     const info: TLPointerInfo<'pinch'> = {
284
     const info: TLPointerInfo<'pinch'> = {
282
       pointerId: 0,
285
       pointerId: 0,
283
       target: 'pinch',
286
       target: 'pinch',
284
-      origin: prev?.origin || Vec.round(point),
287
+      origin: prev?.origin || Vec.sub(Vec.round(point), this.offset),
285
       delta: delta,
288
       delta: delta,
286
-      point: Vec.round(point),
289
+      point: Vec.sub(Vec.round(point), this.offset),
287
       pressure: 0.5,
290
       pressure: 0.5,
288
       shiftKey,
291
       shiftKey,
289
       ctrlKey,
292
       ctrlKey,
304
   }
307
   }
305
 
308
 
306
   static getPoint(
309
   static getPoint(
307
-    e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent
310
+    e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent,
311
+    offset = [0, 0]
308
   ): number[] {
312
   ): number[] {
309
-    return [Number(e.clientX.toPrecision(5)), Number(e.clientY.toPrecision(5))]
313
+    return [
314
+      Number(e.clientX.toPrecision(5)) - offset[0],
315
+      Number(e.clientY.toPrecision(5)) - offset[1],
316
+    ]
310
   }
317
   }
311
 
318
 
312
   static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) {
319
   static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) {

+ 2
- 0
packages/core/src/test/context-wrapper.tsx View File

3
 import { mockDocument } from './mockDocument'
3
 import { mockDocument } from './mockDocument'
4
 import { mockUtils } from './mockUtils'
4
 import { mockUtils } from './mockUtils'
5
 import { useTLTheme, TLContext } from '../hooks'
5
 import { useTLTheme, TLContext } from '../hooks'
6
+import { Inputs } from '+inputs'
6
 
7
 
7
 export const ContextWrapper: React.FC = ({ children }) => {
8
 export const ContextWrapper: React.FC = ({ children }) => {
8
   useTLTheme()
9
   useTLTheme()
14
     shapeUtils: mockUtils,
15
     shapeUtils: mockUtils,
15
     rScreenBounds,
16
     rScreenBounds,
16
     rPageState,
17
     rPageState,
18
+    inputs: new Inputs(),
17
   }))
19
   }))
18
 
20
 
19
   return <TLContext.Provider value={context}>{children}</TLContext.Provider>
21
   return <TLContext.Provider value={context}>{children}</TLContext.Provider>

+ 4
- 0
packages/dev/esbuild.config.mjs View File

9
   fs.mkdirSync('./dist')
9
   fs.mkdirSync('./dist')
10
 }
10
 }
11
 
11
 
12
+fs.copyFile('./src/styles.css', './dist/styles.css', (err) => {
13
+  if (err) throw err
14
+})
15
+
12
 fs.copyFile('./src/index.html', './dist/index.html', (err) => {
16
 fs.copyFile('./src/index.html', './dist/index.html', (err) => {
13
   if (err) throw err
17
   if (err) throw err
14
 })
18
 })

+ 2
- 1
packages/dev/src/app.tsx View File

2
 import Basic from './basic'
2
 import Basic from './basic'
3
 import Controlled from './controlled'
3
 import Controlled from './controlled'
4
 import Imperative from './imperative'
4
 import Imperative from './imperative'
5
+import Small from './small'
5
 
6
 
6
 export default function App(): JSX.Element {
7
 export default function App(): JSX.Element {
7
-  return <Basic />
8
+  return <Small />
8
 }
9
 }

+ 2
- 1
packages/dev/src/index.html View File

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

+ 30
- 0
packages/dev/src/small.tsx View File

1
+import * as React from 'react'
2
+import Editor from './components/editor'
3
+
4
+export default function BasicUsage(): JSX.Element {
5
+  return (
6
+    <div>
7
+      <div
8
+        style={{
9
+          position: 'relative',
10
+          margin: '5%',
11
+          width: 'calc(100% - 100px)',
12
+          height: '500px',
13
+        }}
14
+      >
15
+        <Editor id="small1" />
16
+      </div>
17
+
18
+      <div
19
+        style={{
20
+          position: 'relative',
21
+          margin: '5%',
22
+          width: 'calc(100% - 100px)',
23
+          height: '500px',
24
+        }}
25
+      >
26
+        <Editor id="small2" />
27
+      </div>
28
+    </div>
29
+  )
30
+}

+ 8
- 0
packages/dev/src/styles.css View File

1
+html,
2
+* {
3
+  box-sizing: border-box;
4
+}
5
+
6
+body {
7
+  overscroll-behavior: none;
8
+}

+ 1
- 1
packages/tldraw/src/components/shared/dialog.tsx View File

5
 /* -------------------------------------------------- */
5
 /* -------------------------------------------------- */
6
 
6
 
7
 export const DialogContent = styled('div', {
7
 export const DialogContent = styled('div', {
8
-  position: 'fixed',
8
+  position: 'absolute',
9
   top: '50%',
9
   top: '50%',
10
   left: '50%',
10
   left: '50%',
11
   transform: 'translate(-50%, -50%)',
11
   transform: 'translate(-50%, -50%)',

+ 8
- 7
packages/tldraw/src/components/tldraw/tldraw.tsx View File

227
   gap: 8,
227
   gap: 8,
228
 })
228
 })
229
 
229
 
230
-const Layout = styled('main', {
231
-  position: 'fixed',
230
+const Layout = styled('div', {
232
   overflow: 'hidden',
231
   overflow: 'hidden',
233
-  top: 0,
234
-  left: 0,
235
-  bottom: 0,
236
-  right: 0,
232
+  position: 'absolute',
237
   height: '100%',
233
   height: '100%',
238
   width: '100%',
234
   width: '100%',
235
+  top: 0,
236
+  right: 0,
237
+  bottom: 0,
238
+  left: 0,
239
   padding: '8px 8px 0 8px',
239
   padding: '8px 8px 0 8px',
240
-  zIndex: 200,
241
   display: 'flex',
240
   display: 'flex',
242
   alignItems: 'flex-start',
241
   alignItems: 'flex-start',
243
   justifyContent: 'flex-start',
242
   justifyContent: 'flex-start',
253
     position: 'absolute',
252
     position: 'absolute',
254
     top: 0,
253
     top: 0,
255
     left: 0,
254
     left: 0,
255
+    width: '100%',
256
+    height: '100%',
256
   },
257
   },
257
 })
258
 })

+ 1
- 1
packages/tldraw/src/components/tools-panel/tools-panel.tsx View File

139
 })
139
 })
140
 
140
 
141
 const ToolsPanelContainer = styled('div', {
141
 const ToolsPanelContainer = styled('div', {
142
-  position: 'fixed',
142
+  position: 'absolute',
143
   bottom: 0,
143
   bottom: 0,
144
   left: 0,
144
   left: 0,
145
   right: 0,
145
   right: 0,

+ 5
- 5
packages/tldraw/src/state/tlstate.ts View File

2231
   }
2231
   }
2232
 
2232
 
2233
   onPinchEnd: TLPinchEventHandler = () => {
2233
   onPinchEnd: TLPinchEventHandler = () => {
2234
-    if (this.state.settings.isZoomSnap) {
2235
-      const i = Math.round((this.pageState.camera.zoom * 100) / 25)
2236
-      const nextZoom = TLDR.getCameraZoom(i * 0.25)
2237
-      this.zoomTo(nextZoom, inputs.pointer?.point)
2238
-    }
2234
+    // if (this.state.settings.isZoomSnap) {
2235
+    //   const i = Math.round((this.pageState.camera.zoom * 100) / 25)
2236
+    //   const nextZoom = TLDR.getCameraZoom(i * 0.25)
2237
+    //   this.zoomTo(nextZoom, inputs.pointer?.point)
2238
+    // }
2239
     this.setStatus(this.appState.status.previous)
2239
     this.setStatus(this.appState.status.previous)
2240
   }
2240
   }
2241
 
2241
 

+ 2
- 1
setupTests.ts View File

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

+ 5
- 0
yarn.lock View File

11417
     resolve-from "^2.0.0"
11417
     resolve-from "^2.0.0"
11418
     semver "^5.1.0"
11418
     semver "^5.1.0"
11419
 
11419
 
11420
+resize-observer-polyfill@^1.5.1:
11421
+  version "1.5.1"
11422
+  resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
11423
+  integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
11424
+
11420
 resolve-cwd@^2.0.0:
11425
 resolve-cwd@^2.0.0:
11421
   version "2.0.0"
11426
   version "2.0.0"
11422
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
11427
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"

Loading…
Cancel
Save