dioxus-icons 0.1.0

Lucide icons for Dioxus, one component per icon.
Documentation
use dioxus::core::{
    Attribute, AttributeValue, DynamicNode, Element, Template, TemplateAttribute, TemplateNode,
    VNode,
};

use crate::IconProps;

type AttributeDescription = (&'static str, Option<&'static str>, bool);
const SVG_NAMESPACE: Option<&'static str> = Some("http://www.w3.org/2000/svg");
const EMPTY_CHILDREN: &[TemplateNode] = &[];
const ICON_ATTR_PATHS: &[&[u8]] = &[&[0u8]];
static DEFAULT_SVG_ATTRS: [TemplateAttribute; 1] = svg_attrs();

const XMLNS: AttributeDescription = ("xmlns", None, false);
const WIDTH: AttributeDescription = ("width", None, false);
const HEIGHT: AttributeDescription = ("height", None, false);
const VIEW_BOX: AttributeDescription = ("viewBox", None, false);
const FILL: AttributeDescription = ("fill", None, false);
const STROKE: AttributeDescription = ("stroke", None, false);
const STROKE_WIDTH: AttributeDescription = ("stroke-width", None, false);
const STROKE_LINECAP: AttributeDescription = ("stroke-linecap", None, false);
const STROKE_LINEJOIN: AttributeDescription = ("stroke-linejoin", None, false);

#[inline]
pub(crate) const fn icon_template(roots: &'static [TemplateNode]) -> Template {
    Template {
        roots,
        node_paths: &[],
        attr_paths: ICON_ATTR_PATHS,
    }
}

#[inline]
pub(crate) const fn svg_attrs() -> [TemplateAttribute; 1] {
    [dynamic_attr(0)]
}

#[inline]
pub(crate) const fn svg(children: &'static [TemplateNode]) -> TemplateNode {
    svg_with_attrs(&DEFAULT_SVG_ATTRS, children)
}

#[inline]
pub(crate) const fn svg_with_attrs(
    attrs: &'static [TemplateAttribute],
    children: &'static [TemplateNode],
) -> TemplateNode {
    TemplateNode::Element {
        tag: "svg",
        namespace: SVG_NAMESPACE,
        attrs,
        children,
    }
}

#[inline]
pub(crate) const fn path(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("path", attrs)
}

#[inline]
pub(crate) const fn circle(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("circle", attrs)
}

#[inline]
pub(crate) const fn rect(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("rect", attrs)
}

#[inline]
pub(crate) const fn line(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("line", attrs)
}

#[inline]
pub(crate) const fn polyline(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("polyline", attrs)
}

#[inline]
pub(crate) const fn polygon(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("polygon", attrs)
}

#[inline]
pub(crate) const fn ellipse(attrs: &'static [TemplateAttribute]) -> TemplateNode {
    child("ellipse", attrs)
}

#[inline]
const fn child(tag: &'static str, attrs: &'static [TemplateAttribute]) -> TemplateNode {
    TemplateNode::Element {
        tag,
        namespace: SVG_NAMESPACE,
        attrs,
        children: EMPTY_CHILDREN,
    }
}

#[inline]
pub(crate) const fn attr(name: &'static str, value: &'static str) -> TemplateAttribute {
    TemplateAttribute::Static {
        name,
        value,
        namespace: None,
    }
}

#[inline]
const fn dynamic_attr(id: usize) -> TemplateAttribute {
    TemplateAttribute::Dynamic { id }
}

#[inline]
fn icon_attr(
    (name, namespace, volatile): AttributeDescription,
    value: AttributeValue,
) -> Attribute {
    Attribute {
        name,
        value,
        namespace,
        volatile,
    }
}

#[inline]
pub(crate) fn icon_element(
    template: Template,
    view_box: &'static str,
    props: IconProps,
) -> Element {
    let IconProps { size, attributes } = props;
    let size = size.into_value();

    let mut root_attributes = Vec::with_capacity(attributes.len() + 9);
    push_default_attr(
        &mut root_attributes,
        &attributes,
        XMLNS,
        "http://www.w3.org/2000/svg",
    );
    push_default_attr_value(&mut root_attributes, &attributes, WIDTH, size.clone());
    push_default_attr_value(&mut root_attributes, &attributes, HEIGHT, size);
    push_default_attr(&mut root_attributes, &attributes, VIEW_BOX, view_box);
    push_default_attr(&mut root_attributes, &attributes, FILL, "none");
    push_default_attr(&mut root_attributes, &attributes, STROKE, "currentColor");
    push_default_attr(&mut root_attributes, &attributes, STROKE_WIDTH, "2");
    push_default_attr(&mut root_attributes, &attributes, STROKE_LINECAP, "round");
    push_default_attr(&mut root_attributes, &attributes, STROKE_LINEJOIN, "round");
    root_attributes.extend(attributes);

    let dynamic_attributes = Box::new([root_attributes.into_boxed_slice()]);
    let dynamic_nodes: Box<[DynamicNode]> = Box::new([]);
    Ok(VNode::new(
        None,
        template,
        dynamic_nodes,
        dynamic_attributes,
    ))
}

#[inline]
fn push_default_attr(
    output: &mut Vec<Attribute>,
    attributes: &[Attribute],
    description: AttributeDescription,
    value: &str,
) {
    if !has_attr(attributes, description) {
        push_default_attr_value(
            output,
            attributes,
            description,
            AttributeValue::Text(value.to_owned()),
        );
    }
}

#[inline]
fn push_default_attr_value(
    output: &mut Vec<Attribute>,
    attributes: &[Attribute],
    description: AttributeDescription,
    value: AttributeValue,
) {
    if !has_attr(attributes, description) {
        output.push(icon_attr(description, value));
    }
}

#[inline]
fn has_attr(attributes: &[Attribute], (name, namespace, _): AttributeDescription) -> bool {
    attributes
        .iter()
        .any(|attribute| attribute.name == name && attribute.namespace == namespace)
}