You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bounding-box.tsx 2.6KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import * as React from 'react'
  2. import { Edge, Corner, LineShape, ArrowShape } from 'types'
  3. import { useSelector } from 'state'
  4. import {
  5. deepCompareArrays,
  6. getPage,
  7. getSelectedShapes,
  8. isMobile,
  9. } from 'utils/utils'
  10. import CenterHandle from './center-handle'
  11. import CornerHandle from './corner-handle'
  12. import EdgeHandle from './edge-handle'
  13. import RotateHandle from './rotate-handle'
  14. import Handles from './handles'
  15. export default function Bounds() {
  16. const isBrushing = useSelector((s) => s.isIn('brushSelecting'))
  17. const isSelecting = useSelector((s) => s.isIn('selecting'))
  18. const zoom = useSelector((s) => s.data.camera.zoom)
  19. const bounds = useSelector((s) => s.values.selectedBounds)
  20. const selectedIds = useSelector(
  21. (s) => Array.from(s.values.selectedIds.values()),
  22. deepCompareArrays
  23. )
  24. const rotation = useSelector(({ data }) =>
  25. data.selectedIds.size === 1 ? getSelectedShapes(data)[0].rotation : 0
  26. )
  27. const isAllLocked = useSelector((s) => {
  28. const page = getPage(s.data)
  29. return selectedIds.every((id) => page.shapes[id]?.isLocked)
  30. })
  31. const isSingleHandles = useSelector((s) => {
  32. const page = getPage(s.data)
  33. return (
  34. selectedIds.length === 1 &&
  35. page.shapes[selectedIds[0]]?.handles !== undefined
  36. )
  37. })
  38. if (!bounds) return null
  39. if (!isSelecting) return null
  40. if (isSingleHandles) return null
  41. const size = (isMobile().any ? 10 : 8) / zoom // Touch target size
  42. return (
  43. <g
  44. pointerEvents={isBrushing ? 'none' : 'all'}
  45. transform={`
  46. rotate(${rotation * (180 / Math.PI)},
  47. ${(bounds.minX + bounds.maxX) / 2},
  48. ${(bounds.minY + bounds.maxY) / 2})
  49. translate(${bounds.minX},${bounds.minY})`}
  50. >
  51. <CenterHandle bounds={bounds} isLocked={isAllLocked} />
  52. {!isAllLocked && (
  53. <>
  54. <EdgeHandle size={size} bounds={bounds} edge={Edge.Top} />
  55. <EdgeHandle size={size} bounds={bounds} edge={Edge.Right} />
  56. <EdgeHandle size={size} bounds={bounds} edge={Edge.Bottom} />
  57. <EdgeHandle size={size} bounds={bounds} edge={Edge.Left} />
  58. <CornerHandle size={size} bounds={bounds} corner={Corner.TopLeft} />
  59. <CornerHandle size={size} bounds={bounds} corner={Corner.TopRight} />
  60. <CornerHandle
  61. size={size}
  62. bounds={bounds}
  63. corner={Corner.BottomRight}
  64. />
  65. <CornerHandle
  66. size={size}
  67. bounds={bounds}
  68. corner={Corner.BottomLeft}
  69. />
  70. <RotateHandle size={size} bounds={bounds} />
  71. </>
  72. )}
  73. </g>
  74. )
  75. }