Skip to main content

dioxus_flow/
background.rs

1//! The canvas background pattern (dots, lines, or crosses), kept in lockstep
2//! with the viewport transform.
3
4use dioxus::prelude::*;
5
6use crate::state::FlowCore;
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
9pub enum BackgroundVariant {
10    #[default]
11    Dots,
12    Lines,
13    Cross,
14}
15
16/// A pan/zoom-aware background pattern. Render as a child of
17/// [`crate::Flow`].
18#[component]
19pub fn Background(
20    #[props(default)] variant: BackgroundVariant,
21    /// Grid spacing in flow units.
22    #[props(default = 24.0)]
23    gap: f64,
24    /// Dot radius / line width in flow units.
25    #[props(default = 1.0)]
26    size: f64,
27    class: Option<String>,
28) -> Element {
29    let core = use_context::<FlowCore>();
30    let vp = *core.viewport.read();
31    let scaled = (gap * vp.zoom).max(1.0);
32    let x = vp.x.rem_euclid(scaled);
33    let y = vp.y.rem_euclid(scaled);
34    let pattern_id = format!("df-bg-{}", core.iid);
35    let class = format!(
36        "df-background{}",
37        class
38            .as_deref()
39            .map(|c| format!(" {c}"))
40            .unwrap_or_default()
41    );
42
43    rsx! {
44        svg { class,
45            defs {
46                pattern {
47                    id: "{pattern_id}",
48                    x,
49                    y,
50                    width: scaled,
51                    height: scaled,
52                    "patternUnits": "userSpaceOnUse",
53                    match variant {
54                        BackgroundVariant::Dots => rsx! {
55                            circle {
56                                class: "df-background-dot",
57                                cx: scaled / 2.0,
58                                cy: scaled / 2.0,
59                                r: (size * vp.zoom).max(0.4),
60                            }
61                        },
62                        BackgroundVariant::Lines => rsx! {
63                            path {
64                                class: "df-background-line",
65                                d: "M {scaled} 0 H 0 V {scaled}",
66                                fill: "none",
67                                stroke_width: (size * vp.zoom).max(0.3),
68                            }
69                        },
70                        BackgroundVariant::Cross => rsx! {
71                            path {
72                                class: "df-background-line",
73                                d: {
74                                    let c = scaled / 2.0;
75                                    let arm = (3.0 * vp.zoom).max(1.5);
76                                    format!(
77                                        "M {} {c} H {} M {c} {} V {}",
78                                        c - arm,
79                                        c + arm,
80                                        c - arm,
81                                        c + arm,
82                                    )
83                                },
84                                fill: "none",
85                                stroke_width: (size * vp.zoom).max(0.3),
86                            }
87                        },
88                    }
89                }
90            }
91            rect {
92                width: "100%",
93                height: "100%",
94                fill: "url(#{pattern_id})",
95            }
96        }
97    }
98}