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.

page.tsx 1.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import { getShapeUtils } from 'lib/shape-utils'
  2. import state, { useSelector } from 'state'
  3. import { Bounds, GroupShape, PageState } from 'types'
  4. import { boundsCollide, boundsContain } from 'utils/bounds'
  5. import {
  6. deepCompareArrays,
  7. getPage,
  8. getViewport,
  9. screenToWorld,
  10. } from 'utils/utils'
  11. import Shape from './shape'
  12. /*
  13. On each state change, compare node ids of all shapes
  14. on the current page. Kind of expensive but only happens
  15. here; and still cheaper than any other pattern I've found.
  16. */
  17. const noOffset = [0, 0]
  18. const viewportCache = new WeakMap<PageState, Bounds>()
  19. export default function Page() {
  20. const currentPageShapeIds = useSelector((s) => {
  21. const page = getPage(s.data)
  22. const pageState = s.data.pageStates[page.id]
  23. if (!viewportCache.has(pageState)) {
  24. const viewport = getViewport(s.data)
  25. viewportCache.set(pageState, viewport)
  26. }
  27. const viewport = viewportCache.get(pageState)
  28. return s.values.currentShapes
  29. .filter((shape) => {
  30. const shapeBounds = getShapeUtils(shape).getBounds(shape)
  31. return (
  32. boundsContain(viewport, shapeBounds) ||
  33. boundsCollide(viewport, shapeBounds)
  34. )
  35. })
  36. .map((shape) => shape.id)
  37. }, deepCompareArrays)
  38. const isSelecting = useSelector((s) => s.isIn('selecting'))
  39. return (
  40. <g pointerEvents={isSelecting ? 'all' : 'none'}>
  41. {currentPageShapeIds.map((shapeId) => (
  42. <Shape
  43. key={shapeId}
  44. id={shapeId}
  45. isSelecting={isSelecting}
  46. parentPoint={noOffset}
  47. />
  48. ))}
  49. </g>
  50. )
  51. }