freya_components/
graph.rs1use dioxus::prelude::*;
2use freya_core::{
3 custom_attributes::CanvasRunnerContext,
4 parsing::Parse,
5};
6use freya_elements as dioxus_elements;
7use freya_engine::prelude::*;
8use freya_hooks::{
9 use_applied_theme,
10 use_canvas_with_deps,
11 use_node_signal,
12 use_platform,
13 GraphTheme,
14 GraphThemeWith,
15};
16
17#[derive(Debug, PartialEq, Clone)]
19pub struct GraphLine {
20 color: String,
21 points: Vec<Option<i32>>,
22}
23
24impl GraphLine {
25 pub fn new(color: &str, points: Vec<Option<i32>>) -> Self {
26 Self {
27 color: color.to_string(),
28 points,
29 }
30 }
31}
32
33#[derive(Debug, Props, PartialEq, Clone)]
35pub struct GraphProps {
36 pub theme: Option<GraphThemeWith>,
38 labels: Vec<String>,
40 data: Vec<GraphLine>,
42}
43
44#[allow(non_snake_case)]
46pub fn Graph(props: GraphProps) -> Element {
47 let platform = use_platform();
48 let (reference, size) = use_node_signal();
49 let GraphTheme { width, height } = use_applied_theme!(&props.theme, graph);
50
51 let canvas = use_canvas_with_deps(&props, move |props| {
52 platform.invalidate_drawing_area(size.peek().area);
53 platform.request_animation_frame();
54 move |ctx: &mut CanvasRunnerContext| {
55 ctx.canvas.translate((ctx.area.min_x(), ctx.area.min_y()));
56
57 let mut paragraph_style = ParagraphStyle::default();
58 paragraph_style.set_text_align(TextAlign::Center);
59
60 let mut text_style = TextStyle::new();
61 text_style.set_color(Color::BLACK);
62 text_style.set_font_size(16. * ctx.scale_factor);
63 paragraph_style.set_text_style(&text_style);
64
65 let x_labels = &props.labels;
66 let x_height: f32 = 50.0;
67
68 let start_x = ctx.area.min_x();
69 let start_y = ctx.area.height() - x_height;
70 let height = ctx.area.height() - x_height;
71
72 let space_x = ctx.area.width() / x_labels.len() as f32;
73
74 let (smallest_y, biggest_y) = {
76 let mut smallest_y = 0;
77 let mut biggest_y = 0;
78 for line in props.data.iter() {
79 let max = line.points.iter().max().unwrap();
80 let min = line.points.iter().min().unwrap();
81
82 if let Some(max) = *max {
83 if max > biggest_y {
84 biggest_y = max;
85 }
86 }
87 if let Some(min) = *min {
88 if min < smallest_y {
89 smallest_y = min;
90 }
91 }
92 }
93
94 (smallest_y, biggest_y)
95 };
96
97 let y_axis_len = biggest_y - smallest_y;
99 let space_y = height / y_axis_len as f32;
101
102 for line in &props.data {
104 let mut paint = Paint::default();
105
106 paint.set_anti_alias(true);
107 paint.set_style(PaintStyle::Fill);
108 paint.set_color(Color::parse(&line.color).unwrap());
109 paint.set_stroke_width(3.0 * ctx.scale_factor);
110
111 let mut previous_x = None;
112 let mut previous_y = None;
113
114 for (i, y_point) in line.points.iter().enumerate() {
115 let line_x = (space_x * i as f32) + start_x + (space_x / 2.0);
116 let new_previous_x = previous_x.unwrap_or(line_x);
118
119 if let Some(y_point) = y_point {
120 let line_y = start_y - (space_y * ((y_point - smallest_y) as f32));
121 let new_previous_y = previous_y.unwrap_or(line_y);
122
123 ctx.canvas
125 .draw_circle((line_x, line_y), 5.0 * ctx.scale_factor, &paint);
126 ctx.canvas.draw_line(
127 (new_previous_x, new_previous_y),
128 (line_x, line_y),
129 &paint,
130 );
131
132 previous_y = Some(line_y);
133 previous_x = Some(line_x);
134 } else {
135 previous_y = None;
136 previous_x = None;
137 }
138 }
139 }
140
141 let space_x = ctx.area.width() / x_labels.len() as f32;
143
144 for (i, point) in x_labels.iter().enumerate() {
146 let x = (space_x * i as f32) + start_x;
147
148 let mut paragrap_builder =
149 ParagraphBuilder::new(¶graph_style, ctx.font_collection.clone());
150 paragrap_builder.add_text(point);
151 let mut text = paragrap_builder.build();
152
153 text.layout(space_x);
154 text.paint(ctx.canvas, (x, start_y + x_height - 30.0));
155 }
156
157 ctx.canvas.restore();
158 }
159 });
160
161 rsx!(
162 rect {
163 width: "{width}",
164 height: "{height}",
165 padding: "15 5",
166 background: "white",
167 rect {
168 canvas_reference: canvas.attribute(),
169 reference,
170 width: "100%",
171 height: "100%",
172 }
173 }
174 )
175}