zen_rs/layouts/html/
icon.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! This module contains the `icon_html` function for rendering an `Icon` component
//! into an SVG string with specified styles and attributes.
//!
//! The `icon_html` function takes an `Icon` component, extracts its properties
//! (such as foreground and background colors, width, height, viewBox, stroke
//! attributes, and paths), and generates an SVG element with the appropriate
//! attributes and content. The paths are wrapped inside `<path>` tags, and
//! the SVG attributes are applied accordingly, such as stroke and fill styles.
//!
//! # Example
//! ```rust
//! let icon = Icon::new()
//!     .foreground_color((255, 0, 0))
//!     .background_color((0, 0, 0))
//!     .width(24)
//!     .height(24)
//!     .content(vec!["M10 10 H 90 V 90 H 10 Z"]);
//! let svg = icon_html(&icon);
//! assert_eq!(svg, r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 100 100" fill="rgb(0, 0, 0)" stroke="rgb(255, 0, 0)" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"><path d="M10 10 H 90 V 90 H 10 Z" fill="rgb(0, 0, 0)" /></svg>"#);
//! ```

use crate::components::{icon::Icon, XMLNS};

/// Renders an `Icon` component into an SVG string with the specified attributes
/// such as foreground and background colors, width, height, viewBox, and paths.
///
/// # Arguments
/// * `component` - A reference to an `Icon` component containing the properties
///   such as content (paths), size, stroke attributes, and colors.
///
/// # Returns
/// A string representing the SVG element that renders the icon with the
/// specified properties and styles.
///
/// # Example
/// ```rust
/// let icon = Icon::new()
///     .foreground_color((255, 0, 0))
///     .background_color((0, 0, 0))
///     .width(24)
///     .height(24)
///     .content(vec!["M10 10 H 90 V 90 H 10 Z"]);
/// let svg = icon_html(&icon);
/// assert_eq!(svg, r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 100 100" fill="rgb(0, 0, 0)" stroke="rgb(255, 0, 0)" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"><path d="M10 10 H 90 V 90 H 10 Z" fill="rgb(0, 0, 0)" /></svg>"#);
/// ```
pub fn icon_html(component: &Icon) -> String {
    let xmlns = XMLNS;
    // data
    let fg = component.get_foreground_color().to_string();
    let bg = component.get_background_color().to_string();
    let w = component.get_width();
    let h = component.get_height();
    let (bl, bt, bb, br) = component.get_view_box();

    // content
    let content = component.get_content();

    // content formating
    let first_path_wrap = |path: &str| format!(r#"<path stroke="none" d="{path}" fill="{bg}" />"#);
    let path_wrap = |path: &str| format!(r#"<path d="{path}"/>"#);
    let first = if let Some(path) = content.first() {
        first_path_wrap(path)
    } else {
        "".to_string()
    };
    let content = &content[1..];
    let paths: String = content.iter().map(|x| path_wrap(x)).collect();

    // Specific svg attributes
    let slp = component
        .get_stroke_linecap()
        .map(|x| format!(r#"stroke-linecap="{x}""#))
        .unwrap_or_default();
    let slj = component
        .get_stroke_linejoin()
        .map(|x| format!(r#"stroke-linejoin="{x}""#))
        .unwrap_or_default();
    let sw = component
        .get_stroke_width()
        .map(|x| format!(r#"stroke-width="{x}""#))
        .unwrap_or_default();
    let vb = format!(r#"viewBox="{bl} {bt} {bb} {br}""#);

    // out
    format!(
        r#"
        <svg xmlns="{xmlns}"
        width="{w}"
        height="{h}"
        {vb}
        fill="{bg}"
        stroke="{fg}"
        {slp}
        {slj}
        {sw}>
        {first}
        {paths}</svg>
    "#
    )
}