選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

shape.tsx 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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 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>{getShapeUtils(shape).render(shape)}</defs>
  48. <HoverIndicator as="use" xlinkHref={"#" + id} />
  49. <use xlinkHref={"#" + id} {...shape.style} />
  50. <Indicator as="use" xlinkHref={"#" + id} />
  51. </StyledGroup>
  52. )
  53. }
  54. const Indicator = styled("path", {
  55. fill: "none",
  56. stroke: "transparent",
  57. zStrokeWidth: [1, 1],
  58. pointerEvents: "none",
  59. strokeLineCap: "round",
  60. strokeLinejoin: "round",
  61. })
  62. const HoverIndicator = styled("path", {
  63. fill: "none",
  64. stroke: "transparent",
  65. zStrokeWidth: [8, 4],
  66. pointerEvents: "all",
  67. strokeLinecap: "round",
  68. strokeLinejoin: "round",
  69. })
  70. const StyledGroup = styled("g", {
  71. [`& ${HoverIndicator}`]: {
  72. opacity: "0",
  73. },
  74. variants: {
  75. isSelected: {
  76. true: {
  77. [`& ${Indicator}`]: {
  78. stroke: "$selected",
  79. },
  80. [`&:hover ${HoverIndicator}`]: {
  81. opacity: "1",
  82. stroke: "$hint",
  83. },
  84. },
  85. false: {
  86. [`&:hover ${HoverIndicator}`]: {
  87. opacity: "1",
  88. stroke: "$hint",
  89. },
  90. },
  91. },
  92. },
  93. })
  94. export { Indicator, HoverIndicator }
  95. export default memo(Shape)