Adds provisional keyboard panning
This commit is contained in:
parent
e5408219de
commit
7da573ffdb
10 changed files with 79 additions and 40 deletions
|
@ -1,15 +1,16 @@
|
||||||
import Cursor from './cursor'
|
import Cursor from './cursor'
|
||||||
import { useCoopSelector } from 'state/coop/coop-state'
|
import { useCoopSelector } from 'state/coop/coop-state'
|
||||||
|
import { useSelector } from 'state'
|
||||||
|
|
||||||
export default function Presence(): JSX.Element {
|
export default function Presence(): JSX.Element {
|
||||||
const others = useCoopSelector((s) => s.data.others)
|
const others = useCoopSelector((s) => s.data.others)
|
||||||
|
const currentPageId = useSelector((s) => s.data.currentPageId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.values(others).map(({ connectionId, presence }) => {
|
{Object.values(others).map(({ connectionId, presence }) => {
|
||||||
if (presence == null) {
|
if (presence === null) return null
|
||||||
return null
|
if (presence.pageId !== currentPageId) return null
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Cursor
|
<Cursor
|
||||||
|
|
|
@ -2,12 +2,6 @@ import React from 'react'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
// const transition = {
|
|
||||||
// type: 'spring',
|
|
||||||
// mass: 2,
|
|
||||||
// damping: 20,
|
|
||||||
// }
|
|
||||||
|
|
||||||
export default function Cursor({
|
export default function Cursor({
|
||||||
color = 'dodgerblue',
|
color = 'dodgerblue',
|
||||||
duration = 0,
|
duration = 0,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
fastTranslate,
|
fastTranslate,
|
||||||
} from 'state/hacks'
|
} from 'state/hacks'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
|
import Vec from 'utils/vec'
|
||||||
|
|
||||||
export default function useCanvasEvents(
|
export default function useCanvasEvents(
|
||||||
rCanvas: MutableRefObject<SVGGElement>
|
rCanvas: MutableRefObject<SVGGElement>
|
||||||
|
@ -27,8 +28,14 @@ export default function useCanvasEvents(
|
||||||
const handlePointerMove = useCallback((e: React.PointerEvent) => {
|
const handlePointerMove = useCallback((e: React.PointerEvent) => {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
|
|
||||||
|
const prev = inputs.pointer?.point
|
||||||
const info = inputs.pointerMove(e)
|
const info = inputs.pointerMove(e)
|
||||||
|
|
||||||
|
if (prev && state.isIn('selecting') && inputs.keys[' ']) {
|
||||||
|
state.send('KEYBOARD_PANNED_CAMERA', { delta: Vec.sub(prev, info.point) })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (state.isIn('draw.editing')) {
|
if (state.isIn('draw.editing')) {
|
||||||
fastDrawUpdate(info)
|
fastDrawUpdate(info)
|
||||||
} else if (state.isIn('brushSelecting')) {
|
} else if (state.isIn('brushSelecting')) {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
|
import inputs from 'state/inputs'
|
||||||
import { MoveType } from 'types'
|
import { MoveType } from 'types'
|
||||||
import { getKeyboardEventInfo, metaKey } from 'utils'
|
import { metaKey } from 'utils'
|
||||||
|
|
||||||
export default function useKeyboardEvents() {
|
export default function useKeyboardEvents() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -24,7 +25,7 @@ export default function useKeyboardEvents() {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
const info = getKeyboardEventInfo(e)
|
const info = inputs.keydown(e)
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'ArrowUp': {
|
case 'ArrowUp': {
|
||||||
|
@ -269,7 +270,7 @@ export default function useKeyboardEvents() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyUp(e: KeyboardEvent) {
|
function handleKeyUp(e: KeyboardEvent) {
|
||||||
const info = getKeyboardEventInfo(e)
|
const info = inputs.keyup(e)
|
||||||
|
|
||||||
if (e.key === 'Shift') {
|
if (e.key === 'Shift') {
|
||||||
state.send('RELEASED_SHIFT_KEY', info)
|
state.send('RELEASED_SHIFT_KEY', info)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import React, { MutableRefObject, useCallback } from 'react'
|
import React, { MutableRefObject, useCallback } from 'react'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
|
import Vec from 'utils/vec'
|
||||||
|
|
||||||
export default function useShapeEvents(
|
export default function useShapeEvents(
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -57,6 +58,20 @@ export default function useShapeEvents(
|
||||||
(e: React.PointerEvent) => {
|
(e: React.PointerEvent) => {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
|
|
||||||
|
const prev = inputs.pointer?.point
|
||||||
|
const info = inputs.pointerMove(e)
|
||||||
|
|
||||||
|
if (prev && state.isIn('selecting') && inputs.keys[' ']) {
|
||||||
|
if (!e.currentTarget.hasPointerCapture(e.pointerId)) {
|
||||||
|
e.currentTarget.setPointerCapture(e.pointerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.send('KEYBOARD_PANNED_CAMERA', {
|
||||||
|
delta: Vec.sub(prev, info.point),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
state.send('MOVED_OVER_GROUP', inputs.pointerEnter(e, id))
|
state.send('MOVED_OVER_GROUP', inputs.pointerEnter(e, id))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable prefer-const */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
import { Client, Room, createClient } from '@liveblocks/client'
|
import { Client, Room, createClient } from '@liveblocks/client'
|
||||||
import coopState from './coop-state'
|
import coopState from './coop-state'
|
||||||
import { CoopPresence } from 'types'
|
import { CoopPresence } from 'types'
|
||||||
|
@ -108,6 +106,7 @@ class CoopClient {
|
||||||
bufferedYs: this.bufferedYs,
|
bufferedYs: this.bufferedYs,
|
||||||
times,
|
times,
|
||||||
duration,
|
duration,
|
||||||
|
pageId,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Reset data for next update
|
// Reset data for next update
|
||||||
|
@ -118,6 +117,7 @@ class CoopClient {
|
||||||
elapsed = 0
|
elapsed = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the new point and time
|
||||||
this.bufferedXs.push(point[0])
|
this.bufferedXs.push(point[0])
|
||||||
this.bufferedYs.push(point[1])
|
this.bufferedYs.push(point[1])
|
||||||
this.bufferedTs.push(elapsed / 1000)
|
this.bufferedTs.push(elapsed / 1000)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { PointerInfo } from 'types'
|
import { KeyboardInfo, PointerInfo } from 'types'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import { isDarwin, getPoint } from 'utils'
|
import { isDarwin, getPoint } from 'utils'
|
||||||
|
|
||||||
|
@ -8,8 +8,12 @@ const DOUBLE_CLICK_DURATION = 250
|
||||||
class Inputs {
|
class Inputs {
|
||||||
activePointerId?: number
|
activePointerId?: number
|
||||||
pointerUpTime = 0
|
pointerUpTime = 0
|
||||||
points: Record<string, PointerInfo> = {}
|
|
||||||
pointer: PointerInfo
|
pointer: PointerInfo
|
||||||
|
points: Record<string, PointerInfo> = {}
|
||||||
|
|
||||||
|
keyboard: KeyboardInfo
|
||||||
|
keys: Record<string, boolean> = {}
|
||||||
|
|
||||||
touchStart(e: TouchEvent | React.TouchEvent, target: string) {
|
touchStart(e: TouchEvent | React.TouchEvent, target: string) {
|
||||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||||
|
@ -190,6 +194,36 @@ class Inputs {
|
||||||
resetDoubleClick() {
|
resetDoubleClick() {
|
||||||
this.pointerUpTime = 0
|
this.pointerUpTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keydown = (e: KeyboardEvent | React.KeyboardEvent): KeyboardInfo => {
|
||||||
|
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||||
|
|
||||||
|
this.keys[e.key] = true
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: e.key,
|
||||||
|
keys: Object.keys(this.keys),
|
||||||
|
shiftKey,
|
||||||
|
ctrlKey,
|
||||||
|
metaKey: isDarwin() ? metaKey : ctrlKey,
|
||||||
|
altKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyup = (e: KeyboardEvent | React.KeyboardEvent): KeyboardInfo => {
|
||||||
|
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
||||||
|
|
||||||
|
delete this.keys[e.key]
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: e.key,
|
||||||
|
keys: Object.keys(this.keys),
|
||||||
|
shiftKey,
|
||||||
|
ctrlKey,
|
||||||
|
metaKey: isDarwin() ? metaKey : ctrlKey,
|
||||||
|
altKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Inputs()
|
export default new Inputs()
|
||||||
|
|
|
@ -173,8 +173,6 @@ const state = createState({
|
||||||
// RT_CHANGED_STATUS: 'setRtStatus',
|
// RT_CHANGED_STATUS: 'setRtStatus',
|
||||||
// RT_DELETED_SHAPE: 'deleteRtShape',
|
// RT_DELETED_SHAPE: 'deleteRtShape',
|
||||||
// RT_EDITED_SHAPE: 'editRtShape',
|
// RT_EDITED_SHAPE: 'editRtShape',
|
||||||
// RT_MOVED_CURSOR: 'moveRtCursor',
|
|
||||||
// MOVED_POINTER: { secretlyDo: 'sendRtCursorMove' },
|
|
||||||
// Client
|
// Client
|
||||||
RESIZED_WINDOW: 'resetPageState',
|
RESIZED_WINDOW: 'resetPageState',
|
||||||
RESET_DOCUMENT_STATE: 'resetDocumentState',
|
RESET_DOCUMENT_STATE: 'resetDocumentState',
|
||||||
|
@ -334,6 +332,7 @@ const state = createState({
|
||||||
selecting: {
|
selecting: {
|
||||||
onEnter: ['setActiveToolSelect', 'clearInputs'],
|
onEnter: ['setActiveToolSelect', 'clearInputs'],
|
||||||
on: {
|
on: {
|
||||||
|
KEYBOARD_PANNED_CAMERA: 'panCamera',
|
||||||
STARTED_PINCHING: {
|
STARTED_PINCHING: {
|
||||||
unless: 'isInSession',
|
unless: 'isInSession',
|
||||||
to: 'pinching.selectPinching',
|
to: 'pinching.selectPinching',
|
||||||
|
|
10
types.ts
10
types.ts
|
@ -44,6 +44,7 @@ export type CoopPresence = {
|
||||||
bufferedYs: number[]
|
bufferedYs: number[]
|
||||||
times: number[]
|
times: number[]
|
||||||
duration: number
|
duration: number
|
||||||
|
pageId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TLDocument {
|
export interface TLDocument {
|
||||||
|
@ -286,6 +287,15 @@ export interface PointerInfo {
|
||||||
altKey: boolean
|
altKey: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface KeyboardInfo {
|
||||||
|
key: string
|
||||||
|
keys: string[]
|
||||||
|
shiftKey: boolean
|
||||||
|
ctrlKey: boolean
|
||||||
|
metaKey: boolean
|
||||||
|
altKey: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export enum Edge {
|
export enum Edge {
|
||||||
Top = 'top_edge',
|
Top = 'top_edge',
|
||||||
Right = 'right_edge',
|
Right = 'right_edge',
|
||||||
|
|
|
@ -1564,28 +1564,6 @@ export function metaKey(e: KeyboardEvent | React.KeyboardEvent): boolean {
|
||||||
return isDarwin() ? e.metaKey : e.ctrlKey
|
return isDarwin() ? e.metaKey : e.ctrlKey
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract info about a keyboard event.
|
|
||||||
* @param e
|
|
||||||
*/
|
|
||||||
export function getKeyboardEventInfo(e: KeyboardEvent | React.KeyboardEvent): {
|
|
||||||
key: string
|
|
||||||
shiftKey: boolean
|
|
||||||
ctrlKey: boolean
|
|
||||||
metaKey: boolean
|
|
||||||
altKey: boolean
|
|
||||||
} {
|
|
||||||
const { shiftKey, ctrlKey, metaKey, altKey } = e
|
|
||||||
|
|
||||||
return {
|
|
||||||
key: e.key,
|
|
||||||
shiftKey,
|
|
||||||
ctrlKey,
|
|
||||||
metaKey: isDarwin() ? metaKey : ctrlKey,
|
|
||||||
altKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the closest point on a SVG path to an off-path point.
|
* Find the closest point on a SVG path to an off-path point.
|
||||||
* @param pathNode
|
* @param pathNode
|
||||||
|
|
Loading…
Reference in a new issue