| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 | import * as React from 'react'import { TLSelectionHandle, TLTargetType } from '@tldraw/core'import { DOUBLE_CLICK_DURATION } from '../constants'import type { TLReactCustomEvents } from '../types'import { useRendererContext } from './useRendererContext'export function useBoundsEvents(handle: TLSelectionHandle) {  const { callbacks } = useRendererContext()  const rDoubleClickTimer = React.useRef<number>(-1)  const events = React.useMemo(() => {    const onPointerMove: TLReactCustomEvents['pointer'] = e => {      const { order = 0 } = e      if (order) return      callbacks.onPointerMove?.({ type: TLTargetType.Selection, handle, order }, e)      e.order = order + 1    }    const onPointerDown: TLReactCustomEvents['pointer'] = e => {      const { order = 0 } = e      if (order) return      // See note at bottom of the file      const elm = loopToHtmlElement(e.currentTarget)      elm.setPointerCapture(e.pointerId)      elm.addEventListener('pointerup', onPointerUp)      callbacks.onPointerDown?.({ type: TLTargetType.Selection, handle, order }, e)      e.order = order + 1    }    const onPointerUp = (e: PointerEvent & { order?: number }) => {      const { order = 0 } = e      if (order) return      const elm = e.target as HTMLElement      elm.removeEventListener('pointerup', onPointerUp)      elm.releasePointerCapture(e.pointerId)      callbacks.onPointerUp?.({ type: TLTargetType.Selection, handle, order }, e)      const now = Date.now()      const elapsed = now - rDoubleClickTimer.current      if (elapsed > DOUBLE_CLICK_DURATION) {        rDoubleClickTimer.current = now      } else {        if (elapsed <= DOUBLE_CLICK_DURATION) {          callbacks.onDoubleClick?.({ type: TLTargetType.Selection, handle, order }, e)          rDoubleClickTimer.current = -1        }      }      e.order = order + 1    }    const onPointerEnter: TLReactCustomEvents['pointer'] = e => {      const { order = 0 } = e      if (order) return      callbacks.onPointerEnter?.({ type: TLTargetType.Selection, handle, order }, e)      e.order = order + 1    }    const onPointerLeave: TLReactCustomEvents['pointer'] = e => {      const { order = 0 } = e      if (order) return      callbacks.onPointerLeave?.({ type: TLTargetType.Selection, handle, order }, e)      e.order = order + 1    }    const onKeyDown: TLReactCustomEvents['keyboard'] = e => {      callbacks.onKeyDown?.({ type: TLTargetType.Selection, handle, order: -1 }, e)    }    const onKeyUp: TLReactCustomEvents['keyboard'] = e => {      callbacks.onKeyUp?.({ type: TLTargetType.Selection, handle, order: -1 }, e)    }    return {      onPointerDown,      onPointerMove,      // onPointerUp,      onPointerEnter,      onPointerLeave,      onKeyDown,      onKeyUp,    }  }, [callbacks])  return events}function loopToHtmlElement(elm: Element): HTMLElement {  if (elm.namespaceURI?.endsWith('svg')) {    if (elm.parentElement) return loopToHtmlElement(elm.parentElement)    else throw Error('Could not find a parent element of an HTML type!')  }  return elm as HTMLElement}/*There are a few hacks here in facilitate double clicking and pointercapture on elements.The events in this file are possibly set on individual SVG elements,such as handles or corner handles, rather than on HTML elements orSVGSVGElements. Raw SVG elements do not support pointerCapture inmost cases, meaning that in order for pointer capture to work, weneed to crawl up the DOM tree to find the nearest HTML element. Then,in order for that element to also call the `onPointerUp` event fromthis file, we need to manually set that event on that element andlater remove it when the pointerup occurs. This is a potential leakif the user clicks on a handle but the pointerup does not fire forwhatever reason.*/
 |