Makes keyboard events work again (#114)

This commit is contained in:
Steve Ruiz 2021-09-23 12:14:22 +01:00 committed by GitHub
parent 48f784c322
commit fc1dde724e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 108 additions and 39 deletions

View file

@ -6,6 +6,7 @@ import {
useSafariFocusOutFix,
useCanvasEvents,
useCameraCss,
useKeyEvents,
} from '+hooks'
import type { TLBinding, TLPage, TLPageState, TLShape } from '+types'
import { ErrorFallback } from '+components/error-fallback'
@ -57,6 +58,8 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
e.currentTarget.scrollTo(0, 0)
}, [])
useKeyEvents()
return (
<div id={id} className="tl-container" ref={rContainer}>
<div

View file

@ -14,3 +14,4 @@ export * from './useHandles'
export * from './usePreventNavigation'
export * from './useBoundsEvents'
export * from './usePosition'
export * from './useKeyEvents'

View file

@ -0,0 +1,25 @@
import { useTLContext } from '+hooks'
import * as React from 'react'
export function useKeyEvents() {
const { inputs, callbacks } = useTLContext()
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
callbacks.onKeyDown?.(e.key, inputs.keydown(e), e)
}
const handleKeyUp = (e: KeyboardEvent) => {
inputs.keyup(e)
callbacks.onKeyUp?.(e.key, inputs.keyup(e), e)
}
window.addEventListener('keydown', handleKeyDown)
window.addEventListener('keyup', handleKeyUp)
return () => {
window.removeEventListener('keydown', handleKeyDown)
window.removeEventListener('keyup', handleKeyUp)
}
}, [inputs, callbacks])
}

View file

@ -133,6 +133,8 @@ export type TLShapeChangeHandler<T, K = any> = (
export type TLShapeBlurHandler<K = any> = (info?: K) => void
export type TLKeyboardEventHandler = (key: string, info: TLKeyboardInfo, e: KeyboardEvent) => void
export type TLPointerEventHandler = (info: TLPointerInfo<string>, e: React.PointerEvent) => void
export type TLCanvasEventHandler = (info: TLPointerInfo<'canvas'>, e: React.PointerEvent) => void
@ -206,6 +208,10 @@ export interface TLCallbacks<T extends TLShape> {
onRenderCountChange: (ids: string[]) => void
onError: (error: Error) => void
onBoundsChange: (bounds: TLBounds) => void
// Keyboard event handlers
onKeyDown: TLKeyboardEventHandler
onKeyUp: TLKeyboardEventHandler
}
export interface TLBounds {

View file

@ -240,6 +240,8 @@ function InnerTldraw({
onShapeChange={tlstate.onShapeChange}
onShapeBlur={tlstate.onShapeBlur}
onBoundsChange={tlstate.updateBounds}
onKeyDown={tlstate.onKeyDown}
onKeyUp={tlstate.onKeyUp}
/>
</ContextMenu>
{isFocusMode ? (

View file

@ -11,24 +11,6 @@ export function useKeyboardShortcuts(ref: React.RefObject<HTMLDivElement>) {
return elm && (document.activeElement === elm || elm.contains(document.activeElement))
}, [ref])
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (canHandleEvent()) tlstate.onKeyDown(e.key)
}
const handleKeyUp = (e: KeyboardEvent) => {
if (canHandleEvent()) tlstate.onKeyUp(e.key)
}
window.addEventListener('keydown', handleKeyDown)
window.addEventListener('keyup', handleKeyUp)
return () => {
window.removeEventListener('keydown', handleKeyDown)
window.removeEventListener('keyup', handleKeyUp)
}
}, [tlstate])
/* ---------------------- Tools --------------------- */
useHotkeys(

View file

@ -61,6 +61,53 @@ describe('Rotate session', () => {
it.todo('rotates handles only on shapes with handles')
describe('when rotating a single shape while pressing shift', () => {
it('Clamps rotation to 15 degrees', () => {
const tlstate = new TLDrawState()
.loadDocument(mockDocument)
.select('rect1')
.startTransformSession([0, 0], 'rotate')
.updateTransformSession([20, 10], true)
.completeSession()
expect(Math.round((tlstate.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual(
0
)
})
it('Clamps rotation to 15 degrees when starting from a rotation', () => {
// Rect 1 is a little rotated
const tlstate = new TLDrawState()
.loadDocument(mockDocument)
.select('rect1')
.startTransformSession([0, 0], 'rotate')
.updateTransformSession([5, 5])
.completeSession()
// Rect 1 clamp rotated, starting from a little rotation
tlstate
.select('rect1')
.startTransformSession([0, 0], 'rotate')
.updateTransformSession([100, 200], true)
.completeSession()
expect(Math.round((tlstate.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual(
0
)
// Try again, too.
tlstate
.select('rect1')
.startTransformSession([0, 0], 'rotate')
.updateTransformSession([-100, 5000], true)
.completeSession()
expect(Math.round((tlstate.getShape('rect1').rotation || 0) * (180 / Math.PI)) % 15).toEqual(
0
)
})
})
describe('when rotating multiple shapes', () => {
it('keeps the center', () => {
tlstate.loadDocument(mockDocument).select('rect1', 'rect2')

View file

@ -30,19 +30,28 @@ export class RotateSession implements Session {
const shapes: Record<string, Partial<TLDrawShape>> = {}
const nextDirection = Vec.angle(commonBoundsCenter, point) - this.initialAngle
let nextBoundsRotation = this.snapshot.boundsRotation + nextDirection
let directionDelta = Vec.angle(commonBoundsCenter, point) - this.initialAngle
if (isLocked) {
nextBoundsRotation = Utils.snapAngleToSegments(nextBoundsRotation, 24)
directionDelta = Utils.snapAngleToSegments(directionDelta, 24) // 15 degrees
}
const delta = nextBoundsRotation - this.snapshot.boundsRotation
// Update the shapes
initialShapes.forEach(({ id, center, shape }) => {
const change = TLDR.getRotatedShapeMutation(shape, center, commonBoundsCenter, delta)
const { rotation = 0 } = shape
let shapeDelta = 0
if (isLocked) {
const snappedRotation = Utils.snapAngleToSegments(rotation, 24)
shapeDelta = snappedRotation - rotation
}
const change = TLDR.getRotatedShapeMutation(
shape,
center,
commonBoundsCenter,
isLocked ? directionDelta + shapeDelta : directionDelta
)
if (change) {
shapes[id] = change
@ -51,6 +60,8 @@ export class RotateSession implements Session {
this.changes = shapes
const nextBoundsRotation = this.snapshot.boundsRotation + directionDelta
return {
document: {
pages: {

View file

@ -5,6 +5,7 @@ import {
TLBoundsEdge,
TLBoundsEventHandler,
TLBoundsHandleEventHandler,
TLKeyboardEventHandler,
TLCanvasEventHandler,
TLPageState,
TLPinchEventHandler,
@ -361,10 +362,6 @@ export class TLDrawState extends StateManager<Data> {
)
}
handleMount = (inputs: Inputs): void => {
this.inputs = inputs
}
/**
* Update the bounding box when the renderer's bounds change.
* @param bounds
@ -2261,16 +2258,14 @@ export class TLDrawState extends StateManager<Data> {
/* ----------------- Keyboard Events ---------------- */
onKeyDown = (key: string) => {
const info = this.inputs?.pointer
if (!info) return
onKeyDown: TLKeyboardEventHandler = (key, info) => {
if (key === 'Escape') {
this.cancel()
return
}
if (!info) return
switch (this.appState.status.current) {
case TLDrawStatus.Idle: {
break
@ -2296,9 +2291,7 @@ export class TLDrawState extends StateManager<Data> {
case TLDrawStatus.Transforming: {
if (key === 'Escape') {
this.cancelSession(this.getPagePoint(info.point))
}
if (key === 'Shift' || key === 'Alt') {
} else if (key === 'Shift' || key === 'Alt') {
this.updateTransformSession(this.getPagePoint(info.point), info.shiftKey, info.altKey)
}
break
@ -2321,8 +2314,7 @@ export class TLDrawState extends StateManager<Data> {
}
}
onKeyUp = (key: string) => {
const info = this.inputs?.pointer
onKeyUp: TLKeyboardEventHandler = (key, info) => {
if (!info) return
switch (this.appState.status.current) {