import React, { useState, useRef, useEffect, useMemo, useCallback } from "react"; import { JsonFormsDispatch, JsonFormsStateProvider } from "@jsonforms/react"; import Grid from "@mui/material/Grid2"; import { CollapseToggle } from "./CollapseToggle"; import { NodeContainerProps, NODE_SCHEMA, NODE_UISCHEMA, FOREIGNOBJECT_MAX_HEIGHT, } from "./Interface"; export const NodeContainer = React.memo(function NodeContainer({ nodeData, renderers, cells, enabled, nodeElements, nodeWidth, minHeight, hasChildren, isCollapsed, onToggle, onHeightChange, currentHeight, levelStyle, depth, isHighlighted, isNodeLoading, styleConfig, }: NodeContainerProps) { const displayData = useMemo(() => { const { children, hasChildren: _hc, id, isHighlighted: _ih, ...rest } = nodeData; return rest; }, [nodeData]); const contentRef = useRef(null); const lastReportedH = useRef(0); const [isNodeHovered, setIsNodeHovered] = useState(false); const initState = useMemo(() => ({ core: { data: displayData, schema: NODE_SCHEMA, uischema: NODE_UISCHEMA, }, renderers, cells, }), [displayData, renderers, cells]); const nodeHeight = Math.max(currentHeight, minHeight); const backgroundColor = levelStyle.backgroundColor ?? "#ffffff"; const borderColor = levelStyle.borderColor ?? "#aaaaaa"; const borderWidth = levelStyle.borderWidth ?? 1; const borderRadius = levelStyle.borderRadius ?? 6; const borderDash = levelStyle.borderDash ?? "0"; const borderOpacity = levelStyle.borderOpacity ?? 1; useEffect(() => { const contentElement = contentRef.current; if (!contentElement ) return; const updateMeasuredHeight = () => { const measured = Math.max(contentElement.offsetHeight, minHeight); if (measured !== lastReportedH.current) { lastReportedH.current = measured; onHeightChange(measured); } }; const resizeObserver = new ResizeObserver(updateMeasuredHeight); resizeObserver.observe(contentElement); const timer = setTimeout(updateMeasuredHeight, 80); return () => { resizeObserver.disconnect(); clearTimeout(timer); }; }, [minHeight, onHeightChange]); const getElement = useCallback( (uiSchemaElem: any, i: number) => ( ), [renderers, cells, enabled] ); return ( setIsNodeHovered(true)} onMouseLeave={() => setIsNodeHovered(false)} onTouchStart={() => setIsNodeHovered(true)} onTouchEnd={() => setIsNodeHovered(false)} style={{ transform: isHighlighted ? "translateY(-8px)" : isNodeHovered ? "translateY(-4px)" : "translateY(0px)", transition: "transform 0.2s ease", }} > {isHighlighted && ( )} e.stopPropagation()} onTouchStart={(e) => e.stopPropagation()} onTouchMove={(e) => e.stopPropagation()} onTouchEnd={(e) => e.stopPropagation()} >
{ if (!hasChildren) return; const interactive = (e.target as HTMLElement).closest( 'button, input, textarea, select, [role="button"], a' ); if (!interactive) onToggle(); }} style={{ width: nodeWidth, boxSizing: "border-box", padding: "16px", cursor: hasChildren ? "pointer" : "default", position: "relative", ...styleConfig?.nodeContentStyle, }} > {nodeElements.map((element: any, i: number) => (
{getElement(element, i)}
))}
{hasChildren && ( )}
); });