Spaces:
Paused
Paused
File size: 5,166 Bytes
5a81b95 | 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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | /**
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
* β FLOWCHART VIEW COMPONENT β
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
* β Business process and decision flow visualization β
* β Part of the Visual Cortex Layer β
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
*/
import { useMemo } from 'react';
import { MermaidDiagram } from './MermaidDiagram';
import { cn } from '@/lib/utils';
export interface FlowNode {
id: string;
label: string;
type?: 'start' | 'end' | 'process' | 'decision' | 'data' | 'subprocess' | 'custom';
style?: string;
}
export interface FlowEdge {
from: string;
to: string;
label?: string;
style?: 'solid' | 'dashed' | 'thick';
}
export interface FlowSubgraph {
id: string;
label: string;
nodes: string[];
direction?: 'TB' | 'LR';
}
export interface FlowchartViewProps {
/** Flowchart title */
title?: string;
/** Nodes in the flowchart */
nodes: FlowNode[];
/** Edges connecting nodes */
edges: FlowEdge[];
/** Optional subgraphs for grouping */
subgraphs?: FlowSubgraph[];
/** Flow direction: TB (top-bottom), LR (left-right), BT, RL */
direction?: 'TB' | 'LR' | 'BT' | 'RL';
/** Custom class */
className?: string;
/** Callback when rendered */
onRender?: (svg: string) => void;
}
// Node type to Mermaid shape
const NODE_SHAPES: Record<string, { prefix: string; suffix: string }> = {
start: { prefix: '([', suffix: '])' }, // Stadium (rounded)
end: { prefix: '([', suffix: '])' }, // Stadium
process: { prefix: '[', suffix: ']' }, // Rectangle
decision: { prefix: '{', suffix: '}' }, // Diamond
data: { prefix: '[/', suffix: '/]' }, // Parallelogram
subprocess: { prefix: '[[', suffix: ']]' }, // Subroutine
custom: { prefix: '[', suffix: ']' }, // Rectangle
};
// Edge style to arrow
const EDGE_STYLES: Record<string, string> = {
solid: '-->',
dashed: '-.->',
thick: '==>',
};
export function FlowchartView({
title,
nodes,
edges,
subgraphs = [],
direction = 'TB',
className,
onRender,
}: FlowchartViewProps) {
const mermaidCode = useMemo(() => {
const lines: string[] = [`flowchart ${direction}`];
// Track nodes in subgraphs
const nodesInSubgraphs = new Set<string>();
subgraphs.forEach(sg => sg.nodes.forEach(n => nodesInSubgraphs.add(n)));
// Generate subgraphs
subgraphs.forEach(sg => {
const subDir = sg.direction ? ` direction ${sg.direction}` : '';
lines.push(` subgraph ${sg.id}["${sg.label}"]${subDir}`);
sg.nodes.forEach(nodeId => {
const node = nodes.find(n => n.id === nodeId);
if (node) {
const shape = NODE_SHAPES[node.type || 'process'];
lines.push(` ${node.id}${shape.prefix}"${node.label}"${shape.suffix}`);
}
});
lines.push(' end');
});
// Generate standalone nodes
nodes.forEach(node => {
if (!nodesInSubgraphs.has(node.id)) {
const shape = NODE_SHAPES[node.type || 'process'];
lines.push(` ${node.id}${shape.prefix}"${node.label}"${shape.suffix}`);
}
});
// Generate edges
edges.forEach(edge => {
const arrow = EDGE_STYLES[edge.style || 'solid'];
if (edge.label) {
lines.push(` ${edge.from} ${arrow}|"${edge.label}"| ${edge.to}`);
} else {
lines.push(` ${edge.from} ${arrow} ${edge.to}`);
}
});
// Add styling
lines.push('');
lines.push(' %% Styling');
// Style start/end nodes
const startNodes = nodes.filter(n => n.type === 'start').map(n => n.id);
const endNodes = nodes.filter(n => n.type === 'end').map(n => n.id);
const decisionNodes = nodes.filter(n => n.type === 'decision').map(n => n.id);
if (startNodes.length > 0) {
lines.push(` style ${startNodes.join(',')} fill:#22c55e,stroke:#16a34a,color:#fff`);
}
if (endNodes.length > 0) {
lines.push(` style ${endNodes.join(',')} fill:#ef4444,stroke:#dc2626,color:#fff`);
}
if (decisionNodes.length > 0) {
lines.push(` style ${decisionNodes.join(',')} fill:#f59e0b,stroke:#d97706,color:#000`);
}
return lines.join('\n');
}, [nodes, edges, subgraphs, direction]);
return (
<div className={cn('flowchart-view', className)}>
<MermaidDiagram
code={mermaidCode}
title={title}
theme="dark"
zoomable={true}
onRender={onRender}
/>
</div>
);
}
export default FlowchartView;
|