//! The `Icon` component: renders a glyph from the central [`crate::icons`] //! registry (Material Symbols via Iconify) as inline SVG. use leptos::prelude::*; use crate::icons::IconName; /// Folder-shaped glyphs get the accent color; everything else stays muted. fn icon_class(name: IconName) -> &'static str { if name.is_folder() { "ic-folder" } else { "ic-muted" } } /// The icon markup, as a plain function rather than a component. /// /// Prefer this over [`Icon`] inside long lists. A `#[component]` call costs a /// reactive owner per instance, which is measurable at a thousand rows; a /// plain function returning a view costs nothing extra. /// /// `inner_html` rather than a `NodeRef` with an `on_load` callback, for the /// same reason: `on_load` is an effect, so a thousand icons meant a thousand /// effects. The registry bodies are raw SVG markup (``) and are /// injected as-is: the strings are `const`, vendored from the Iconify API, /// and never user input. pub fn icon_svg(name: IconName, extra: &str) -> impl IntoView + use<> { let mut cls = String::from("icon "); cls.push_str(icon_class(name)); if !extra.is_empty() { cls.push(' '); cls.push_str(extra); } view! { } } #[component] pub fn Icon(name: IconName, #[prop(optional)] class: Option) -> impl IntoView { icon_svg(name, class.as_deref().unwrap_or("")) }