123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- import { register } from "@antv/x6-react-shape";
- import { EventArgs, Graph, Node } from "@antv/x6";
- import { topicData } from "@/config/data";
- import { useSizeHook, useShapeProps } from "@/hooks";
- import { useEffect, useMemo, useRef, useState } from "react";
- import { PlusOutlined } from "@ant-design/icons";
- import { TopicType } from "@/enum";
- import { addTopic } from "@/pages/mindmap/mindMap";
- import Link from "./Link";
- import ExtraModule from "./ExtraModule";
- import CustomTag from "@/components/mindMap/CustomTag";
- import { TopicItem } from "@/types";
- import { selectTopic } from "@/utils/mindmapHander";
- import { Tooltip, Popover } from "antd";
- import LinkForm from "./LinkForm";
- import Text from "./Text";
- import FlowExtra from "../FlowExtra";
- const component = ({ node, graph }: { node: Node; graph: Graph }) => {
- const {
- fill,
- stroke,
- opacity,
- label,
- text,
- borderSize,
- setMindProjectInfo,
- icons,
- tags,
- extraModules,
- remark,
- href,
- children,
- type,
- collapsed,
- fixedWidth,
- linkTopicId,
- } = node.getData();
- const { size, ref } = useSizeHook();
- const { fillContent, strokeColor, strokeWidth } = useShapeProps(
- fill,
- size,
- stroke
- );
- const [selected, setSelected] = useState(false);
- const [showPopover, setShowPopover] = useState(false);
- const [popoverContent, setPopoverContent] = useState<React.ReactNode>();
- const handleSelect = (_args: EventArgs["node:selected"]) => {
- const cells = graph.getSelectedCells();
- setSelected(!!cells.find((item) => item.id === node.id));
- };
- const [showCollapsePoint, setShowCollapsePoint] = useState(collapsed);
- const extraModuleRef = useRef<HTMLDivElement>(null);
- const titleRef = useRef<HTMLDivElement>(null);
- const tagRef = useRef<HTMLDivElement>(null);
- const remarkRef = useRef<HTMLDivElement>(null);
- const padding = useMemo(() => {
- switch (type) {
- case TopicType.main:
- return {
- y: 14,
- x: 28,
- };
- case TopicType.branch:
- return {
- y: 8,
- x: 16,
- };
- default:
- return {
- y: 4,
- x: 6,
- };
- }
- }, [type]);
- const showHrefConfig = () => {
- setShowPopover(true);
- setPopoverContent(
- <LinkForm
- title={href?.title}
- value={href?.value}
- onCancel={() => setShowPopover(false)}
- onConfirm={(data) => {
- setShowPopover(false);
- node.setData({ href: data });
- }}
- />
- );
- };
- // @ts-ignore 绑定一个外部调用方法
- node.extendAttr = {
- showHrefConfig,
- };
- useEffect(() => {
- // graph.createTransformWidget(node);
- // graph.select(node);
- graph.on("node:selected", handleSelect);
- graph.on("node:unselected", handleSelect);
- return () => {
- graph.off("node:selected", handleSelect);
- graph.off("node:unselected", handleSelect);
- };
- }, []);
- const changeSize = () => {
- const { clientHeight = 0, clientWidth = 0 } = ref.current || {};
- if (
- clientHeight &&
- (size.width !== clientWidth || size.height !== clientHeight)
- ) {
- node.setData({
- width: clientWidth,
- height: clientHeight,
- });
- }
- };
- useEffect(() => {
- changeSize();
- }, [node.data]);
- const childrenCount = useMemo(() => {
- let count = 0;
- const traverse = (topics: TopicItem[]) => {
- topics.forEach((item) => {
- count++;
- if (item.children) {
- traverse(item.children);
- }
- });
- };
- traverse(children);
- return count;
- }, [children]);
- const handleShowRemark = () => {
- selectTopic(graph, node.data);
- // @ts-ignore
- graph.extendAttr.setRightToolbarActive("remark");
- };
- const handleAddBranch = () => {
- const data = node.getData();
- let topic;
- if (data.type === TopicType.main) {
- topic = addTopic(TopicType.branch, setMindProjectInfo, node);
- } else {
- topic = addTopic(TopicType.sub, setMindProjectInfo, node);
- }
- selectTopic(graph, topic);
- };
- const handleToggleCollapse = () => {
- node.setData({
- collapsed: !collapsed,
- });
- };
- const handleDeleteHeft = () => {
- node.setData(
- {
- href: undefined,
- },
- {
- deep: false,
- }
- );
- };
- const handleChangeTag = (tagInfo: { color?: string; title?: string }, index: number) => {
- node.setData({
- tags: tags.map((item: { color?: string; title?: string }, i: number) => {
- if (index === i) {
- return {
- ...item,
- ...tagInfo
- }
- }
- return item;
- }, {
- deep: false
- })
- })
- }
- const handleDeleteTag = (index: number) => {
- tags.splice(index, 1);
- node.setData({
- tags
- }, {
- deep: false
- });
- }
- return (
- <div>
- {selected && (
- <div
- style={{
- width: `calc(100% + 4px)`,
- height: `calc(100% + 4px)`,
- border: "1.5px solid #239edd",
- position: "absolute",
- top: -2,
- left: -2,
- }}
- />
- )}
- <Popover open={showPopover} content={popoverContent}>
- <div
- className="content relative text-0"
- ref={ref}
- style={{
- width: fixedWidth ? "100%" : "fit-content",
- opacity: opacity / 100,
- border: `${stroke.type} ${strokeWidth}px ${strokeColor}`,
- background: fillContent,
- borderRadius: borderSize,
- padding: `${padding.y}px ${padding.x}px`,
- }}
- onMouseOver={() => !collapsed && setShowCollapsePoint(true)}
- onMouseLeave={() => !collapsed && setShowCollapsePoint(false)}
- >
- <FlowExtra node={node}/>
- {/* 扩展模块 */}
- {extraModules && (
- <div className="extra" ref={extraModuleRef}>
- <ExtraModule node={node} extraModules={extraModules} />
- </div>
- )}
- {/* 图标、标题、链接等 */}
- <div className="tit flex items-center justify-center" ref={titleRef}>
- <div className="flex items-center text-20px">
- {icons?.map((icon: string) => {
- return (
- <svg key={icon} className="icon mr-6px" aria-hidden="true">
- <use xlinkHref={`#${icon}`}></use>
- </svg>
- );
- })}
- </div>
- <Text
- value={label}
- node={node}
- fixedWidth={fixedWidth}
- styles={{
- ...text,
- }}
- txtStyle={{
- position: "relative",
- ...(fixedWidth
- ? { maxWidth: `calc(100% - ${2 * padding.x}px)` }
- : { width: "max-content" }),
- }}
- />
- <div
- className="flex items-center color-#fff m-l-8px"
- ref={remarkRef}
- >
- {href && (
- <Link
- link={href}
- onEdit={showHrefConfig}
- onDelete={handleDeleteHeft}
- />
- )}
- {remark && (
- <Tooltip color="yellow" title={remark}>
- <i
- className="iconfont icon-pinglun1 cursor-pointer ml-4px"
- onClick={handleShowRemark}
- />
- </Tooltip>
- )}
- {linkTopicId && (
- <Tooltip color="yellow" title={remark}>
- <i
- className="iconfont icon-liangdianlianjie-01 cursor-pointer ml-4px"
- onClick={handleShowRemark}
- />
- </Tooltip>
- )}
- </div>
- </div>
- {/* 标签 */}
- <div className="" ref={tagRef}>
- {tags?.map((item: { name: string; color: string }, index: number) => {
- return (
- <CustomTag
- className="text-14px inline-block mr-8px"
- key={item.name}
- title={item.name}
- color={item.color}
- onChangeTag={(tag) => handleChangeTag(tag, index)}
- onDelete={() => handleDeleteTag(index)}
- hideEditBtn
- >
- {item.name}
- </CustomTag>
- );
- })}
- </div>
- {/* 添加主题按钮 */}
- {selected && !children?.length && (
- <div
- className={`
- absolute
- w-20px
- h-20px
- rounded-full
- right--25px
- top-50%
- translate-y-[-50%]
- flex
- justify-center
- items-center
- text-12px
- cursor-pointer
- bg-#eef0f3
- color-#9aa5b8
- hover:bg-#067bef
- hover:color-white`}
- onClick={handleAddBranch}
- >
- <PlusOutlined />
- </div>
- )}
- {type !== TopicType.main && children.length && (
- <div
- className="absolute right--30px top-0 w-30px h-full"
- onMouseOver={() => !collapsed && setShowCollapsePoint(true)}
- onMouseOut={() => !collapsed && setShowCollapsePoint(false)}
- />
- )}
- {/* 折叠按钮 */}
- {type !== TopicType.main && children?.length && showCollapsePoint && (
- <div
- className={`
- absolute
- rounded-full
- bg-white
- top-50%
- translate-y-[-50%]
- cursor-pointer
- hover:bg-#e6e6e6
- text-12px
- flex
- items-center
- justify-center
- ${collapsed ? "w-16px h-16px right--20px" : "w-10px h-10px right--15px"}
- `}
- onClick={handleToggleCollapse}
- style={{
- border: `1px solid ${fill.color1}`,
- color: fill.color1,
- }}
- >
- {collapsed && childrenCount}
- </div>
- )}
- </div>
- </Popover>
- </div>
- );
- };
- // 主题节点
- register({
- shape: "mind-map-topic",
- width: 206,
- height: 70,
- effect: ["data"],
- component: component,
- });
- const baseNode = {
- shape: "mind-map-topic",
- data: {
- label: "",
- ...topicData,
- },
- };
- export default baseNode;
|