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.

shape.tsx 2.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import React, { useCallback, useRef, memo } from "react"
  2. import state, { useSelector } from "state"
  3. import inputs from "state/inputs"
  4. import { getShapeUtils } from "lib/shapes"
  5. import styled from "styles"
  6. function Shape({ id }: { id: string }) {
  7. const rGroup = useRef<SVGGElement>(null)
  8. const isSelected = useSelector((state) => state.values.selectedIds.has(id))
  9. const shape = useSelector(
  10. ({ data }) => data.document.pages[data.currentPageId].shapes[id]
  11. )
  12. const handlePointerDown = useCallback(
  13. (e: React.PointerEvent) => {
  14. e.stopPropagation()
  15. rGroup.current.setPointerCapture(e.pointerId)
  16. state.send("POINTED_SHAPE", inputs.pointerDown(e, id))
  17. },
  18. [id]
  19. )
  20. const handlePointerUp = useCallback(
  21. (e: React.PointerEvent) => {
  22. e.stopPropagation()
  23. rGroup.current.releasePointerCapture(e.pointerId)
  24. state.send("STOPPED_POINTING", inputs.pointerUp(e))
  25. },
  26. [id]
  27. )
  28. const handlePointerEnter = useCallback(
  29. () => state.send("HOVERED_SHAPE", { id }),
  30. [id]
  31. )
  32. const handlePointerLeave = useCallback(
  33. () => state.send("UNHOVERED_SHAPE", { id }),
  34. [id]
  35. )
  36. return (
  37. <StyledGroup
  38. ref={rGroup}
  39. isSelected={isSelected}
  40. transform={`translate(${shape.point})`}
  41. onPointerDown={handlePointerDown}
  42. onPointerUp={handlePointerUp}
  43. onPointerEnter={handlePointerEnter}
  44. onPointerLeave={handlePointerLeave}
  45. >
  46. <defs>{getShapeUtils(shape).render(shape)}</defs>
  47. <HoverIndicator as="use" xlinkHref={"#" + id} />
  48. <use xlinkHref={"#" + id} {...shape.style} />
  49. <Indicator as="use" xlinkHref={"#" + id} />
  50. </StyledGroup>
  51. )
  52. }
  53. const Indicator = styled("path", {
  54. fill: "none",
  55. stroke: "transparent",
  56. zStrokeWidth: [1, 1],
  57. pointerEvents: "none",
  58. strokeLineCap: "round",
  59. strokeLinejoin: "round",
  60. })
  61. const HoverIndicator = styled("path", {
  62. fill: "none",
  63. stroke: "transparent",
  64. zStrokeWidth: [8, 4],
  65. pointerEvents: "all",
  66. strokeLinecap: "round",
  67. strokeLinejoin: "round",
  68. })
  69. const StyledGroup = styled("g", {
  70. [`& ${HoverIndicator}`]: {
  71. opacity: "0",
  72. },
  73. variants: {
  74. isSelected: {
  75. true: {
  76. [`& ${Indicator}`]: {
  77. stroke: "$selected",
  78. },
  79. [`&:hover ${HoverIndicator}`]: {
  80. opacity: "1",
  81. stroke: "$hint",
  82. },
  83. },
  84. false: {
  85. [`&:hover ${HoverIndicator}`]: {
  86. opacity: "1",
  87. stroke: "$hint",
  88. },
  89. },
  90. },
  91. },
  92. })
  93. export { Indicator, HoverIndicator }
  94. export default memo(Shape)