dioxus_material_symbols/
lib.rs

1#![allow(non_snake_case)]
2#![warn(missing_docs)]
3
4//! # Dioxus Material Symbols
5//!
6//! This project provides a simple but configurable component to render Google's Material Symbols in Dioxus.
7
8use dioxus::prelude::*;
9
10/// Props for the [`MaterialIconStylesheet`](MaterialIconStylesheet) component
11#[derive(Props, PartialEq)]
12pub struct MaterialIconStylesheetProps<'a> {
13    /// Variant prop for the [`MaterialIconStylesheet`](MaterialIconStylesheet) component
14    ///
15    /// See [`MaterialIconVariant`](MaterialIconVariant) for more information.
16    #[props(default = MaterialIconVariant::Rounded)]
17    pub variant: MaterialIconVariant<'a>,
18}
19
20/// Variants (also called categories) of the Material Icon font
21///
22/// See all variants [here](https://fonts.google.com/icons).
23#[derive(PartialEq)]
24pub enum MaterialIconVariant<'a> {
25    /// Outlined
26    Outlined,
27    /// Rounded
28    Rounded,
29    /// Sharp
30    Sharp,
31    /// Self hosted font file
32    ///
33    /// Provide an url to a ttf or otf file.
34    /// You can download the files [here](https://github.com/google/material-design-icons/tree/master/font).
35    SelfHosted(&'a str),
36}
37
38/// Stylesheet component
39///
40/// This component includes the Material Symbols stylesheet.
41/// This is required to render all Material Symbols correctly.
42///
43/// You can provide a variant as a prop (e.g. Rounded).
44/// When you want to provide your own self-hosted font file,
45/// please use [`MaterialIconVariant::SelfHosted`](MaterialIconVariant::SelfHosted) and pass the
46/// file path or url to your .ttf or .otf file to it.
47/// See the [button example](https://github.com/lennartkloock/dioxus-material-symbols/blob/main/examples/button.rs).
48pub fn MaterialIconStylesheet<'a>(cx: Scope<'a, MaterialIconStylesheetProps<'a>>) -> Element<'a> {
49    let href = match &cx.props.variant {
50        MaterialIconVariant::SelfHosted(file) => {
51            return cx.render(rsx!(
52                style { format!(include_str!("./self-hosted-styles.css"), file) }
53            ));
54        }
55        MaterialIconVariant::Outlined => {
56            "https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined"
57        }
58        MaterialIconVariant::Rounded => {
59            "https://fonts.googleapis.com/icon?family=Material+Symbols+Rounded"
60        }
61        MaterialIconVariant::Sharp => {
62            "https://fonts.googleapis.com/icon?family=Material+Symbols+Sharp"
63        }
64    };
65    cx.render(rsx!(link {
66        href: "{href}",
67        rel: "stylesheet"
68    }))
69}
70
71/// Props for the [`MaterialIcon`](MaterialIcon) component
72#[derive(Props, PartialEq)]
73pub struct MaterialIconProps<'a> {
74    /// Name (e.g. `home`)
75    ///
76    /// Browse all symbols [here](https://fonts.google.com/symbols?selected=Material+Symbols).
77    pub name: &'a str,
78    /// Size in pixels
79    ///
80    /// Optional
81    pub size: Option<u32>,
82    /// Color
83    ///
84    /// Optional
85    #[props(into)]
86    pub color: Option<MaterialIconColor<'a>>,
87}
88
89/// Colors of Material Symbols
90///
91/// As described [here](https://developers.google.com/fonts/docs/material_symbols#styling_symbols_in_material_design).
92#[derive(PartialEq)]
93pub enum MaterialIconColor<'a> {
94    /// For using symbols as black on a light background.
95    Dark,
96    /// For using symbols as black on a light background.
97    DarkInactive,
98    /// For using symbols as white on a dark background.
99    Light,
100    /// For using symbols as white on a dark background.
101    LightInactive,
102    /// Custom color, any valid CSS color
103    ///
104    /// E.g.: `#0000ff` or `red`
105    Custom(&'a str),
106}
107
108impl<'a> From<&'a str> for MaterialIconColor<'a> {
109    fn from(value: &'a str) -> Self {
110        Self::Custom(value)
111    }
112}
113
114impl MaterialIconColor<'_> {
115    /// Converts the color to its corresponding CSS color
116    pub fn to_css_color(&self) -> &str {
117        match self {
118            MaterialIconColor::Dark => "rgba(0, 0, 0, 0.54)",
119            MaterialIconColor::DarkInactive => "rgba(0, 0, 0, 0.26)",
120            MaterialIconColor::Light => "rgba(255, 255, 255, 1)",
121            MaterialIconColor::LightInactive => "rgba(255, 255, 255, 0.3)",
122            MaterialIconColor::Custom(c) => c,
123        }
124    }
125}
126
127/// Material Icon component
128///
129/// This component can be used to render a Material Icon.
130pub fn MaterialIcon<'a>(cx: Scope<'a, MaterialIconProps<'a>>) -> Element<'a> {
131    // The `font-size` attribute has to be explicitly declared as `inherit` because the stylesheet sets a default of 24px
132    let css_size = cx
133        .props
134        .size
135        .map(|s| format!("{s}px"))
136        .unwrap_or_else(|| "inherit".to_string());
137    let css_color = cx
138        .props
139        .color
140        .as_ref()
141        .map(|c| format!("color: {};", c.to_css_color()))
142        .unwrap_or_default();
143    cx.render(rsx!(
144        span {
145            class: "material-symbols material-symbols-outlined material-symbols-rounded material-symbols-sharp md-48",
146            style: "font-size: {css_size}; {css_color} user-select: none;",
147            cx.props.name
148        }
149    ))
150}