Topic.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { register } from "@antv/x6-react-shape";
  2. import { EventArgs, Graph, Node, Path } from "@antv/x6";
  3. import { topicData } from "@/config/data";
  4. import { useSizeHook, useShapeProps } from "@/hooks";
  5. import CustomInput from "../CustomInput";
  6. import { useEffect, useState } from "react";
  7. import { PlusOutlined } from "@ant-design/icons";
  8. import { TopicType } from "@/enum";
  9. import { addTopic } from "@/utils/mindMap";
  10. const component = ({ node, graph }: { node: Node; graph: Graph }) => {
  11. const { fill, stroke, opacity, label, text, borderSize } = node.getData();
  12. const { size, ref } = useSizeHook();
  13. const { fillContent, strokeColor, strokeWidth, strokeDasharray } =
  14. useShapeProps(fill, size, stroke);
  15. const [selected, setSelected] = useState(true);
  16. const handleSelect = (_args: EventArgs["node:selected"]) => {
  17. const cells = graph.getSelectedCells();
  18. setSelected(cells.length === 1 && cells[0].id === node.id);
  19. };
  20. useEffect(() => {
  21. graph.createTransformWidget(node);
  22. graph.select(node);
  23. graph.on("node:selected", handleSelect);
  24. graph.on("node:unselected", handleSelect);
  25. return () => {
  26. graph.off("node:selected", handleSelect);
  27. graph.off("node:unselected", handleSelect);
  28. };
  29. }, []);
  30. const handleAddBranch = () => {
  31. const data = node.getData();
  32. if( data.type === TopicType.main) {
  33. addTopic(node, TopicType.branch);
  34. } else {
  35. addTopic(node, TopicType.sub);
  36. }
  37. }
  38. return (
  39. <>
  40. <div
  41. className="relative text-0 w-full h-full"
  42. ref={ref}
  43. style={{
  44. opacity: opacity / 100,
  45. border: `solid ${strokeWidth}px ${strokeColor}`,
  46. background: fillContent,
  47. borderRadius: borderSize,
  48. }}
  49. >
  50. <CustomInput value={label} node={node} styles={text} />
  51. {selected && (
  52. <div
  53. className={`
  54. absolute
  55. w-20px
  56. h-20px
  57. rounded-full
  58. right--25px
  59. top-50%
  60. translate-y-[-50%]
  61. flex
  62. justify-center
  63. items-center
  64. text-12px
  65. cursor-pointer
  66. bg-#eef0f3
  67. color-#9aa5b8
  68. hover:bg-#067bef
  69. hover:color-white`}
  70. onClick={handleAddBranch}
  71. >
  72. <PlusOutlined />
  73. </div>
  74. )}
  75. </div>
  76. </>
  77. );
  78. };
  79. // 连接器
  80. Graph.registerConnector(
  81. "mindmap",
  82. (sourcePoint, targetPoint, routerPoints, options) => {
  83. const midX = sourcePoint.x + 10;
  84. const midY = sourcePoint.y;
  85. const ctrX = (targetPoint.x - midX) / 5 + midX;
  86. const ctrY = targetPoint.y;
  87. const pathData = `
  88. M ${sourcePoint.x} ${sourcePoint.y}
  89. L ${midX} ${midY}
  90. Q ${ctrX} ${ctrY} ${targetPoint.x} ${targetPoint.y}
  91. `;
  92. return options.raw ? Path.parse(pathData) : pathData;
  93. },
  94. true
  95. );
  96. // 注册思维导图边
  97. Graph.registerEdge(
  98. "mindmap-edge",
  99. {
  100. inherit: "edge",
  101. connector: {
  102. name: "mindmap",
  103. },
  104. attrs: {
  105. line: {
  106. targetMarker: "",
  107. stroke: "#A2B1C3",
  108. strokeWidth: 2,
  109. },
  110. },
  111. zIndex: 0,
  112. },
  113. true
  114. );
  115. // 主题
  116. register({
  117. shape: "mind-map-topic",
  118. width: 206,
  119. height: 70,
  120. effect: ["data"],
  121. component: component,
  122. });
  123. const baseNode = {
  124. shape: "mind-map-topic",
  125. data: {
  126. label: "",
  127. ...topicData,
  128. },
  129. };
  130. export default baseNode;