Skip to main content

dioxus_icon_component/
lib.rs

1// Copyright (c) 2026 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License
3// that can be found in the LICENSE file.
4
5use dioxus::prelude::*;
6
7/// This trait is used to override `IconProps`.
8///
9/// Implements this trait when adding a new real icon.
10pub trait IconShape: Clone + PartialEq + 'static {
11    /// Returns the SVG child elements (paths, circles, etc.) that define the icon shape.
12    ///
13    /// # Errors
14    ///
15    /// Returns `Err` if rendering the child elements fails.
16    fn child_elements(&self) -> Element;
17
18    /// Default title text for the SVG element.
19    const TITLE: Option<&'static str> = None;
20
21    /// Default width of the SVG element in pixels.
22    const WIDTH: Option<&'static str> = None;
23
24    /// Default height of the SVG element in pixels.
25    const HEIGHT: Option<&'static str> = None;
26
27    /// Default fill color of the SVG element.
28    const FILL: Option<&'static str> = Some("currentColor");
29
30    /// Default stroke color of the SVG element.
31    const STROKE: Option<&'static str> = None;
32
33    /// Default stroke width of the SVG element (e.g., "2").
34    const STROKE_WIDTH: Option<&'static str> = None;
35
36    /// Default stroke line cap style (e.g., "round", "butt", "square").
37    const STROKE_LINE_CAP: Option<&'static str> = None;
38
39    /// Default stroke line join style (e.g., "round", "miter", "bevel").
40    const STROKE_LINE_JOIN: Option<&'static str> = None;
41
42    /// Default view box string (e.g., "0 0 24 24").
43    const VIEW_BOX: Option<&'static str> = None;
44
45    /// Default XML namespace for the SVG element.
46    /// Falls back to `"http://www.w3.org/2000/svg"` if not set.
47    const XMLNS: Option<&'static str> = Some("http://www.w3.org/2000/svg");
48}
49
50/// Props for the `Icon` component.
51///
52/// All fields are optional except `icon`. When a field is not provided,
53/// the default value from the associated `IconShape` implementation is used.
54#[derive(Clone, PartialEq, Props)]
55pub struct IconProps<T: IconShape> {
56    /// The icon shape implementation that provides SVG child elements.
57    pub icon: T,
58
59    /// Optional title text rendered as a `<title>` element inside the SVG for accessibility.
60    #[props(default = None)]
61    pub title: Option<&'static str>,
62
63    /// Additional HTML/SVG global attributes to spread onto the `<svg>` element.
64    /// Allows passing standard attributes like `id`, `aria-*`, event handlers, etc.
65    #[props(extends = GlobalAttributes)]
66    pub attributes: Vec<Attribute>,
67}
68
69/// # Errors
70///
71/// Returns `Err` if rendering the icon fails.
72#[allow(non_snake_case)]
73pub fn Icon<T: IconShape>(props: IconProps<T>) -> Element {
74    rsx! {
75        svg {
76            width: T::WIDTH,
77            height: T::HEIGHT,
78            fill: T::FILL,
79            stroke: T::STROKE,
80            stroke_width: T::STROKE_WIDTH,
81            stroke_linecap: T::STROKE_LINE_CAP,
82            stroke_linejoin: T::STROKE_LINE_JOIN,
83            view_box: T::VIEW_BOX,
84            xmlns: T::XMLNS,
85
86            ..props.attributes,
87
88            if let Some(title_text) = props.title {
89                title {{ title_text }}
90            } else if let Some(title_text) = T::TITLE {
91                title {{ title_text }}
92            }
93
94            {props.icon.child_elements()}
95        }
96    }
97}