Adds space panning, tweak css for performance
This commit is contained in:
parent
5a99f5e49c
commit
f8cb7f03b6
17 changed files with 308 additions and 239 deletions
|
@ -10,7 +10,7 @@ interface BoundsBgProps {
|
||||||
rotation: number
|
rotation: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BoundsBg({ bounds, rotation }: BoundsBgProps): JSX.Element {
|
export const BoundsBg = React.memo(({ bounds, rotation }: BoundsBgProps): JSX.Element => {
|
||||||
const events = useBoundsEvents()
|
const events = useBoundsEvents()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -20,4 +20,4 @@ export function BoundsBg({ bounds, rotation }: BoundsBgProps): JSX.Element {
|
||||||
</SVGContainer>
|
</SVGContainer>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
|
@ -17,14 +17,8 @@ interface BoundsProps {
|
||||||
viewportWidth: number
|
viewportWidth: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Bounds({
|
export const Bounds = React.memo(
|
||||||
zoom,
|
({ zoom, bounds, viewportWidth, rotation, isHidden, isLocked }: BoundsProps): JSX.Element => {
|
||||||
bounds,
|
|
||||||
viewportWidth,
|
|
||||||
rotation,
|
|
||||||
isHidden,
|
|
||||||
isLocked,
|
|
||||||
}: BoundsProps): JSX.Element {
|
|
||||||
// Touch target size
|
// Touch target size
|
||||||
const targetSize = (viewportWidth < 768 ? 16 : 8) / zoom
|
const targetSize = (viewportWidth < 768 ? 16 : 8) / zoom
|
||||||
// Handle size
|
// Handle size
|
||||||
|
@ -102,3 +96,4 @@ export function Bounds({
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -59,14 +59,15 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
|
||||||
|
|
||||||
useSafariFocusOutFix()
|
useSafariFocusOutFix()
|
||||||
|
|
||||||
usePreventNavigation(rCanvas)
|
usePreventNavigation(rCanvas, inputs.bounds.width)
|
||||||
|
|
||||||
const events = useCanvasEvents()
|
const events = useCanvasEvents()
|
||||||
|
|
||||||
useCameraCss(rLayer, rContainer, pageState)
|
useCameraCss(rLayer, rContainer, pageState)
|
||||||
|
|
||||||
const preventScrolling = React.useCallback((e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
const preventScrolling = React.useCallback((e: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||||
e.currentTarget.scrollTo(0, 0)
|
e.preventDefault()
|
||||||
|
// e.currentTarget.scrollTo(0, 0)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useKeyEvents()
|
useKeyEvents()
|
||||||
|
|
|
@ -31,7 +31,7 @@ export const ShapeNode = React.memo(
|
||||||
meta={meta}
|
meta={meta}
|
||||||
/>
|
/>
|
||||||
{children &&
|
{children &&
|
||||||
children.map((childNode, i) => (
|
children.map((childNode) => (
|
||||||
<ShapeNode key={childNode.shape.id} utils={utils} {...childNode} />
|
<ShapeNode key={childNode.shape.id} utils={utils} {...childNode} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -9,27 +9,34 @@ export function useCameraCss(
|
||||||
) {
|
) {
|
||||||
// Update the tl-zoom CSS variable when the zoom changes
|
// Update the tl-zoom CSS variable when the zoom changes
|
||||||
const rZoom = React.useRef(pageState.camera.zoom)
|
const rZoom = React.useRef(pageState.camera.zoom)
|
||||||
|
const rPoint = React.useRef(pageState.camera.point)
|
||||||
|
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
const {
|
const { zoom, point } = pageState.camera
|
||||||
zoom,
|
|
||||||
point: [x, y],
|
const didZoom = zoom !== rZoom.current
|
||||||
} = pageState.camera
|
const didPan = point !== rPoint.current
|
||||||
|
|
||||||
if (zoom !== rZoom.current) {
|
|
||||||
rZoom.current = zoom
|
rZoom.current = zoom
|
||||||
|
rPoint.current = point
|
||||||
|
|
||||||
|
if (didZoom || didPan) {
|
||||||
|
// If we zoomed, set the CSS variable for the zoom
|
||||||
|
if (didZoom) {
|
||||||
const container = containerRef.current
|
const container = containerRef.current
|
||||||
|
|
||||||
if (container) {
|
if (container) {
|
||||||
container.style.setProperty('--tl-zoom', zoom.toString())
|
container.style.setProperty('--tl-zoom', zoom.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Either way, position the layer
|
||||||
const layer = layerRef.current
|
const layer = layerRef.current
|
||||||
|
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.style.setProperty('transform', `scale(${zoom}) translate(${x}px, ${y}px)`)
|
layer.style.setProperty(
|
||||||
|
'transform',
|
||||||
|
`scale(${zoom}) translateX(${point[0]}px) translateY(${point[1]}px)`
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [pageState.camera])
|
}, [pageState.camera])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import Vec from '@tldraw/vec'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTLContext } from './useTLContext'
|
import { useTLContext } from './useTLContext'
|
||||||
|
|
||||||
|
@ -10,8 +11,9 @@ export function useCanvasEvents() {
|
||||||
if (!inputs.pointerIsValid(e)) return
|
if (!inputs.pointerIsValid(e)) return
|
||||||
e.currentTarget.setPointerCapture(e.pointerId)
|
e.currentTarget.setPointerCapture(e.pointerId)
|
||||||
|
|
||||||
if (e.button === 0) {
|
|
||||||
const info = inputs.pointerDown(e, 'canvas')
|
const info = inputs.pointerDown(e, 'canvas')
|
||||||
|
|
||||||
|
if (e.button === 0) {
|
||||||
callbacks.onPointCanvas?.(info, e)
|
callbacks.onPointCanvas?.(info, e)
|
||||||
callbacks.onPointerDown?.(info, e)
|
callbacks.onPointerDown?.(info, e)
|
||||||
}
|
}
|
||||||
|
@ -22,11 +24,12 @@ export function useCanvasEvents() {
|
||||||
const onPointerMove = React.useCallback(
|
const onPointerMove = React.useCallback(
|
||||||
(e: React.PointerEvent) => {
|
(e: React.PointerEvent) => {
|
||||||
if (!inputs.pointerIsValid(e)) return
|
if (!inputs.pointerIsValid(e)) return
|
||||||
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
|
||||||
const info = inputs.pointerMove(e, 'canvas')
|
const info = inputs.pointerMove(e, 'canvas')
|
||||||
|
|
||||||
|
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
||||||
callbacks.onDragCanvas?.(info, e)
|
callbacks.onDragCanvas?.(info, e)
|
||||||
}
|
}
|
||||||
const info = inputs.pointerMove(e, 'canvas')
|
|
||||||
callbacks.onPointerMove?.(info, e)
|
callbacks.onPointerMove?.(info, e)
|
||||||
},
|
},
|
||||||
[callbacks, inputs]
|
[callbacks, inputs]
|
||||||
|
|
|
@ -25,7 +25,7 @@ export function usePosition(bounds: TLBounds, rotation = 0, zIndex = 0) {
|
||||||
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
|
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
|
||||||
)
|
)
|
||||||
|
|
||||||
elm.style.setProperty('z-index', zIndex + '')
|
// elm.style.setProperty('z-index', zIndex + '')
|
||||||
}, [bounds, rotation])
|
}, [bounds, rotation])
|
||||||
|
|
||||||
return rBounds
|
return rBounds
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
|
||||||
export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>): void {
|
export function usePreventNavigation(
|
||||||
|
rCanvas: React.RefObject<HTMLDivElement>,
|
||||||
|
width: number
|
||||||
|
): void {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const preventGestureNavigation = (event: TouchEvent) => {
|
const preventGestureNavigation = (event: TouchEvent) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@ -17,10 +20,7 @@ export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>):
|
||||||
// if the touch area overlaps with the screen edges
|
// if the touch area overlaps with the screen edges
|
||||||
// it's likely to trigger the navigation. We prevent the
|
// it's likely to trigger the navigation. We prevent the
|
||||||
// touchstart event in that case.
|
// touchstart event in that case.
|
||||||
if (
|
if (touchXPosition - touchXRadius < 10 || touchXPosition + touchXRadius > width - 10) {
|
||||||
touchXPosition - touchXRadius < 10 ||
|
|
||||||
touchXPosition + touchXRadius > window.innerWidth - 10
|
|
||||||
) {
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,5 +56,5 @@ export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>):
|
||||||
elm.removeEventListener('touchstart', preventNavigation)
|
elm.removeEventListener('touchstart', preventNavigation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [rCanvas])
|
}, [rCanvas, width])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as React from 'react'
|
||||||
import type { TLPage, TLPageState, TLShape, TLBounds, TLShapeUtils, TLBinding } from '+types'
|
import type { TLPage, TLPageState, TLShape, TLBounds, TLShapeUtils, TLBinding } from '+types'
|
||||||
import Utils from '+utils'
|
import Utils from '+utils'
|
||||||
import { useTLContext } from '+hooks'
|
import { useTLContext } from '+hooks'
|
||||||
|
@ -13,6 +14,7 @@ export function useSelection<T extends TLShape, E extends Element>(
|
||||||
) {
|
) {
|
||||||
const { rSelectionBounds } = useTLContext()
|
const { rSelectionBounds } = useTLContext()
|
||||||
const { selectedIds } = pageState
|
const { selectedIds } = pageState
|
||||||
|
const rPrevBounds = React.useRef<TLBounds>()
|
||||||
|
|
||||||
let bounds: TLBounds | undefined = undefined
|
let bounds: TLBounds | undefined = undefined
|
||||||
let rotation = 0
|
let rotation = 0
|
||||||
|
@ -62,5 +64,20 @@ export function useSelection<T extends TLShape, E extends Element>(
|
||||||
rSelectionBounds.current = null
|
rSelectionBounds.current = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const prevBounds = rPrevBounds.current
|
||||||
|
|
||||||
|
if (!prevBounds || !bounds) {
|
||||||
|
rPrevBounds.current = bounds
|
||||||
|
} else if (bounds) {
|
||||||
|
if (
|
||||||
|
prevBounds.minX === bounds.minX &&
|
||||||
|
prevBounds.minY === bounds.minY &&
|
||||||
|
prevBounds.maxX === bounds.maxX &&
|
||||||
|
prevBounds.maxY === bounds.maxY
|
||||||
|
) {
|
||||||
|
bounds = rPrevBounds.current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { bounds, rotation, isLocked }
|
return { bounds, rotation, isLocked }
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,19 +135,20 @@ const tlcss = css`
|
||||||
|
|
||||||
.tl-canvas {
|
.tl-canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
|
overflow: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-layer {
|
.tl-layer {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 0;
|
top: 0px;
|
||||||
left: 0;
|
left: 0px;
|
||||||
height: 0;
|
height: 0px;
|
||||||
width: 0;
|
width: 0px;
|
||||||
|
contain: layout style size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-absolute {
|
.tl-absolute {
|
||||||
|
@ -166,13 +167,14 @@ const tlcss = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: clip;
|
overflow: hidden;
|
||||||
|
contain: layout style size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-positioned-svg {
|
.tl-positioned-svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: clip;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-positioned-div {
|
.tl-positioned-div {
|
||||||
|
@ -181,7 +183,7 @@ const tlcss = css`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: var(--tl-padding);
|
padding: var(--tl-padding);
|
||||||
overflow: clip;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-counter-scaled {
|
.tl-counter-scaled {
|
||||||
|
@ -263,6 +265,14 @@ const tlcss = css`
|
||||||
|
|
||||||
.tl-bounds {
|
.tl-bounds {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
contain: layout style size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tl-bounds-bg {
|
||||||
|
stroke: none;
|
||||||
|
fill: var(--tl-selectFill);
|
||||||
|
pointer-events: all;
|
||||||
|
contain: layout style size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-bounds-center {
|
.tl-bounds-center {
|
||||||
|
@ -271,17 +281,12 @@ const tlcss = css`
|
||||||
stroke-width: calc(1.5px * var(--tl-scale));
|
stroke-width: calc(1.5px * var(--tl-scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-bounds-bg {
|
|
||||||
stroke: none;
|
|
||||||
fill: var(--tl-selectFill);
|
|
||||||
pointer-events: all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tl-brush {
|
.tl-brush {
|
||||||
fill: var(--tl-brushFill);
|
fill: var(--tl-brushFill);
|
||||||
stroke: var(--tl-brushStroke);
|
stroke: var(--tl-brushStroke);
|
||||||
stroke-width: calc(1px * var(--tl-scale));
|
stroke-width: calc(1px * var(--tl-scale));
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
contain: layout style size;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-dot {
|
.tl-dot {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTLContext } from './useTLContext'
|
import { useTLContext } from './useTLContext'
|
||||||
import { useGesture } from '@use-gesture/react'
|
import { useGesture, usePinch, useWheel } from '@use-gesture/react'
|
||||||
import { Vec } from '@tldraw/vec'
|
import { Vec } from '@tldraw/vec'
|
||||||
|
|
||||||
// Capture zoom gestures (pinches, wheels and pans)
|
// Capture zoom gestures (pinches, wheels and pans)
|
||||||
|
@ -31,22 +31,38 @@ export function useZoomEvents<T extends HTMLElement>(zoom: number, ref: React.Re
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useGesture(
|
React.useEffect(() => {
|
||||||
{
|
|
||||||
onWheel: ({ event: e, delta }) => {
|
|
||||||
const elm = ref.current
|
const elm = ref.current
|
||||||
|
|
||||||
if (!elm || !(e.target === elm || elm.contains(e.target as Node))) return
|
function handleWheel(e: WheelEvent) {
|
||||||
|
if (e.altKey) {
|
||||||
|
const point = inputs.pointer?.point ?? [inputs.bounds.width / 2, inputs.bounds.height / 2]
|
||||||
|
|
||||||
|
const info = inputs.pinch(point, point)
|
||||||
|
|
||||||
|
callbacks.onZoom?.({ ...info, delta: [...point, e.deltaY] }, e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (inputs.isPinching) return
|
if (inputs.isPinching) return
|
||||||
|
|
||||||
if (Vec.isEqual(delta, [0, 0])) return
|
if (Vec.isEqual([e.deltaX, e.deltaY], [0, 0])) return
|
||||||
|
|
||||||
|
const info = inputs.pan([e.deltaX, e.deltaY], e as WheelEvent)
|
||||||
|
|
||||||
const info = inputs.pan(delta, e as WheelEvent)
|
|
||||||
callbacks.onPan?.(info, e)
|
callbacks.onPan?.(info, e)
|
||||||
},
|
}
|
||||||
|
|
||||||
|
elm?.addEventListener('wheel', handleWheel, { passive: false })
|
||||||
|
return () => {
|
||||||
|
elm?.removeEventListener('wheel', handleWheel)
|
||||||
|
}
|
||||||
|
}, [ref, callbacks, inputs])
|
||||||
|
|
||||||
|
useGesture(
|
||||||
|
{
|
||||||
onPinchStart: ({ origin, event }) => {
|
onPinchStart: ({ origin, event }) => {
|
||||||
const elm = ref.current
|
const elm = ref.current
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -84,6 +85,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -116,6 +118,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -141,6 +144,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -167,6 +171,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -195,6 +200,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -225,6 +231,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -248,6 +255,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey,
|
metaKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -275,6 +283,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey,
|
metaKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
@ -348,6 +357,7 @@ export class Inputs {
|
||||||
ctrlKey,
|
ctrlKey,
|
||||||
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
|
||||||
altKey,
|
altKey,
|
||||||
|
spaceKey: this.keys[' '],
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer = info
|
this.pointer = info
|
||||||
|
|
|
@ -265,6 +265,7 @@ export interface TLPointerInfo<T extends string = string> {
|
||||||
ctrlKey: boolean
|
ctrlKey: boolean
|
||||||
metaKey: boolean
|
metaKey: boolean
|
||||||
altKey: boolean
|
altKey: boolean
|
||||||
|
spaceKey: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TLKeyboardInfo {
|
export interface TLKeyboardInfo {
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default function Editor(props: TLDrawProps): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tldraw">
|
<div className="tldraw">
|
||||||
<TLDraw id="tldraw" {...props} onMount={handleMount} autofocus />
|
<TLDraw id="tldraw1" {...props} onMount={handleMount} autofocus />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ html,
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
position: fixed;
|
||||||
|
overflow: hidden;
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|
|
@ -176,7 +176,7 @@ function InnerTldraw({
|
||||||
|
|
||||||
// Hide indicators when not using the select tool, or when in session
|
// Hide indicators when not using the select tool, or when in session
|
||||||
const hideIndicators =
|
const hideIndicators =
|
||||||
(isInSession && tlstate.appState.status.current !== TLDrawStatus.Brushing) || !isSelecting
|
(isInSession && tlstate.appState.status !== TLDrawStatus.Brushing) || !isSelecting
|
||||||
|
|
||||||
// Custom rendering meta, with dark mode for shapes
|
// Custom rendering meta, with dark mode for shapes
|
||||||
const meta = React.useMemo(() => ({ isDarkMode }), [isDarkMode])
|
const meta = React.useMemo(() => ({ isDarkMode }), [isDarkMode])
|
||||||
|
@ -215,7 +215,7 @@ function InnerTldraw({
|
||||||
<div ref={rWrapper} tabIndex={0}>
|
<div ref={rWrapper} tabIndex={0}>
|
||||||
<div className={layout()}>
|
<div className={layout()}>
|
||||||
<OneOff focusableRef={rWrapper} autofocus={autofocus} />
|
<OneOff focusableRef={rWrapper} autofocus={autofocus} />
|
||||||
<ContextMenu>
|
{/* <ContextMenu> */}
|
||||||
<Renderer
|
<Renderer
|
||||||
id={id}
|
id={id}
|
||||||
containerRef={rWrapper}
|
containerRef={rWrapper}
|
||||||
|
@ -278,7 +278,8 @@ function InnerTldraw({
|
||||||
onKeyDown={tlstate.onKeyDown}
|
onKeyDown={tlstate.onKeyDown}
|
||||||
onKeyUp={tlstate.onKeyUp}
|
onKeyUp={tlstate.onKeyUp}
|
||||||
/>
|
/>
|
||||||
</ContextMenu>
|
{/* </ContextMenu> */}
|
||||||
|
<div className={ui()}>
|
||||||
{isFocusMode ? (
|
{isFocusMode ? (
|
||||||
<div className={unfocusButton()}>
|
<div className={unfocusButton()}>
|
||||||
<button className={iconButton({ bp: breakpoints })} onClick={tlstate.toggleFocusMode}>
|
<button className={iconButton({ bp: breakpoints })} onClick={tlstate.toggleFocusMode}>
|
||||||
|
@ -298,6 +299,7 @@ function InnerTldraw({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,28 +333,33 @@ const layout = css({
|
||||||
maxHeight: '100%',
|
maxHeight: '100%',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
padding: '8px 8px 0 8px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
pointerEvents: 'none',
|
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
zIndex: 1,
|
|
||||||
border: '1px solid $blurred',
|
|
||||||
|
|
||||||
'&:focus': {
|
|
||||||
border: '1px solid $focused',
|
|
||||||
},
|
|
||||||
|
|
||||||
'& > *': {
|
|
||||||
pointerEvents: 'all',
|
|
||||||
},
|
|
||||||
|
|
||||||
'& .tl-container': {
|
'& .tl-container': {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const ui = css({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
padding: '8px 8px 0 8px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
zIndex: 2,
|
||||||
|
'& > *': {
|
||||||
|
pointerEvents: 'all',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ const pointsBoundsCache = new WeakMap<DrawShape['points'], TLBounds>([])
|
||||||
const shapeBoundsCache = new Map<string, TLBounds>()
|
const shapeBoundsCache = new Map<string, TLBounds>()
|
||||||
const rotatedCache = new WeakMap<DrawShape, number[][]>([])
|
const rotatedCache = new WeakMap<DrawShape, number[][]>([])
|
||||||
const pointCache: Record<string, number[]> = {}
|
const pointCache: Record<string, number[]> = {}
|
||||||
|
|
||||||
export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
|
export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
|
||||||
type: TLDrawShapeType.Draw,
|
type: TLDrawShapeType.Draw,
|
||||||
|
|
||||||
|
@ -139,6 +140,8 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
|
||||||
return getSolidStrokePathData(shape, false)
|
return getSolidStrokePathData(shape, false)
|
||||||
}, [points])
|
}, [points])
|
||||||
|
|
||||||
|
if (!shape) return null
|
||||||
|
|
||||||
const bounds = this.getBounds(shape)
|
const bounds = this.getBounds(shape)
|
||||||
|
|
||||||
const verySmall = bounds.width < 4 && bounds.height < 4
|
const verySmall = bounds.width < 4 && bounds.height < 4
|
||||||
|
@ -151,6 +154,8 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
|
||||||
},
|
},
|
||||||
|
|
||||||
getBounds(shape: DrawShape): TLBounds {
|
getBounds(shape: DrawShape): TLBounds {
|
||||||
|
// return Utils.translateBounds(Utils.getBoundsFromPoints(shape.points), shape.point)
|
||||||
|
|
||||||
// The goal here is to avoid recalculating the bounds from the
|
// The goal here is to avoid recalculating the bounds from the
|
||||||
// points array, which is expensive. However, we still need a
|
// points array, which is expensive. However, we still need a
|
||||||
// new bounds if the point has changed, but we will reuse the
|
// new bounds if the point has changed, but we will reuse the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue