UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

70 lines (69 loc) 3.33 kB
import React, { useEffect, useLayoutEffect, useRef } from "react"; import { computed } from "@preact/signals-core"; import { useSignal } from "./hooks"; import { useBlockState, useBlockViewState } from "./hooks/useBlockState"; import "./Block.css"; export const GraphBlock = ({ graph, block, children, className, containerClassName, }) => { const containerRef = useRef(null); const lastStateRef = useRef({ x: 0, y: 0, width: 0, height: 0, zIndex: 0 }); const viewState = useBlockViewState(graph, block); const state = useBlockState(graph, block); const selected = useSignal(computed(() => state?.$selected.value ?? false)); useEffect(() => { viewState?.setHiddenBlock(true); return () => viewState?.setHiddenBlock(false); }, [viewState]); // Optimized updates only on actual changes useLayoutEffect(() => { if (!containerRef.current || !state) return; const element = containerRef.current; const lastState = lastStateRef.current; // Проверяем, что действительно изменилось const hasPositionChange = lastState.x !== state.x || lastState.y !== state.y; const hasSizeChange = lastState.width !== state.width || lastState.height !== state.height; if (hasPositionChange) { // Используем transform для позиции - самый быстрый способ element.style.transform = `translate3d(${state.x}px, ${state.y}px, 0)`; lastState.x = state.x; lastState.y = state.y; } if (hasSizeChange) { // Размеры устанавливаем напрямую element.style.width = `${state.width}px`; element.style.height = `${state.height}px`; lastState.width = state.width; lastState.height = state.height; } }, [state?.x, state?.y, state?.width, state?.height]); useEffect(() => { if (viewState && containerRef.current) { containerRef.current.style.pointerEvents = viewState.isInteractive() ? "auto" : "none"; return viewState.onChange(() => { if (containerRef.current) { containerRef.current.style.pointerEvents = viewState.isInteractive() ? "auto" : "none"; } }); } return undefined; }, [viewState]); useEffect(() => { if (viewState && containerRef.current) { return viewState.$viewState.subscribe(({ zIndex, order }) => { const element = containerRef.current; const lastState = lastStateRef.current; const newZIndex = (zIndex || 0) + (order || 0); if (element && lastState.zIndex !== newZIndex) { element.style.zIndex = `${newZIndex}`; lastState.zIndex = newZIndex; } }); } return undefined; }, [viewState]); if (!viewState || !state) { return null; } return (React.createElement("div", { className: `graph-block-container ${containerClassName || ""}`, ref: containerRef }, React.createElement("div", { className: `graph-block-wrapper ${className || ""} ${selected ? "selected" : ""}` }, children))); };