m3rs_core 0.0.1

A design system for wasm apps based on rust and material design 3
Documentation
use crate::{
	color::ColorRole,
	component::Component,
	css::properties::{CssProperty, Height, Width},
	dom::{self, create_element},
	elevation::Elevation,
};
use convert_case::{Case, Casing};
use std::fmt::Display;
use web_sys::{HtmlElement, HtmlStyleElement, ShadowRootInit, ShadowRootMode};

#[derive(Debug, Clone, Copy, Default)]
pub enum Scale {
	ExtraLarge,
	ExtraLargeTop,
	ExtraSmall,
	ExtraSmallTop,
	Full,
	Large,
	LargeEnd,
	LargeStart,
	LargeTop,
	#[default]
	Medium,
	None,
	Small,
}

impl Display for Scale {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "{self:?}")
	}
}

impl Scale {
	fn css_var(&self) -> String {
		format!("--md-sys-shape-corner-{}", self.to_kebab_case())
	}

	fn to_kebab_case(self) -> String {
		self.to_string().to_case(Case::Kebab)
	}
}

#[derive(Debug, Clone, Copy, Default)]
pub enum Family {
	#[default]
	Rounded,
	Cuted,
	Circular,
}

#[derive(Debug, Clone, Copy, Default)]
pub struct ContainerOpts {
	pub scale: Scale,
	pub family: Family,
	pub elevation: Elevation,
	pub color: ColorRole,
	pub shadow: ColorRole,
	pub disable: bool,
	// css options
	pub opacity: Option<f32>,
	pub width: Option<Width>,
	pub height: Option<Height>,
}

pub trait Container<T: Into<HtmlElement>>: Component<T> {
	fn new(inner: T, opts: Self::ComponentOpts) -> Self;
}

impl<T: Into<HtmlElement>> Container<T> for HtmlElement {
	fn new(inner: T, opts: Self::ComponentOpts) -> Self {
		HtmlElement::create(inner, opts)
	}
}

impl<T: Into<HtmlElement>> Component<T> for HtmlElement {
	type ComponentOpts = ContainerOpts;

	fn create(inner: T, opts: Self::ComponentOpts) -> HtmlElement {
		let ContainerOpts {
			scale,
			color,
			opacity,
			width,
			height,
			..
		} = opts;

		let container = create_element::<HtmlElement>("div");
		container.append_child(&inner.into()).unwrap();
		container.set_id("md-shape");

		let host = create_element::<HtmlElement>("div");

		let mut props = vec![
			(
				"--background".to_owned(),
				format!("var({})", color.css_var()),
			),
			(
				"--border-radius".to_owned(),
				format!("var({})", scale.css_var()),
			),
		];

		if let Some(opacity) = opacity {
			props.push(("--opacity".to_owned(), opacity.to_string()));
		};

		if let Some(height) = height {
			props.push(height.to_key_value());
		}

		if let Some(width) = width {
			props.push(width.to_key_value());
		}

		dom::set_properties(&host, props);

		let shadow = host
			.attach_shadow(&ShadowRootInit::new(ShadowRootMode::Open))
			.unwrap();

		let styles = create_element::<HtmlStyleElement>("style");
		styles.set_inner_html(include_str!(concat!(env!("OUT_DIR"), "/containers.css")));

		shadow.append_child(&styles).unwrap();
		shadow.append_child(&container).unwrap();

		host
	}
}