1+ // src/utils/buildFlow.js
2+
3+ // ——— Label mapping for the non-rule nodes ———
4+ const typeLabelMap = {
5+ NodeROOT : 'Requirements' ,
6+ NodeAND : 'All need to be fulfilled' ,
7+ NodeOR : 'At least one needs to be met' ,
8+ } ;
9+
10+ export default function buildFlow ( graphRoot ) {
11+ let nextId = 0 ;
12+
13+ // Temp storage of every node with depth & parent
14+ const raw = [ ] ;
15+ const edges = [ ] ;
16+
17+ // 1) Traverse & collect, skipping MinCount RULEs
18+ function traverse ( node , depth = 0 , parentId = null ) {
19+ const typeName = node . constructor ?. name ;
20+
21+ // ─── FILTER OUT MinCountConstraintComponent RULEs ───
22+ if (
23+ typeName === 'NodeRULE' &&
24+ node . type === 'sh:MinCountConstraintComponent'
25+ ) {
26+ return ;
27+ }
28+
29+ const myId = `node_${ nextId ++ } ` ;
30+ raw . push ( { id : myId , node, depth, parentId } ) ;
31+
32+ if ( parentId ) {
33+ edges . push ( {
34+ id : `e_${ parentId } -${ myId } ` ,
35+ source : parentId ,
36+ target : myId ,
37+ style : { stroke : '#888' } ,
38+ } ) ;
39+ }
40+
41+ ( node . children || [ ] ) . forEach ( child =>
42+ traverse ( child , depth + 1 , myId )
43+ ) ;
44+ }
45+ traverse ( graphRoot ) ;
46+
47+ // 2) Group by depth
48+ const levels = raw . reduce ( ( acc , item ) => {
49+ ( acc [ item . depth ] = acc [ item . depth ] || [ ] ) . push ( item ) ;
50+ return acc ;
51+ } , { } ) ;
52+
53+ const horizontalSpacing = 200 ;
54+ const verticalSpacing = 100 ;
55+
56+ // 3) Assign positions symmetrically
57+ const nodes = raw . map ( item => {
58+ const { id, node, depth } = item ;
59+ const group = levels [ depth ] ;
60+ const idx = group . findIndex ( g => g . id === id ) ;
61+ const count = group . length ;
62+
63+ // center siblings around x=0
64+ const x = ( idx - ( count - 1 ) / 2 ) * horizontalSpacing ;
65+ const y = depth * verticalSpacing ;
66+
67+ // determine label
68+ let label ;
69+ if ( node . constructor ?. name === 'NodeRULE' ) {
70+ const compType = node . type || '<constraint>' ;
71+ const val = Array . isArray ( node . value ) ? node . value . join ( ', ' ) : node . value ;
72+ label = `${ compType } : ${ val } ` ;
73+ } else {
74+ label = typeLabelMap [ node . constructor ?. name ]
75+ ?? node . path
76+ ?? node . constructor ?. name
77+ ?? 'node' ;
78+ }
79+
80+ // detect missing
81+ const isMissing = node . status === 'missing' || node . shaclEval ?. status === 'missing' ;
82+
83+ return {
84+ id,
85+ data : { label } ,
86+ position : { x, y } ,
87+ style : {
88+ padding : 10 ,
89+ borderRadius : 6 ,
90+ background : isMissing ? '#fde2e2' : '#e2f7de' ,
91+ border : `2px solid ${ isMissing ? '#f5c2c7' : '#a8e6a3' } ` ,
92+ width : Math . max ( 150 , label . length * 8 ) ,
93+ }
94+ } ;
95+ } ) ;
96+
97+ return { nodes, edges } ;
98+ }
0 commit comments