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.6KB

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