import React from "react"; import { ReactZoomPanPinchRef, TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; import { Canvas, Edge, ElkRoot } from "reaflow"; import { CustomNode } from "src/components/CustomNode"; import useGraph from "src/store/useGraph"; import useUser from "src/store/useUser"; import { getNodePath } from "src/utils/getNodePath"; import styled from "styled-components"; import { Loading } from "../Loading"; import { ErrorView } from "./ErrorView"; import { PremiumView } from "./PremiumView"; interface GraphProps { isWidget?: boolean; openNodeModal: () => void; } const StyledEditorWrapper = styled.div<{ widget: boolean }>` position: absolute; width: 100%; height: ${({ widget }) => (widget ? "calc(100vh - 36px)" : "calc(100vh - 65px)")}; background: ${({ theme }) => theme.BACKGROUND_SECONDARY}; background-image: ${({ theme }) => `radial-gradient(#505050 1px, ${theme.BACKGROUND_SECONDARY} 1px)`}; background-size: 25px 25px; :active { cursor: move; } .dragging, .dragging button { pointer-events: none; } rect { fill: ${({ theme }) => theme.BACKGROUND_NODE}; } @media only screen and (max-width: 1440px) { background-image: ${({ theme }) => `radial-gradient(#505050 0.5px, ${theme.BACKGROUND_SECONDARY} 0.5px)`}; background-size: 15px 15px; } @media only screen and (max-width: 320px) { height: 100vh; } `; const GraphComponent = ({ isWidget = false, openNodeModal }: GraphProps) => { const isPremium = useUser(state => state.isPremium); const setLoading = useGraph(state => state.setLoading); const setZoomPanPinch = useGraph(state => state.setZoomPanPinch); const centerView = useGraph(state => state.centerView); const setSelectedNode = useGraph(state => state.setSelectedNode); const loading = useGraph(state => state.loading); const direction = useGraph(state => state.direction); const nodes = useGraph(state => state.nodes); const edges = useGraph(state => state.edges); const [size, setSize] = React.useState({ width: 1, height: 1, }); const handleNodeClick = React.useCallback( (_: React.MouseEvent, data: NodeData) => { if (setSelectedNode) setSelectedNode({ node: data.text, path: getNodePath(nodes, edges, data.id) }); if (openNodeModal) openNodeModal(); }, [edges, nodes, openNodeModal, setSelectedNode] ); const onInit = React.useCallback( (ref: ReactZoomPanPinchRef) => { setZoomPanPinch(ref); }, [setZoomPanPinch] ); const onLayoutChange = React.useCallback( (layout: ElkRoot) => { if (layout.width && layout.height) { const areaSize = layout.width * layout.height; const changeRatio = Math.abs((areaSize * 100) / (size.width * size.height) - 100); setSize({ width: (layout.width as number) + 400, height: (layout.height as number) + 400, }); requestAnimationFrame(() => { setTimeout(() => { setLoading(false); setTimeout(() => { if (changeRatio > 70 || isWidget) centerView(); }); }); }); } }, [centerView, isWidget, setLoading, size.height, size.width] ); const onCanvasClick = React.useCallback(() => { const input = document.querySelector("input:focus") as HTMLInputElement; if (input) input.blur(); }, []); if (nodes.length > 8_000) return ; if (nodes.length > 1_000 && !isWidget) { if (!isPremium()) return ; } return ( e.preventDefault()} widget={isWidget}> ref.instance.wrapperComponent?.classList.add("dragging")} onPanningStop={ref => ref.instance.wrapperComponent?.classList.remove("dragging")} > } edge={props => } /> ); }; export const Graph = React.memo(GraphComponent);