UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

78 lines (77 loc) 3.56 kB
import React, { memo, useEffect, useMemo, useState } from "react"; import debounce from "lodash/debounce"; import isEqual from "lodash/isEqual"; import { Block as CanvasBlock } from "../components/canvas/blocks/Block"; import { GraphState } from "../graph"; import { ECameraScaleLevel } from "../services/camera/CameraService"; import { useSignal } from "./hooks"; import { useGraphEvent } from "./hooks/useGraphEvents"; import { useCompareState } from "./utils/hooks/useCompareState"; import { useFn } from "./utils/hooks/useFn"; export const Block = (props) => { const block = useSignal(props.blockState.$state); if (!block) return; return props.renderBlock(props.graphObject, block, props.blockState); }; export const BlocksList = memo(function BlocksList({ renderBlock, graphObject }) { const [blockStates, setBlockStates] = useState([]); const [isRenderAllowed, setRenderAllowed] = useCompareState(false); const [graphState, setGraphState] = useCompareState(graphObject.state); const render = useFn((graphObject, block) => { return renderBlock(graphObject, block); }); const updateBlockList = useFn(() => { if (graphObject.cameraService.getCameraBlockScaleLevel() !== ECameraScaleLevel.Detailed) { setBlockStates([]); return; } const CAMERA_VIEWPORT_TRESHOLD = graphObject.graphConstants.system.CAMERA_VIEWPORT_TRESHOLD; const cameraSize = graphObject.cameraService.getCameraState(); const x = -cameraSize.relativeX - cameraSize.relativeWidth * CAMERA_VIEWPORT_TRESHOLD; const y = -cameraSize.relativeY - cameraSize.relativeHeight * CAMERA_VIEWPORT_TRESHOLD; const width = -cameraSize.relativeX + cameraSize.relativeWidth * (1 + CAMERA_VIEWPORT_TRESHOLD) - x; const height = -cameraSize.relativeY + cameraSize.relativeHeight * (1 + CAMERA_VIEWPORT_TRESHOLD) - y; const statesInRect = graphObject .getElementsOverRect({ x, y, width, height, }, [CanvasBlock]) .map((component) => component.connectedState); if (!isEqual(statesInRect.map((state) => state.id).sort(), blockStates.map((state) => state.id).sort())) { setBlockStates(statesInRect); } }); const scheduleListUpdate = useMemo(() => { return debounce(() => updateBlockList(), 0); }, []); useGraphEvent(graphObject, "state-change", () => { setGraphState(graphObject.state); }); useEffect(() => { setGraphState(graphObject.state); }, [graphObject]); useGraphEvent(graphObject, "camera-change", ({ scale }) => { if (graphObject.cameraService.getCameraBlockScaleLevel(scale) !== ECameraScaleLevel.Detailed) { setRenderAllowed(false); return; } setRenderAllowed(true); scheduleListUpdate(); }); // init list useEffect(() => { graphObject.hitTest.on("update", scheduleListUpdate); scheduleListUpdate(); return () => { graphObject.hitTest.off("update", scheduleListUpdate); }; }, [graphObject.cameraService, graphObject.hitTest, scheduleListUpdate]); return (React.createElement(React.Fragment, null, graphState === GraphState.READY && isRenderAllowed && blockStates.map((blockState) => { return (React.createElement(Block, { key: blockState.id.toString(), renderBlock: render, graphObject: graphObject, blockState: blockState })); }))); });