diff --git a/Cargo.toml b/Cargo.toml index 203cf57..bf1c281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ maintenance = { status = "passively-maintained" } [dependencies] image = { version = "0.25", default-features = false, optional = true } +yew = { version = "0.21", optional = true } [dev-dependencies] image = "0.25" @@ -31,6 +32,7 @@ std = [] bench = [] svg = [] pic = [] +yew = ["dep:yew"] [[bin]] name = "qrencode" diff --git a/src/lib.rs b/src/lib.rs index 4d7ba8e..acdcc55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ pub mod ec; pub mod optimize; pub mod render; pub mod types; +pub mod yew; pub use crate::types::{Color, EcLevel, QrResult, Version}; diff --git a/src/yew.rs b/src/yew.rs new file mode 100644 index 0000000..22c902a --- /dev/null +++ b/src/yew.rs @@ -0,0 +1,98 @@ +#![cfg(feature = "yew")] + +use yew::{function_component, html, AttrValue, Callback, Html, MouseEvent, Properties}; + +use alloc::format; +use alloc::string::String; +use core::fmt::Write; + +use crate::render::{Canvas as RenderCanvas, Pixel}; +use crate::types::Color as ModuleColor; +use crate::{EcLevel, Version}; + +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Color<'a>(pub &'a str); + +pub struct SvgAttrs { + width: u32, + height: u32, + fg: String, + bg: String, + d: String, +} + +impl<'a> Pixel for Color<'a> { + type Canvas = Canvas<'a>; + type Image = SvgAttrs; + + fn default_color(color: ModuleColor) -> Self { + Color(color.select("#000", "#fff")) + } +} + +#[doc(hidden)] +pub struct Canvas<'a> { + d: String, + w: u32, + h: u32, + fg: &'a str, + bg: &'a str, +} + +impl<'a> RenderCanvas for Canvas<'a> { + type Pixel = Color<'a>; + type Image = SvgAttrs; + + fn new(width: u32, height: u32, dark_pixel: Color<'a>, light_pixel: Color<'a>) -> Self { + Self { d: String::new(), w: width, h: height, fg: dark_pixel.0, bg: light_pixel.0 } + } + + fn draw_dark_pixel(&mut self, x: u32, y: u32) { + self.draw_dark_rect(x, y, 1, 1); + } + + fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) { + write!(self.d, "M{left} {top}h{width}v{height}h-{width}z").unwrap(); + } + + fn into_image(self) -> SvgAttrs { + SvgAttrs { width: self.w, height: self.h, fg: self.fg.to_owned(), bg: self.bg.to_owned(), d: self.d } + } +} + +#[derive(Clone, PartialEq, Properties)] +pub struct Props { + version: Version, + ec_level: EcLevel, + + url: AttrValue, + size: AttrValue, + #[prop_or_default] + on_click: Callback, + + #[prop_or("#000000".into())] + dark_color: AttrValue, + #[prop_or("#ffffff".into())] + light_color: AttrValue, +} + +#[function_component] +pub fn QrCode(props: &Props) -> Html { + let code = crate::QrCode::with_version(&*props.url, props.version, props.ec_level).unwrap(); + let SvgAttrs { width, height, fg, bg, d } = + code.render().dark_color(Color(&props.dark_color)).light_color(Color(&props.light_color)).build(); + + html! { + + + + + } +}