All files / src/pages/main-page/components FlowsheetCardPreview.tsx

83.01% Statements 44/53
44.82% Branches 13/29
50% Functions 2/4
90.62% Lines 29/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                                103x 103x 103x 103x                         1282x         22x   22x   22x       682x       641x     641x 1078x 1078x   641x 641x   641x 641x                     1048x       44x         212x 212x 212x 212x   212x 212x     1034x                 9403x                   424x 1908x          
import {
  api,
  type CardPreview,
  type CardPreviewGraphicObject,
  type GraphicObjectRead,
} from "@/api/apiStore.gen";
import {
  FlowsheetPreviewEdgeGlyph,
  FlowsheetPreviewNodeGlyph,
} from "@/pages/flowsheet-page/flowsheet/Canvas/FlowsheetPreviewNodeGlyph";
import {
  type FlowsheetPreviewNode,
  getFlowsheetPreviewRenderGraph,
  getVisibleFlowsheetPreviewNodes,
} from "@/pages/flowsheet-page/flowsheet/Canvas/flowsheetPreviewRendering.ts";
 
const PREVIEW_WIDTH = 320;
const PREVIEW_HEIGHT = 180;
const PREVIEW_PADDING = 24;
const MAX_PREVIEW_OBJECTS = 80;
 
type FlowsheetCardPreviewProps = {
  flowsheetId: number;
  rootGrouping?: number | null;
  name: string;
  preview?: CardPreview;
  isLoading?: boolean;
};
 
type PreviewGraphicObject = CardPreviewGraphicObject | GraphicObjectRead;
 
function getVisibleObjects(graphicsObjects?: PreviewGraphicObject[]) {
  return getVisibleFlowsheetPreviewNodes(
    graphicsObjects as FlowsheetPreviewNode[],
  );
}
 
function EmptyPreview() {
  return (
    <div className="flex h-40 w-full items-center justify-center bg-muted/30 text-xs text-muted-foreground">
      Empty flowsheet
    </div>
  );
}
 
export function FlowsheetCardPreview({
  rootGrouping,
  name,
  preview,
  isLoading = false,
}: FlowsheetCardPreviewProps) {
  const { data: cachedGroupGraphicsObjects } =
    api.endpoints.graphicsObjectsList.useQueryState(
      { group: rootGrouping ?? 0 },
      { skip: !rootGrouping },
    );
  const visibleRootObjects = getVisibleObjects(cachedGroupGraphicsObjects);
  const visiblePreviewObjects = getVisibleObjects(preview?.graphicsObjects);
  const visibleObjects =
    visibleRootObjects.length > 0 ? visibleRootObjects : visiblePreviewObjects;
  const renderGraph = getFlowsheetPreviewRenderGraph({
    nodes: visibleObjects,
    connections: preview?.connections,
    width: PREVIEW_WIDTH,
    height: PREVIEW_HEIGHT,
    padding: PREVIEW_PADDING,
    maxNodes: MAX_PREVIEW_OBJECTS,
    minNodeSize: 8,
  });
 
  if (isLoading && visibleObjects.length === 0) {
    return <div className="h-40 w-full animate-pulse bg-muted/40" />;
  }
 
  if (visibleObjects.length === 0) {
    return <EmptyPreview />;
  }
 
  return (
    <svg
      role="img"
      aria-label={`${name} flowsheet preview`}
      className="h-40 w-full bg-white"
      viewBox={`0 0 ${PREVIEW_WIDTH} ${PREVIEW_HEIGHT}`}
    >
      <rect width={PREVIEW_WIDTH} height={PREVIEW_HEIGHT} fill="transparent" />
      {renderGraph.edges.map((edge) => (
        <FlowsheetPreviewEdgeGlyph key={edge.id} edge={edge} />
      ))}
      {renderGraph.nodes.map(({ node, layout }) => (
        <FlowsheetPreviewNodeGlyph
          key={node.id}
          iconScale={0.5}
          layout={layout}
          node={node}
          pointerEvents="none"
          strokeWidth={1}
        />
      ))}
      {renderGraph.hiddenCount > 0 && (
        <text
          x={PREVIEW_WIDTH - 14}
          y={PREVIEW_HEIGHT - 12}
          textAnchor="end"
          className="fill-muted-foreground text-[11px] font-medium"
        >
          +{renderGraph.hiddenCount}
        </text>
      )}
    </svg>
  );
}
 
export default FlowsheetCardPreview;