UI overhaul rebase

This commit is contained in:
2026-01-28 20:42:47 +01:00
parent af095684a1
commit 7ecf0f10e8
55 changed files with 323294 additions and 3 deletions

View File

@@ -0,0 +1,13 @@
[package]
name = "dioxus-fontawesome-examples"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
codegen = "0.1.3"
heck = "0.4.0"
regex = "1.6.0"
scraper = "0.13.0"
walkdir = "2.3.2"

View File

@@ -0,0 +1,215 @@
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::{ffi::OsStr, path::Path};
use heck::ToSnakeCase;
use heck::ToUpperCamelCase;
use regex::Regex;
use scraper::node::Element;
use scraper::ElementRef;
use scraper::Html;
use walkdir::WalkDir;
const ICON_TEMPLATE: &str = r#"#[derive(Copy, Clone, Debug, PartialEq)]
pub struct {ICON_NAME};
impl IconShape for {ICON_NAME} {
fn view_box(&self) -> &str {
"{VIEW_BOX}"
}
fn xmlns(&self) -> &str {
"{XMLNS}"
}
fn fill_and_stroke<'a>(&self, user_color: &'a str) -> (&'a str, &'a str, &'a str) {
({FILL_COLOR}, {STROKE_COLOR}, {STROKE_WIDTH})
}
fn stroke_linecap(&self) -> &str {
"{STROKE_LINECAP}"
}
fn stroke_linejoin(&self) -> &str {
"{STROKE_LINEJOIN}"
}
fn child_elements(&self) -> Element {
rsx! {
{CHILD_ELEMENTS}
}
}
}
"#;
pub fn create_icon_file(svg_path: &str, output_path: &str, icon_prefix: &str) {
let files = collect_svg_files(svg_path, icon_prefix);
let icon_file = files
.into_iter()
.map(|file| {
let svg_str = fs::read_to_string(&file).unwrap();
let fragment = Html::parse_fragment(&svg_str);
let elements = fragment
.tree
.nodes()
.filter_map(|node| {
if node.value().is_element() {
let element = ElementRef::wrap(node).unwrap().value();
if !element.attrs.is_empty() {
return Some(element);
}
}
None
})
.collect::<Vec<_>>();
let svg_element = &elements[0];
let svg_child_elements = &elements[1..];
let icon_name = icon_name(&file, icon_prefix);
let (view_box, xmlns) = extract_svg_attrs(svg_element);
let child_elements = extract_svg_child_elements(svg_child_elements, icon_prefix);
let (fill_color, stroke_color, stroke_width) = extract_svg_colors(icon_prefix);
let stroke_linecap = extract_stroke_linecap(icon_prefix);
let stroke_linejoin = extract_stroke_linejoin(icon_prefix);
ICON_TEMPLATE
.replace("{ICON_NAME}", &format!("{}{}", icon_prefix, &icon_name))
.replace("{VIEW_BOX}", &view_box)
.replace("{XMLNS}", &xmlns)
.replace("{CHILD_ELEMENTS}", &child_elements)
.replace("{FILL_COLOR}", &fill_color)
.replace("{STROKE_COLOR}", &stroke_color)
.replace("{STROKE_WIDTH}", &stroke_width)
.replace("{STROKE_LINECAP}", &stroke_linecap)
.replace("{STROKE_LINEJOIN}", &stroke_linejoin)
})
.collect::<Vec<_>>()
.join("\n");
// write to file
let mut file = File::create(output_path).unwrap();
file.write_all(
format!(
"{}\n\n{}",
"use super::super::IconShape;\nuse dioxus::prelude::*;", icon_file
)
.as_bytes(),
)
.unwrap();
file.flush().unwrap();
}
fn collect_svg_files(svg_path: &str, icon_prefix: &str) -> Vec<PathBuf> {
let dir_entries = WalkDir::new(svg_path)
.sort_by_file_name()
.into_iter()
.filter_map(|e| e.ok())
.collect::<Vec<_>>();
dir_entries
.into_iter()
.filter(|e| match icon_prefix {
"Go" => {
let re = Regex::new(r".*-16.svg$").unwrap();
return re.is_match(e.path().to_str().unwrap());
}
"Md" => {
let split_vec = e.path().components().collect::<Vec<_>>();
return split_vec.iter().any(|c| c.as_os_str() == "materialicons")
&& e.file_name().to_str().unwrap() == "24px.svg";
}
_ => return e.path().extension() == Some(OsStr::new("svg")),
})
.map(|dir| PathBuf::from(dir.path()))
.collect::<Vec<_>>()
}
fn icon_name(path: &Path, icon_prefix: &str) -> String {
match icon_prefix {
"Go" => {
let filename = path.file_name().unwrap().to_str().unwrap();
let name = filename.split('.').next().unwrap();
name.replace("-16", "").to_upper_camel_case()
}
"Md" => {
let split_vec = path.components().collect::<Vec<_>>();
let name = split_vec[split_vec.len() - 3];
name.as_os_str().to_str().unwrap().to_upper_camel_case()
}
_ => {
let filename = path.file_name().unwrap().to_str().unwrap();
let name = filename.split('.').next().unwrap();
name.to_upper_camel_case()
}
}
}
fn extract_svg_attrs(element: &Element) -> (String, String) {
let view_box = element.attr("viewBox").unwrap_or("0 0 16 16");
let xmlns = element
.attr("xmlns")
.unwrap_or("http://www.w3.org/2000/svg");
(String::from(view_box), String::from(xmlns))
}
fn extract_svg_colors(icon_prefix: &str) -> (&str, &str, &str) {
match icon_prefix {
"Fi" => ("\"none\"", "user_color", "\"2\""),
"Ld" => ("\"none\"", "user_color", "\"2\""),
"Io" => ("user_color", "user_color", "\"0\""),
_ => ("user_color", "\"none\"", "\"0\""),
}
}
fn extract_stroke_linecap(icon_prefix: &str) -> &str {
match icon_prefix {
"Ld" => "round",
"Fi" => "round",
_ => "butt",
}
}
fn extract_stroke_linejoin(icon_prefix: &str) -> &str {
match icon_prefix {
"Ld" => "round",
"Fi" => "round",
_ => "miter",
}
}
fn extract_svg_child_elements(elements: &[&Element], icon_prefix: &str) -> String {
let elements = match icon_prefix {
"Md" => &elements[1..],
_ => elements,
};
elements
.iter()
.map(|element| {
let tag_name = element.name();
let mut element_attrs = element
.attrs()
.filter_map(|(name, value)| {
let value = if icon_prefix == "Io" {
value.replace("fill:none;stroke:#000;", "")
} else {
value.to_string()
};
let re = Regex::new(r"^data-.*$").unwrap();
if !re.is_match(name) && name != "fill" {
Some(format!(
" {}: \"{}\",",
name.to_snake_case(),
value
))
} else {
None
}
})
.collect::<Vec<_>>();
element_attrs.sort();
let attrs_str = element_attrs.join("\n");
" {TAG_NAME} {\n{ATTRS}\n }"
.replace("{TAG_NAME}", tag_name)
.replace("{ATTRS}", &attrs_str)
})
.collect::<Vec<_>>()
.join("\n")
}

View File

@@ -0,0 +1,79 @@
mod create_icon_file;
fn main() {
const OUTPUT_BASE_PATH: &str = "../lib/src/icons";
// create font awesome icons
const FA_SVG_BASE_PATH: &str = "../../icon_resources/font-awesome/svgs";
for icon_type in vec!["brands", "regular", "solid"].into_iter() {
let svg_path = format!("{}/{}", FA_SVG_BASE_PATH, icon_type);
let output_path = format!("{}/fa_{}_icons.rs", OUTPUT_BASE_PATH, icon_type);
create_icon_file::create_icon_file(&svg_path, &output_path, "Fa");
}
// create hero icons
const HI_SVG_BASE_PATH: &str = "../../icon_resources/heroicons/src";
for icon_type in vec!["outline", "solid"].into_iter() {
let svg_path = format!("{}/{}", HI_SVG_BASE_PATH, icon_type);
let output_path = format!("{}/hi_{}_icons.rs", OUTPUT_BASE_PATH, icon_type);
create_icon_file::create_icon_file(&svg_path, &output_path, "Hi");
}
// create ionicons
const IO_SVG_BASE_PATH: &str = "../../icon_resources/ionicons/src/svg";
let output_path = format!("{}/io_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(IO_SVG_BASE_PATH, &output_path, "Io");
// create octicons
const GO_SVG_BASE_PATH: &str = "../../icon_resources/octicons/icons";
let go_output_path = format!("{}/go_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(GO_SVG_BASE_PATH, &go_output_path, "Go");
// create bootstrap icons
const BS_SVG_BASE_PATH: &str = "../../icon_resources/bootstrap/icons";
let bs_output_path = format!("{}/bs_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(BS_SVG_BASE_PATH, &bs_output_path, "Bs");
// create feather icons
const FI_SVG_BASE_PATH: &str = "../../icon_resources/feather/icons";
let fi_output_path = format!("{}/fi_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(FI_SVG_BASE_PATH, &fi_output_path, "Fi");
// create feather icons
const LD_SVG_BASE_PATH: &str = "../../icon_resources/lucide/icons";
let ld_output_path = format!("{}/ld_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(LD_SVG_BASE_PATH, &ld_output_path, "Ld");
// create material design icons
const MI_SVG_BASE_PATH: &str = "../../icon_resources/material-design-icons/src";
for icon_type in vec![
"action",
"alert",
"av",
"communication",
"content",
"device",
"editor",
"file",
"hardware",
"home",
"image",
"maps",
"navigation",
"notification",
"places",
"social",
"toggle",
]
.into_iter()
{
let svg_path = format!("{}/{}", MI_SVG_BASE_PATH, icon_type);
let output_path = format!("{}/md_{}_icons.rs", OUTPUT_BASE_PATH, icon_type);
create_icon_file::create_icon_file(&svg_path, &output_path, "Md");
}
// create vscode-codicons
const VS_SVG_BASE_PATH: &str = "../../icon_resources/vscode-codicons/src/icons";
let vs_output_path = format!("{}/vsc_icons.rs", OUTPUT_BASE_PATH);
create_icon_file::create_icon_file(VS_SVG_BASE_PATH, &vs_output_path, "Vsc");
}