Skip to content

Commit

Permalink
refactor(rust-plugins): 💡 svg builder
Browse files Browse the repository at this point in the history
  • Loading branch information
CCherry07 committed Nov 18, 2024
1 parent 14d5507 commit 8e8c075
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 143 deletions.
6 changes: 6 additions & 0 deletions rust-plugins/icons/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @farmfe/plugin-icons

## 0.0.6

### Patch Changes

- refactor: svg builder

## 0.0.5

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion rust-plugins/icons/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@farmfe/plugin-icons",
"version": "0.0.5",
"version": "0.0.6",
"main": "scripts/index.js",
"types": "scripts/index.d.ts",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion rust-plugins/icons/playground-react/farm.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default defineConfig({
visualizer(),
react(),
icons({
scale: 1,
scale: 1.2,
autoInstall: true,
compiler: "jsx",
defaultClass: "icon-color",
Expand Down
2 changes: 1 addition & 1 deletion rust-plugins/icons/playground-react/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function Main() {
<div className="i-logos-react text-100px text-#00D8FF"></div>
<ReactLogoIconify className="text-100px text-#00D8FF" />
<ReactLogoComponent className="text-100px h-1em w-1em" />
<LocalReactLogo className="text-100px h-1em w-1em" />
<LocalReactLogo />
<RemoteComponent className="text-100px h-1em w-1em" />
<div dangerouslySetInnerHTML={
{
Expand Down
65 changes: 37 additions & 28 deletions rust-plugins/icons/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use loader::{
},
icon_data::gen_svg_for_icon_data,
struct_config::{IconifyIcon, IconifyLoaderOptions},
svg_modifier::SvgModifier,
svg_builder::{SvgBuilder, SvgCustomizations},
};
use options::Options;
use serde_json::Value;
Expand Down Expand Up @@ -118,17 +118,17 @@ impl Plugin for FarmfePluginIcons {
) -> farmfe_core::error::Result<Option<farmfe_core::plugin::PluginLoadHookResult>> {
if param.query.iter().any(|(k, _)| k == "component") {
let query_map = param.query.iter().cloned().collect::<HashMap<_, _>>();
let svg_builder = SvgModifier::new(SvgModifier {
fill: query_map.get("fill").and_then(|v| v.parse().ok()),
stroke: query_map.get("stroke").and_then(|v| v.parse().ok()),
stroke_width: query_map.get("stroke-width").and_then(|v| v.parse().ok()),
width: query_map.get("width").and_then(|v| v.parse().ok()),
height: query_map.get("height").and_then(|v| v.parse().ok()),
class: self.options.default_class.clone(),
style: self.options.default_style.clone(),
view_box: None,
});
let svg = svg_builder.apply_to_svg(&get_svg_by_local_path(param.resolved_path));
let svg_content = get_svg_by_local_path(param.resolved_path);
let svg = SvgBuilder::new(&svg_content)
.fill(query_map.get("fill").cloned())
.stroke(query_map.get("stroke").cloned())
.stroke_width(query_map.get("stroke-width").cloned())
.width(query_map.get("width").cloned())
.height(query_map.get("height").cloned())
.class(self.options.default_class.clone())
.style(self.options.default_style.clone())
.view_box(None)
.build();
let compiler = get_compiler(GetCompilerParams {
jsx: self.options.jsx.clone().unwrap_or_default(),
compiler: self.options.compiler.clone().unwrap_or_default(),
Expand Down Expand Up @@ -159,16 +159,16 @@ impl Plugin for FarmfePluginIcons {
compiler: self.options.compiler.clone().unwrap_or_default(),
});
let query_map = param.query.iter().cloned().collect::<HashMap<_, _>>();
let svg_builder = SvgModifier::new(SvgModifier {
fill: query_map.get("fill").and_then(|v| v.parse().ok()),
stroke: query_map.get("stroke").and_then(|v| v.parse().ok()),
stroke_width: query_map.get("stroke-width").and_then(|v| v.parse().ok()),
width: query_map.get("width").and_then(|v| v.parse().ok()),
height: query_map.get("height").and_then(|v| v.parse().ok()),
class: self.options.default_class.clone(),
style: self.options.default_style.clone(),
view_box: None,
});
// let svg_builder = SvgModifier::new(SvgModifier {
// fill: query_map.get("fill").and_then(|v| v.parse().ok()),
// stroke: query_map.get("stroke").and_then(|v| v.parse().ok()),
// stroke_width: query_map.get("stroke-width").and_then(|v| v.parse().ok()),
// width: query_map.get("width").and_then(|v| v.parse().ok()),
// height: query_map.get("height").and_then(|v| v.parse().ok()),
// class: self.options.default_class.clone(),
// style: self.options.default_style.clone(),
// view_box: None,
// });
let meta = resolve_icons_path(source);
let mut svg_raw = String::new();
let custom_collections = self
Expand All @@ -193,7 +193,16 @@ impl Plugin for FarmfePluginIcons {
);

if !svg_raw.is_empty() {
svg_raw = svg_builder.apply_to_svg(&svg_raw);
svg_raw = SvgBuilder::new(&svg_raw)
.fill(query_map.get("fill").cloned())
.stroke(query_map.get("stroke").cloned())
.stroke_width(query_map.get("stroke-width").cloned())
.width(query_map.get("width").cloned())
.height(query_map.get("height").cloned())
.class(self.options.default_class.clone())
.style(self.options.default_style.clone())
.view_box(None)
.build();
}
} else {
let data = get_icon_data_by_iconify(GetIconPathDataParams {
Expand All @@ -211,7 +220,7 @@ impl Plugin for FarmfePluginIcons {
let svg_data_height: Option<i64> = data.get("height").and_then(|v| v.as_i64());
let svg_data_width: Option<i64> = data.get("width").and_then(|v| v.as_i64());

let customizations = SvgModifier {
let customizations = SvgCustomizations {
fill: query_map.get("fill").and_then(|v| v.parse().ok()),
stroke: query_map.get("stroke").and_then(|v| v.parse().ok()),
stroke_width: query_map.get("stroke-width").and_then(|v| v.parse().ok()),
Expand All @@ -221,16 +230,16 @@ impl Plugin for FarmfePluginIcons {
};

if let Some(raw) = gen_svg_for_icon_data(
Some(IconifyIcon {
IconifyIcon {
width: svg_data_width.map(|w| w as i32),
height: svg_data_height.map(|w| w as i32),
body: svg_path_str.unwrap_or_default(),
..Default::default()
}),
Some(IconifyLoaderOptions {
},
IconifyLoaderOptions {
scale: self.options.scale,
customizations: Some(customizations),
}),
},
) {
svg_raw = raw;
}
Expand Down
49 changes: 21 additions & 28 deletions rust-plugins/icons/src/loader/icon_data.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
use super::svg_modifier::SvgModifier;
use super::struct_config::IconifyLoaderOptions;
use super::svg_builder::SvgBuilder;
use super::{
icon_to_svg::{icon_to_svg, IconifyIconBuildResult},
struct_config::IconifyIcon,
};

pub fn gen_svg_for_icon_data(
icon_data: Option<IconifyIcon>,
options: Option<IconifyLoaderOptions>,
icon_data: IconifyIcon,
options: IconifyLoaderOptions,
) -> Option<String> {
if let Some(icon) = icon_data {
let IconifyIconBuildResult {
mut attributes,
body,
..
} = icon_to_svg(icon.clone(), None);
let scale = options.as_ref().and_then(|o| o.scale);
if let Some(s) = scale {
if s != 0.0 {
attributes.height = Some(format!("{}{}", s, "em"));
attributes.width = Some(format!("{}{}", s, "em"));
}
let IconifyIconBuildResult {
mut attributes,
body,
..
} = icon_to_svg(icon_data, None);
if let Some(s) = options.scale {
if s != 0.0 {
attributes.height = Some(format!("{}{}", s, "em"));
attributes.width = Some(format!("{}{}", s, "em"));
}
let svg = SvgModifier::new(SvgModifier {
width: attributes.width,
height: attributes.height,
view_box: Some(attributes.view_box),
..options
.as_ref()
.and_then(|o| o.customizations.clone())
.unwrap_or_default()
});
return Some(svg.apply_to_svg(&format!("<svg>{}</svg>", body)));
} else {
panic!("Icon data is missing");
}
}
let svg_content = format!("<svg>{}</svg>", body);
let svg = SvgBuilder::new(&svg_content)
.width(attributes.width)
.height(attributes.height)
.view_box(Some(attributes.view_box))
.insert_customizations(options.customizations.unwrap_or_default())
.build();

Some(svg)
}
2 changes: 1 addition & 1 deletion rust-plugins/icons/src/loader/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod common;
pub mod svg_modifier;
pub mod svg_builder;
pub mod icon_data;
pub mod struct_config;
pub mod icon_to_svg;
Expand Down
4 changes: 2 additions & 2 deletions rust-plugins/icons/src/loader/struct_config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::svg_modifier::SvgModifier;
use super::svg_builder::SvgCustomizations;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

Expand All @@ -21,6 +21,6 @@ pub struct IconifyIcon {

#[derive(Deserialize, Default)]
pub struct IconifyLoaderOptions {
pub customizations: Option<SvgModifier>,
pub customizations: Option<SvgCustomizations>,
pub scale: Option<f32>,
}
121 changes: 121 additions & 0 deletions rust-plugins/icons/src/loader/svg_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use serde::Deserialize;
use serde_json::Value;
use xmltree::Element;
pub struct SvgBuilder {
element: Element,
}

#[derive(Default, Deserialize)]
pub struct SvgCustomizations {
pub fill: Option<String>,
pub stroke: Option<String>,
pub stroke_width: Option<String>,
pub width: Option<String>,
pub height: Option<String>,
pub view_box: Option<String>,
pub class: Option<String>,
pub style: Option<Value>,
}

impl SvgBuilder {
pub fn new(svg_content: &str) -> Self {
let element = Element::parse(svg_content.as_bytes()).unwrap();
SvgBuilder { element }
}

pub fn fill(mut self, fill: Option<String>) -> Self {
if let Some(fill) = fill {
self.element.attributes.insert("fill".to_string(), fill);
}
self
}

pub fn stroke(mut self, stroke: Option<String>) -> Self {
if let Some(stroke) = stroke {
self.element.attributes.insert("stroke".to_string(), stroke);
}
self
}

pub fn stroke_width(mut self, stroke_width: Option<String>) -> Self {
if let Some(stroke_width) = stroke_width {
self
.element
.attributes
.insert("stroke-width".to_string(), stroke_width);
}
self
}

pub fn width(mut self, width: Option<String>) -> Self {
if let Some(width) = width {
self.element.attributes.insert("width".to_string(), width);
}
self
}

pub fn height(mut self, height: Option<String>) -> Self {
if let Some(height) = height {
self.element.attributes.insert("height".to_string(), height);
}
self
}

pub fn view_box(mut self, view_box: Option<String>) -> Self {
if let Some(view_box) = view_box {
self
.element
.attributes
.insert("viewBox".to_string(), view_box);
}
self
}

pub fn class(mut self, class: Option<String>) -> Self {
if let Some(class) = class {
self.element.attributes.insert("class".to_string(), class);
}
self
}

pub fn style(mut self, style: Option<Value>) -> Self {
if let Some(style) = style {
let style_str = style.as_object().map_or(String::new(), |obj| {
obj
.iter()
.map(|(key, value)| {
format!(
"{}:{};",
key,
value.as_str().unwrap_or("").replace("\"", "")
)
})
.collect::<Vec<_>>()
.join(" ")
});
self
.element
.attributes
.insert("style".to_string(), style_str);
}
self
}

pub fn insert_customizations(self, customizations: SvgCustomizations) -> Self {
self
.fill(customizations.fill)
.class(customizations.class)
.height(customizations.height)
.stroke(customizations.stroke)
.stroke_width(customizations.stroke_width)
.style(customizations.style)
.view_box(customizations.view_box)
.width(customizations.width)
}

pub fn build(self) -> String {
let mut buffer = Vec::new();
self.element.write(&mut buffer).unwrap();
String::from_utf8(buffer).unwrap()
}
}
Loading

0 comments on commit 8e8c075

Please sign in to comment.