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,
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
}
}