ContextBarContainer.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import * as React from 'react'
  2. import { observer } from 'mobx-react-lite'
  3. import { TLBounds, BoundsUtils, TLOffset } from '@tldraw/core'
  4. import { useCounterScaledPosition, useRendererContext } from '~hooks'
  5. import type { TLReactShape } from '~lib'
  6. const stopEventPropagation = (e: React.PointerEvent) => e.stopPropagation()
  7. export interface TLContextBarContainerProps<S extends TLReactShape> {
  8. shapes: S[]
  9. hidden: boolean
  10. bounds: TLBounds
  11. rotation?: number
  12. }
  13. export const ContextBarContainer = observer(function ContextBarContainer<S extends TLReactShape>({
  14. shapes,
  15. hidden,
  16. bounds,
  17. rotation = 0,
  18. }: TLContextBarContainerProps<S>) {
  19. const {
  20. components: { ContextBar },
  21. viewport: {
  22. bounds: vpBounds,
  23. camera: {
  24. point: [x, y],
  25. zoom,
  26. },
  27. },
  28. } = useRendererContext()
  29. const rBounds = React.useRef<HTMLDivElement>(null)
  30. const rotatedBounds = BoundsUtils.getRotatedBounds(bounds, rotation)
  31. const scaledBounds = BoundsUtils.multiplyBounds(rotatedBounds, zoom)
  32. useCounterScaledPosition(rBounds, bounds, rotation, 10003)
  33. if (!ContextBar) throw Error('Expected a ContextBar component.')
  34. const screenBounds = BoundsUtils.translateBounds(scaledBounds, [x * zoom, y * zoom])
  35. const offsets: TLOffset = {
  36. left: screenBounds.minX,
  37. right: vpBounds.width - screenBounds.maxX,
  38. top: screenBounds.minY,
  39. bottom: vpBounds.height - screenBounds.maxY,
  40. width: screenBounds.width,
  41. height: screenBounds.height,
  42. }
  43. const inView =
  44. BoundsUtils.boundsContain(vpBounds, screenBounds) ||
  45. BoundsUtils.boundsCollide(vpBounds, screenBounds)
  46. React.useLayoutEffect(() => {
  47. const elm = rBounds.current
  48. if (!elm) return
  49. if (hidden || !inView) {
  50. elm.classList.add('tl-fade-out')
  51. elm.classList.remove('tl-fade-in')
  52. } else {
  53. elm.classList.add('tl-fade-in')
  54. elm.classList.remove('tl-fade-out')
  55. }
  56. }, [hidden, inView])
  57. return (
  58. <div
  59. ref={rBounds}
  60. className="tl-counter-scaled-positioned tl-fade-out"
  61. aria-label="context-bar-container"
  62. onPointerMove={stopEventPropagation}
  63. onPointerUp={stopEventPropagation}
  64. onPointerDown={stopEventPropagation}
  65. >
  66. <ContextBar
  67. hidden={hidden}
  68. shapes={shapes}
  69. bounds={bounds}
  70. offsets={offsets}
  71. scaledBounds={scaledBounds}
  72. rotation={rotation}
  73. />
  74. </div>
  75. )
  76. })