1mod layout;
4mod node;
5mod reconcile;
6mod theme;
7
8pub use layout::measure_flowchart;
9pub(crate) use node::FlowchartItemEvent;
10pub use node::FlowchartNode;
11pub(crate) use node::PositionedEdge;
12pub(crate) use node::flowchart_local_content_point;
13pub use reconcile::reconcile_flowchart;
14pub use theme::FlowchartTheme;
15
16use std::collections::HashMap;
17use std::sync::Arc;
18
19use crate::callback::Callback;
20use crate::core::element::{Element, ElementKind};
21use crate::style::{BorderStyle, Length, Padding, Style};
22
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
25pub enum FlowDirection {
26 #[default]
28 TopDown,
29 BottomUp,
31 LeftRight,
33 RightLeft,
35}
36
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
39pub enum NodeShape {
40 #[default]
42 Rect,
43 Round,
45 Stadium,
47 Subroutine,
49 Cylinder,
51 Circle,
53 Asymmetric,
55 Diamond,
57 Hexagon,
59 Parallelogram,
61 ParallelogramAlt,
63 Trapezoid,
65 TrapezoidAlt,
67 DoubleCircle,
69}
70
71#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
73pub enum EdgeStyle {
74 #[default]
76 Solid,
77 Dashed,
79 Thick,
81 Invisible,
83}
84
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
87pub enum EdgeArrow {
88 None,
90 Open,
92 #[default]
94 Filled,
95 Cross,
97 Circle,
99}
100
101#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
103pub struct NodeId(Arc<str>);
104
105impl NodeId {
106 pub fn new(id: impl Into<Arc<str>>) -> Self {
108 Self(id.into())
109 }
110
111 pub fn as_str(&self) -> &str {
113 &self.0
114 }
115}
116
117impl From<&str> for NodeId {
118 fn from(value: &str) -> Self {
119 Self::new(value)
120 }
121}
122
123impl From<String> for NodeId {
124 fn from(value: String) -> Self {
125 Self::new(Arc::<str>::from(value))
126 }
127}
128
129impl From<Arc<str>> for NodeId {
130 fn from(value: Arc<str>) -> Self {
131 Self::new(value)
132 }
133}
134
135impl AsRef<str> for NodeId {
136 fn as_ref(&self) -> &str {
137 self.as_str()
138 }
139}
140
141impl std::fmt::Display for NodeId {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 f.write_str(self.as_str())
144 }
145}
146
147#[derive(Clone, Debug, PartialEq, Eq, Hash)]
149pub struct Edge {
150 pub from: NodeId,
152 pub to: NodeId,
154 pub label: Option<Arc<str>>,
156 pub style: EdgeStyle,
158 pub head_from: EdgeArrow,
160 pub head_to: EdgeArrow,
162 pub line_style: Option<Style>,
164 pub label_style: Option<Style>,
166}
167
168impl Edge {
169 pub fn solid(from: impl Into<NodeId>, to: impl Into<NodeId>) -> Self {
171 Self::new(from, to, EdgeStyle::Solid)
172 }
173
174 pub fn dashed(from: impl Into<NodeId>, to: impl Into<NodeId>) -> Self {
176 Self::new(from, to, EdgeStyle::Dashed)
177 }
178
179 pub fn thick(from: impl Into<NodeId>, to: impl Into<NodeId>) -> Self {
181 Self::new(from, to, EdgeStyle::Thick)
182 }
183
184 pub fn invisible(from: impl Into<NodeId>, to: impl Into<NodeId>) -> Self {
186 Self::new(from, to, EdgeStyle::Invisible).arrow_to(EdgeArrow::None)
187 }
188
189 pub fn new(from: impl Into<NodeId>, to: impl Into<NodeId>, style: EdgeStyle) -> Self {
191 Self {
192 from: from.into(),
193 to: to.into(),
194 label: None,
195 style,
196 head_from: EdgeArrow::None,
197 head_to: EdgeArrow::Filled,
198 line_style: None,
199 label_style: None,
200 }
201 }
202
203 pub fn label(mut self, label: impl Into<Arc<str>>) -> Self {
205 self.label = Some(label.into());
206 self
207 }
208
209 pub fn arrow_from(mut self, arrow: EdgeArrow) -> Self {
211 self.head_from = arrow;
212 self
213 }
214
215 pub fn arrow_to(mut self, arrow: EdgeArrow) -> Self {
217 self.head_to = arrow;
218 self
219 }
220
221 pub fn arrows(mut self, from: EdgeArrow, to: EdgeArrow) -> Self {
223 self.head_from = from;
224 self.head_to = to;
225 self
226 }
227
228 pub fn line_style(mut self, style: Style) -> Self {
230 self.line_style = Some(style);
231 self
232 }
233
234 pub fn label_style(mut self, style: Style) -> Self {
236 self.label_style = Some(style);
237 self
238 }
239}
240
241#[derive(Clone, Debug, PartialEq, Eq, Hash)]
243pub struct FlowchartNodeEvent {
244 pub id: NodeId,
246 pub label: Arc<str>,
248}
249
250#[derive(Clone, Debug, PartialEq, Eq, Hash)]
252pub struct FlowchartEdgeEvent {
253 pub from: NodeId,
255 pub to: NodeId,
257 pub label: Option<Arc<str>>,
259}
260
261#[derive(Clone, Debug, PartialEq, Eq, Hash)]
263pub struct FlowchartSubgraphEvent {
264 pub id: NodeId,
266 pub label: Arc<str>,
268}
269
270#[derive(Clone, Debug, PartialEq, Eq, Hash)]
272pub enum FlowchartItemPath {
273 Node(NodeId),
275 Edge(usize),
277 Subgraph(NodeId),
279}
280
281#[derive(Clone, Debug, PartialEq, Eq, Hash)]
282pub(crate) struct FlowchartNodeSpec {
283 pub(crate) id: NodeId,
284 pub(crate) label: Arc<str>,
285 pub(crate) shape: NodeShape,
286 pub(crate) style: Style,
287 pub(crate) hover_style: Style,
288 pub(crate) parent: Option<NodeId>,
289}
290
291#[derive(Clone, Debug, PartialEq, Eq, Hash)]
292pub(crate) struct FlowchartSubgraphSpec {
293 pub(crate) id: NodeId,
294 pub(crate) label: Arc<str>,
295 pub(crate) parent: Option<NodeId>,
296 pub(crate) style: Style,
297}
298
299#[derive(Clone)]
301pub struct Flowchart {
302 pub(crate) direction: FlowDirection,
303 pub(crate) nodes: Arc<[FlowchartNodeSpec]>,
304 pub(crate) edges: Arc<[Edge]>,
305 pub(crate) subgraphs: Arc<[FlowchartSubgraphSpec]>,
306 pub(crate) class_defs: Arc<HashMap<Arc<str>, Style>>,
307 pub(crate) class_assignments: Arc<HashMap<NodeId, Arc<str>>>,
308 pub(crate) style: Style,
309 pub(crate) node_style: Style,
310 pub(crate) edge_style: Style,
311 pub(crate) subgraph_style: Style,
312 pub(crate) label_style: Style,
313 pub(crate) item_hover_style: Style,
314 pub(crate) border: bool,
315 pub(crate) border_style: BorderStyle,
316 pub(crate) padding: Padding,
317 pub(crate) node_gap: u16,
318 pub(crate) layer_gap: u16,
319 pub(crate) subgraph_padding: Padding,
320 pub(crate) max_node_width: u16,
321 pub(crate) theme: FlowchartTheme,
322 pub(crate) on_node_click: Option<Callback<FlowchartNodeEvent>>,
323 pub(crate) on_edge_click: Option<Callback<FlowchartEdgeEvent>>,
324 pub(crate) on_subgraph_click: Option<Callback<FlowchartSubgraphEvent>>,
325 pub(crate) on_node_hover: Option<Callback<FlowchartNodeEvent>>,
326 pub(crate) on_edge_hover: Option<Callback<FlowchartEdgeEvent>>,
327 pub(crate) on_subgraph_hover: Option<Callback<FlowchartSubgraphEvent>>,
328 pub(crate) width: Length,
330 pub(crate) height: Length,
332}
333
334impl Default for Flowchart {
335 fn default() -> Self {
336 Self {
337 direction: FlowDirection::TopDown,
338 nodes: Arc::new([]),
339 edges: Arc::new([]),
340 subgraphs: Arc::new([]),
341 class_defs: Arc::new(HashMap::new()),
342 class_assignments: Arc::new(HashMap::new()),
343 style: Style::default(),
344 node_style: Style::default(),
345 edge_style: Style::default(),
346 subgraph_style: Style::default(),
347 label_style: Style::default(),
348 item_hover_style: Style::default(),
349 border: false,
350 border_style: BorderStyle::Plain,
351 padding: Padding::default(),
352 node_gap: 4,
353 layer_gap: 3,
354 subgraph_padding: (1, 2).into(),
355 max_node_width: 24,
356 theme: FlowchartTheme::classic(),
357 on_node_click: None,
358 on_edge_click: None,
359 on_subgraph_click: None,
360 on_node_hover: None,
361 on_edge_hover: None,
362 on_subgraph_hover: None,
363 width: Length::Auto,
364 height: Length::Auto,
365 }
366 }
367}
368
369impl Flowchart {
370 pub fn new(direction: FlowDirection) -> Self {
372 Self {
373 direction,
374 ..Self::default()
375 }
376 }
377
378 pub fn node(
380 mut self,
381 id: impl Into<NodeId>,
382 label: impl Into<Arc<str>>,
383 shape: NodeShape,
384 ) -> Self {
385 self.push_node(
386 id.into(),
387 label.into(),
388 shape,
389 None,
390 Style::default(),
391 Style::default(),
392 );
393 self
394 }
395
396 pub fn styled_node(
398 mut self,
399 id: impl Into<NodeId>,
400 label: impl Into<Arc<str>>,
401 shape: NodeShape,
402 style: Style,
403 ) -> Self {
404 self.push_node(
405 id.into(),
406 label.into(),
407 shape,
408 None,
409 style,
410 Style::default(),
411 );
412 self
413 }
414
415 pub fn node_hover_style(mut self, id: impl Into<NodeId>, style: Style) -> Self {
417 let id = id.into();
418 let mut nodes = self.nodes.to_vec();
419 if let Some(node) = nodes.iter_mut().find(|node| node.id == id) {
420 node.hover_style = style;
421 }
422 self.nodes = nodes.into();
423 self
424 }
425
426 pub fn edge(mut self, edge: Edge) -> Self {
428 let mut edges = self.edges.to_vec();
429 edges.push(edge);
430 self.edges = edges.into();
431 self
432 }
433
434 pub fn subgraph(
436 mut self,
437 id: impl Into<NodeId>,
438 label: impl Into<Arc<str>>,
439 build: impl FnOnce(FlowchartSubgraphBuilder) -> FlowchartSubgraphBuilder,
440 ) -> Self {
441 let id = id.into();
442 let label = label.into();
443 let builder = build(FlowchartSubgraphBuilder::new(id.clone()));
444 self.push_subgraph(id.clone(), label, None, Style::default());
445 for node in builder.nodes {
446 let parent = node.parent.or_else(|| Some(id.clone()));
447 self.push_node(
448 node.id,
449 node.label,
450 node.shape,
451 parent,
452 node.style,
453 node.hover_style,
454 );
455 }
456 for mut subgraph in builder.subgraphs {
457 if subgraph.parent.is_none() {
458 subgraph.parent = Some(id.clone());
459 }
460 self.push_subgraph(subgraph.id, subgraph.label, subgraph.parent, subgraph.style);
461 }
462 let mut edges = self.edges.to_vec();
463 edges.extend(builder.edges);
464 self.edges = edges.into();
465 self
466 }
467
468 pub fn class_def(mut self, name: impl Into<Arc<str>>, style: Style) -> Self {
470 let mut class_defs = (*self.class_defs).clone();
471 class_defs.insert(name.into(), style);
472 self.class_defs = Arc::new(class_defs);
473 self
474 }
475
476 pub fn assign_class(mut self, id: impl Into<NodeId>, class: impl Into<Arc<str>>) -> Self {
478 let mut assignments = (*self.class_assignments).clone();
479 assignments.insert(id.into(), class.into());
480 self.class_assignments = Arc::new(assignments);
481 self
482 }
483
484 pub fn style(mut self, style: Style) -> Self {
486 self.style = style;
487 self
488 }
489
490 pub fn node_style(mut self, style: Style) -> Self {
492 self.node_style = style;
493 self
494 }
495
496 pub fn edge_style(mut self, style: Style) -> Self {
498 self.edge_style = style;
499 self
500 }
501
502 pub fn subgraph_style(mut self, style: Style) -> Self {
504 self.subgraph_style = style;
505 self
506 }
507
508 pub fn label_style(mut self, style: Style) -> Self {
510 self.label_style = style;
511 self
512 }
513
514 pub fn item_hover_style(mut self, style: Style) -> Self {
516 self.item_hover_style = style;
517 self
518 }
519
520 pub fn theme(mut self, theme: FlowchartTheme) -> Self {
522 self.theme = theme;
523 self
524 }
525
526 pub fn border(mut self, border: bool) -> Self {
528 self.border = border;
529 self
530 }
531
532 pub fn border_style(mut self, border_style: BorderStyle) -> Self {
534 self.border_style = border_style;
535 self
536 }
537
538 pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
540 self.padding = padding.into();
541 self
542 }
543
544 pub fn node_gap(mut self, gap: u16) -> Self {
546 self.node_gap = gap;
547 self
548 }
549
550 pub fn layer_gap(mut self, gap: u16) -> Self {
552 self.layer_gap = gap;
553 self
554 }
555
556 pub fn subgraph_padding(mut self, padding: impl Into<Padding>) -> Self {
558 self.subgraph_padding = padding.into();
559 self
560 }
561
562 pub fn max_node_width(mut self, width: u16) -> Self {
564 self.max_node_width = width.max(1);
565 self
566 }
567
568 pub fn width(mut self, width: Length) -> Self {
570 self.width = width;
571 self
572 }
573
574 pub fn height(mut self, height: Length) -> Self {
576 self.height = height;
577 self
578 }
579
580 pub fn on_node_click(mut self, cb: Callback<FlowchartNodeEvent>) -> Self {
582 self.on_node_click = Some(cb);
583 self
584 }
585
586 pub fn on_edge_click(mut self, cb: Callback<FlowchartEdgeEvent>) -> Self {
588 self.on_edge_click = Some(cb);
589 self
590 }
591
592 pub fn on_subgraph_click(mut self, cb: Callback<FlowchartSubgraphEvent>) -> Self {
594 self.on_subgraph_click = Some(cb);
595 self
596 }
597
598 pub fn on_node_hover(mut self, cb: Callback<FlowchartNodeEvent>) -> Self {
600 self.on_node_hover = Some(cb);
601 self
602 }
603
604 pub fn on_edge_hover(mut self, cb: Callback<FlowchartEdgeEvent>) -> Self {
606 self.on_edge_hover = Some(cb);
607 self
608 }
609
610 pub fn on_subgraph_hover(mut self, cb: Callback<FlowchartSubgraphEvent>) -> Self {
612 self.on_subgraph_hover = Some(cb);
613 self
614 }
615
616 fn push_node(
617 &mut self,
618 id: NodeId,
619 label: Arc<str>,
620 shape: NodeShape,
621 parent: Option<NodeId>,
622 style: Style,
623 hover_style: Style,
624 ) {
625 let mut nodes = self.nodes.to_vec();
626 if let Some(existing) = nodes.iter_mut().find(|node| node.id == id) {
627 *existing = FlowchartNodeSpec {
628 id,
629 label,
630 shape,
631 style,
632 hover_style,
633 parent,
634 };
635 } else {
636 nodes.push(FlowchartNodeSpec {
637 id,
638 label,
639 shape,
640 style,
641 hover_style,
642 parent,
643 });
644 }
645 self.nodes = nodes.into();
646 }
647
648 fn push_subgraph(&mut self, id: NodeId, label: Arc<str>, parent: Option<NodeId>, style: Style) {
649 let mut subgraphs = self.subgraphs.to_vec();
650 if let Some(existing) = subgraphs.iter_mut().find(|subgraph| subgraph.id == id) {
651 *existing = FlowchartSubgraphSpec {
652 id,
653 label,
654 parent,
655 style,
656 };
657 } else {
658 subgraphs.push(FlowchartSubgraphSpec {
659 id,
660 label,
661 parent,
662 style,
663 });
664 }
665 self.subgraphs = subgraphs.into();
666 }
667}
668
669pub struct FlowchartSubgraphBuilder {
671 parent: NodeId,
672 nodes: Vec<FlowchartNodeSpec>,
673 edges: Vec<Edge>,
674 subgraphs: Vec<FlowchartSubgraphSpec>,
675}
676
677impl FlowchartSubgraphBuilder {
678 fn new(parent: NodeId) -> Self {
679 Self {
680 parent,
681 nodes: Vec::new(),
682 edges: Vec::new(),
683 subgraphs: Vec::new(),
684 }
685 }
686
687 pub fn node(
689 mut self,
690 id: impl Into<NodeId>,
691 label: impl Into<Arc<str>>,
692 shape: NodeShape,
693 ) -> Self {
694 self.nodes.push(FlowchartNodeSpec {
695 id: id.into(),
696 label: label.into(),
697 shape,
698 style: Style::default(),
699 hover_style: Style::default(),
700 parent: Some(self.parent.clone()),
701 });
702 self
703 }
704
705 pub fn styled_node(
707 mut self,
708 id: impl Into<NodeId>,
709 label: impl Into<Arc<str>>,
710 shape: NodeShape,
711 style: Style,
712 ) -> Self {
713 self.nodes.push(FlowchartNodeSpec {
714 id: id.into(),
715 label: label.into(),
716 shape,
717 style,
718 hover_style: Style::default(),
719 parent: Some(self.parent.clone()),
720 });
721 self
722 }
723
724 pub fn node_hover_style(mut self, id: impl Into<NodeId>, style: Style) -> Self {
726 let id = id.into();
727 if let Some(node) = self.nodes.iter_mut().find(|node| node.id == id) {
728 node.hover_style = style;
729 }
730 self
731 }
732
733 pub fn edge(mut self, edge: Edge) -> Self {
735 self.edges.push(edge);
736 self
737 }
738
739 pub fn subgraph(
741 mut self,
742 id: impl Into<NodeId>,
743 label: impl Into<Arc<str>>,
744 build: impl FnOnce(FlowchartSubgraphBuilder) -> FlowchartSubgraphBuilder,
745 ) -> Self {
746 let id = id.into();
747 let nested = build(FlowchartSubgraphBuilder::new(id.clone()));
748 self.subgraphs.push(FlowchartSubgraphSpec {
749 id: id.clone(),
750 label: label.into(),
751 parent: Some(self.parent.clone()),
752 style: Style::default(),
753 });
754 self.nodes.extend(nested.nodes);
755 self.edges.extend(nested.edges);
756 self.subgraphs
757 .extend(nested.subgraphs.into_iter().map(|mut sub| {
758 if sub.parent.is_none() {
759 sub.parent = Some(id.clone());
760 }
761 sub
762 }));
763 self
764 }
765}
766
767impl From<Flowchart> for Element {
768 fn from(value: Flowchart) -> Self {
769 Element::new(ElementKind::Flowchart(Box::new(value)))
770 }
771}