1+ @page " /ForceDirectedTreeLayout"
2+
3+ @using Syncfusion .Blazor .Diagram
4+
5+ <SfDiagramComponent @ref =" _diagramComponent" Height =" 690px" Width =" 100%" @bind-Nodes =" @_nodes" @bind-Connectors =" @_connectors" Created =" @OnCreated" >
6+ <SnapSettings Constraints =" @SnapConstraints.None" ></SnapSettings >
7+ <Layout Type =" LayoutType.ForceDirectedTree" @bind-ForceDirectedTreeLayoutSettings =" @_layoutSettings" ></Layout >
8+ </SfDiagramComponent >
9+
10+ @code {
11+ private SfDiagramComponent _diagramComponent ;
12+ private DiagramObjectCollection <Node > _nodes = new DiagramObjectCollection <Node >();
13+ private DiagramObjectCollection <Connector > _connectors = new DiagramObjectCollection <Connector >();
14+
15+ private ForceDirectedTreeLayoutSettings _layoutSettings = new ForceDirectedTreeLayoutSettings
16+ {
17+ ConnectorLength = 120 ,
18+ MaximumIteration = 1500 ,
19+ RepulsionStrength = 20000 ,
20+ AttractionStrength = 0 . 8
21+ };
22+
23+ private int _departmentsUnderCeo = 4 ;
24+ private int _managersPerDepartment = 4 ;
25+ private int _teamsPerManager = 6 ;
26+
27+ protected override void OnInitialized ()
28+ {
29+ InitializeDiagram ();
30+ }
31+
32+ private void InitializeDiagram ()
33+ {
34+ List <OrganizationItem > organizationData = GetCompanyOrganizationData ();
35+ PopulateDiagramFromOrganizationData (organizationData );
36+ }
37+
38+ private void PopulateDiagramFromOrganizationData (IEnumerable < OrganizationItem > organizationItems )
39+ {
40+ Dictionary <string , OrganizationItem > itemsById = organizationItems .ToDictionary (item => item .Id );
41+ foreach (OrganizationItem item in organizationItems )
42+ {
43+ Node node = CreateOrganizationNode (item );
44+ _nodes ! .Add (node );
45+ if (! string .IsNullOrEmpty (item .ParentId ) && itemsById .ContainsKey (item .ParentId ))
46+ {
47+ _connectors ! .Add (CreateNodeConnector (item .ParentId , item .Id ));
48+ }
49+ }
50+ }
51+
52+ private void OnCreated ()
53+ {
54+ FitOptions options = new FitOptions () { Mode = FitMode .Both , Region = DiagramRegion .Content };
55+ _diagramComponent .FitToPage (options );
56+ }
57+
58+ private Node CreateOrganizationNode (OrganizationItem item )
59+ {
60+ ShapeStyle nodeStyle = new ShapeStyle { Fill = " orange" , StrokeWidth = 2 , StrokeColor = " #8c8c8c" };
61+ double nodeWidth = 35 ;
62+ double nodeHeight = 35 ;
63+ Shape nodeShape = new BasicShape () { Shape = NodeBasicShapes .Ellipse };
64+ switch (item .Level )
65+ {
66+ case OrganizationLevel .Header :
67+ nodeStyle .Fill = " #f39c12" ;
68+ nodeWidth = nodeHeight = 140 ;
69+ nodeShape = new BasicShape { Shape = NodeBasicShapes .Ellipse };
70+ break ;
71+ case OrganizationLevel .Department :
72+ nodeStyle .Fill = " #27ae60" ;
73+ nodeWidth = nodeHeight = 120 ;
74+ nodeShape = new BasicShape { Shape = NodeBasicShapes .Ellipse };
75+ break ;
76+ case OrganizationLevel .Manager :
77+ nodeStyle .Fill = " #2980b9" ;
78+ nodeShape = new BasicShape { Shape = NodeBasicShapes .Ellipse };
79+ nodeWidth = nodeHeight = 100 ;
80+ break ;
81+ case OrganizationLevel .Team :
82+ nodeStyle .Fill = " #f39c12" ;
83+ nodeShape = new BasicShape { Shape = NodeBasicShapes .Ellipse };
84+ nodeWidth = nodeHeight = 80 ;
85+ break ;
86+ }
87+ return new Node
88+ {
89+ ID = item .Id ,
90+ Width = nodeWidth ,
91+ Height = nodeHeight ,
92+ Shape = nodeShape ,
93+ Annotations = new DiagramObjectCollection <ShapeAnnotation >
94+ {
95+ new ShapeAnnotation
96+ {
97+ ID = $" {item .Id }_label" ,
98+ Content = item .Name ,
99+ Style = new TextStyle () { FontSize = 18 , Bold = true , Color = " white" }
100+ }
101+ },
102+ Style = nodeStyle
103+ };
104+ }
105+
106+ private Connector CreateNodeConnector (string sourceNodeId , string targetNodeId )
107+ {
108+ return new Connector
109+ {
110+ ID = $" {sourceNodeId }_{targetNodeId }" ,
111+ SourceID = sourceNodeId ,
112+ TargetID = targetNodeId ,
113+ Type = ConnectorSegmentType .Straight
114+ };
115+ }
116+
117+ public enum OrganizationLevel
118+ {
119+ Header ,
120+ Department ,
121+ Manager ,
122+ Team
123+ }
124+
125+ public class OrganizationItem
126+ {
127+ public string Id { get ; set ; } = " " ;
128+ public string ? ParentId { get ; set ; }
129+ public string Name { get ; set ; } = " " ;
130+ public OrganizationLevel Level { get ; set ; }
131+ }
132+
133+ private static List <OrganizationItem > BuildOrganizationData (int departmentCount , int managersPerDepartment , int teamsPerManager )
134+ {
135+ List <OrganizationItem > organizationData = new List <OrganizationItem >
136+ {
137+ new OrganizationItem { Id = " departments" , ParentId = null , Name = " Departments" , Level = OrganizationLevel .Header }
138+ };
139+ // Create departments
140+ for (int departmentIndex = 1 ; departmentIndex <= departmentCount ; departmentIndex ++ )
141+ {
142+ string departmentId = $" dept_{departmentIndex }" ;
143+ organizationData .Add (new OrganizationItem { Id = departmentId , ParentId = " departments" , Name = $" Department {departmentIndex }" , Level = OrganizationLevel .Department });
144+ // Create managers for each department
145+ for (int managerIndex = 1 ; managerIndex <= managersPerDepartment ; managerIndex ++ )
146+ {
147+ string managerId = $" {departmentId }_mgr_{managerIndex }" ;
148+ organizationData .Add (new OrganizationItem { Id = managerId , ParentId = departmentId , Name = $" Manager {departmentIndex }.{managerIndex }" , Level = OrganizationLevel .Manager });
149+ // Create teams for each manager
150+ for (int teamIndex = 1 ; teamIndex <= teamsPerManager ; teamIndex ++ )
151+ {
152+ string teamId = $" {managerId }_team_{teamIndex }" ;
153+ organizationData .Add (new OrganizationItem
154+ {
155+ Id = teamId ,
156+ ParentId = managerId ,
157+ Name = $" Team {departmentIndex }.{managerIndex }.{teamIndex }" ,
158+ Level = OrganizationLevel .Team
159+ });
160+ }
161+ }
162+ }
163+ return organizationData ;
164+ }
165+
166+ // Realistic software company hierarchy: 1 CEO with 4 departments, each with managers and their respective teams
167+ private static List <OrganizationItem > GetCompanyOrganizationData ()
168+ {
169+ List <OrganizationItem > companyData = new List <OrganizationItem >();
170+ // CEO level
171+ companyData .Add (new OrganizationItem { Id = " departments" , ParentId = null , Name = " Departments" , Level = OrganizationLevel .Header });
172+ // Department level (Level 2)
173+ List <OrganizationItem > departmentList = new List <OrganizationItem >
174+ {
175+ new OrganizationItem { Id = " uxTeam" , ParentId = " departments" , Name = " UX Team" , Level = OrganizationLevel .Department },
176+ new OrganizationItem { Id = " devTeam" , ParentId = " departments" , Name = " Development Team" , Level = OrganizationLevel .Department },
177+ new OrganizationItem { Id = " salesTeam" , ParentId = " departments" , Name = " Sales Team" , Level = OrganizationLevel .Department },
178+ new OrganizationItem { Id = " hrTeam" , ParentId = " departments" , Name = " HR Team" , Level = OrganizationLevel .Department }
179+ };
180+ foreach (OrganizationItem department in departmentList )
181+ companyData .Add (department );
182+ // Managers per department (Level 3) - 4 managers per department
183+ Dictionary <string , (string id, string name)[] > managersByDepartment = new Dictionary <string , (string id , string name )[]>
184+ {
185+ [" uxTeam" ] = new []
186+ {
187+ (" mgr1" , " Manager-1" ),
188+ (" mgr2" , " Manager-2" ),
189+ },
190+ [" devTeam" ] = new []
191+ {
192+ (" po1" , " PO-1" ),
193+ (" po2" , " PO-2" ),
194+ (" po3" , " PO-3" ),
195+ (" po4" , " PO-4" ),
196+ },
197+ [" salesTeam" ] = new []
198+ {
199+ (" agm1" , " AGM-1" ),
200+ (" agm2" , " AGM-2" ),
201+ },
202+ [" hrTeam" ] = new []
203+ {
204+ (" hr_mgr" , " Manager" ),
205+ },
206+ };
207+ foreach (string departmentKey in managersByDepartment .Keys )
208+ {
209+ foreach ((string id , string name ) manager in managersByDepartment [departmentKey ])
210+ companyData .Add (new OrganizationItem { Id = manager .id , ParentId = departmentKey , Name = manager .name , Level = OrganizationLevel .Manager });
211+ }
212+ // Teams per manager (Level 4) - Variable count based on requirements
213+ Dictionary <string , int > teamCountByManagerId = new Dictionary <string , int >
214+ {
215+ [" mgr1" ] = 3 ,
216+ [" mgr2" ] = 3 ,
217+ [" po1" ] = 4 ,
218+ [" po2" ] = 4 ,
219+ [" po3" ] = 4 ,
220+ [" po4" ] = 4 ,
221+ [" agm1" ] = 3 ,
222+ [" agm2" ] = 3 ,
223+ [" hr_mgr" ] = 2 ,
224+ };
225+ foreach (string departmentKey in managersByDepartment .Keys )
226+ {
227+ foreach ((string managerId , string managerName ) manager in managersByDepartment [departmentKey ])
228+ {
229+ int teamCount = teamCountByManagerId .ContainsKey (manager .managerId ) ? teamCountByManagerId [manager .managerId ] : 0 ;
230+ for (int teamIndex = 1 ; teamIndex <= teamCount ; teamIndex ++ )
231+ {
232+ string teamId = $" {manager .managerId }_t{teamIndex }" ;
233+ companyData .Add (new OrganizationItem
234+ {
235+ Id = teamId ,
236+ ParentId = manager .managerId ,
237+ Name = $" Team-{teamIndex }" ,
238+ Level = OrganizationLevel .Team
239+ });
240+ }
241+ }
242+ }
243+ return companyData ;
244+ }
245+ }
0 commit comments