export default function GraphViewer()

in packages/graph-explorer/src/modules/GraphViewer/GraphViewer.tsx [96:257]


export default function GraphViewer() {
  const graphRef = useRef<GraphRef | null>(null);

  const [nodesSelectedIds, setNodesSelectedIds] = useAtom(
    nodesSelectedRenderedIdsAtom
  );

  const [edgesSelectedIds, setEdgesSelectedIds] = useAtom(
    edgesSelectedRenderedIdsAtom
  );
  const nodesOutIds = useAtomValue(nodesOutOfFocusRenderedIdsAtom);
  const edgesOutIds = useAtomValue(edgesOutOfFocusRenderedIdsAtom);

  const autoOpenDetails = useAutoOpenDetailsSidebar();

  const onSelectedElementIdsChange = useCallback(
    ({ nodeIds, edgeIds }: SelectedElements) => {
      setNodesSelectedIds(nodeIds as Set<RenderedVertexId>);
      setEdgesSelectedIds(edgeIds as Set<RenderedEdgeId>);

      if (
        (nodeIds.size === 1 && edgeIds.size === 0) ||
        (nodeIds.size === 0 && edgeIds.size === 1)
      ) {
        autoOpenDetails();
      }
    },
    [autoOpenDetails, setEdgesSelectedIds, setNodesSelectedIds]
  );

  const [legendOpen, setLegendOpen] = useState(false);
  const { onZoomIn, onZoomOut, onSaveScreenshot } =
    useGraphGlobalActions(graphRef);

  const {
    clearAllLayers,
    contextLayerProps,
    contextNodeId,
    contextEdgeId,
    isContextOpen,
    onGraphRightClick,
    onNodeRightClick,
    onEdgeRightClick,
    parentRef,
    renderContextLayer,
  } = useContextMenu();

  const styles = useGraphStyles();
  const getNodeBadges = useNodeBadges();

  const { expandNode } = useExpandNode();
  const onNodeDoubleClick: ElementEventCallback<RenderedVertex["data"]> =
    useCallback(
      (_, vertex) => {
        const vertexId = getVertexIdFromRenderedVertexId(vertex.id);

        expandNode(vertexId, vertex.types, {
          limit: 10,
        });
      },
      [expandNode]
    );

  const [layout, setLayout] = useState("F_COSE");
  const onClearGraph = useClearGraph();

  const nodes = useRenderedVertices();
  const edges = useRenderedEdges();

  return (
    <div className="relative size-full grow" onContextMenu={onContextMenu}>
      <Panel>
        <PanelHeader>
          <PanelTitle>Graph View</PanelTitle>
          <PanelHeaderActions>
            <SelectField
              className="min-w-auto max-w-64"
              label="Layout"
              labelPlacement="inner"
              options={LAYOUT_OPTIONS}
              value={layout}
              onValueChange={setLayout}
            />
            <IconButton
              tooltipText="Re-run Layout"
              icon={<RefreshCwIcon />}
              variant="text"
              onClick={() => {
                graphRef.current?.runLayout();
              }}
            />
            <div className="grow" />
            <PanelHeaderActionButton
              label="Download Screenshot"
              icon={<ImageDownIcon />}
              onActionClick={onSaveScreenshot}
            />
            <ExportGraphButton />
            <ImportGraphButton />
            <PanelHeaderDivider />
            <PanelHeaderActionButton
              label="Zoom in"
              icon={<ZoomInIcon />}
              onActionClick={onZoomIn}
            />
            <PanelHeaderActionButton
              label="Zoom out"
              icon={<ZoomOutIcon />}
              onActionClick={onZoomOut}
            />
            <PanelHeaderDivider />
            <PanelHeaderActionButton
              label="Clear canvas"
              icon={<CircleSlash2 />}
              color="danger"
              onActionClick={onClearGraph}
            />
            <PanelHeaderActionButton
              label="Legend"
              icon={<BadgeInfoIcon />}
              onActionClick={() => setLegendOpen(open => !open)}
            />
          </PanelHeaderActions>
        </PanelHeader>
        <PanelContent
          className="bg-background-secondary relative flex h-full w-full"
          ref={parentRef}
        >
          <Graph
            ref={graphRef}
            nodes={nodes}
            edges={edges}
            badgesEnabled={false}
            getNodeBadges={getNodeBadges(nodesOutIds)}
            selectedNodesIds={nodesSelectedIds}
            selectedEdgesIds={edgesSelectedIds}
            outOfFocusNodesIds={nodesOutIds}
            outOfFocusEdgesIds={edgesOutIds}
            onSelectedElementIdsChange={onSelectedElementIdsChange}
            onNodeDoubleClick={onNodeDoubleClick}
            onNodeRightClick={onNodeRightClick}
            onEdgeRightClick={onEdgeRightClick}
            onGraphRightClick={onGraphRightClick}
            styles={styles}
            layout={layout}
          />
          {isContextOpen &&
            renderContextLayer(
              <div
                {...contextLayerProps}
                style={contextLayerProps.style}
                className="z-menu"
              >
                <ContextMenu
                  graphRef={graphRef}
                  onClose={clearAllLayers}
                  affectedNodesIds={contextNodeId ? [contextNodeId] : []}
                  affectedEdgesIds={contextEdgeId ? [contextEdgeId] : []}
                />
              </div>
            )}
          {legendOpen && <Legend onClose={() => setLegendOpen(false)} />}