Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/xilem_web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ peniko = { git = "https://github.com/linebender/peniko", rev = "629fc3325b016a8c
version = "0.3.4"
features = [
"console",
"CssStyleDeclaration",
"Document",
"DomTokenList",
"Element",
"Event",
"HtmlElement",
Expand Down
124 changes: 124 additions & 0 deletions crates/xilem_web/src/class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use std::{borrow::Cow, marker::PhantomData};

use xilem_core::{Id, MessageResult};

use crate::{
interfaces::{sealed::Sealed, Element},
ChangeFlags, Cx, View, ViewMarker,
};

/// A trait to make the class adding functions generic over collection type
pub trait IntoClasses {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>);
}

impl IntoClasses for String {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
classes.push(self.into());
}
}

impl IntoClasses for &'static str {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
classes.push(self.into());
}
}

impl IntoClasses for Cow<'static, str> {
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
classes.push(self);
}
}

impl<T> IntoClasses for Option<T>
where
T: IntoClasses,
{
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
if let Some(t) = self {
t.into_classes(classes);
}
}
}

impl<T> IntoClasses for Vec<T>
where
T: IntoClasses,
{
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
for itm in self {
itm.into_classes(classes);
}
}
}

macro_rules! impl_tuple_intoclasses {
($($name:ident : $type:ident),* $(,)?) => {
impl<$($type),*> IntoClasses for ($($type,)*)
where
$($type: IntoClasses),*
{
#[allow(unused_variables)]
fn into_classes(self, classes: &mut Vec<Cow<'static, str>>) {
let ($($name,)*) = self;
$(
$name.into_classes(classes);
)*
}
}
};
}

impl_tuple_intoclasses!();
impl_tuple_intoclasses!(t1: T1);
impl_tuple_intoclasses!(t1: T1, t2: T2);
impl_tuple_intoclasses!(t1: T1, t2: T2, t3: T3);
impl_tuple_intoclasses!(t1: T1, t2: T2, t3: T3, t4: T4);

/// Applies a class to the underlying element.
pub struct Class<E, T, A> {
pub(crate) element: E,
pub(crate) class_names: Vec<Cow<'static, str>>,
pub(crate) phantom: PhantomData<fn() -> (T, A)>,
}

impl<E, T, A> ViewMarker for Class<E, T, A> {}
impl<E, T, A> Sealed for Class<E, T, A> {}

impl<E: Element<T, A>, T, A> View<T, A> for Class<E, T, A> {
type State = E::State;
type Element = E::Element;

fn build(&self, cx: &mut Cx) -> (Id, Self::State, Self::Element) {
for class_name in &self.class_names {
cx.add_class_to_element(class_name);
}
self.element.build(cx)
}

fn rebuild(
&self,
cx: &mut Cx,
prev: &Self,
id: &mut Id,
state: &mut Self::State,
element: &mut Self::Element,
) -> ChangeFlags {
for class_name in &self.class_names {
cx.add_class_to_element(class_name);
}
self.element.rebuild(cx, &prev.element, id, state, element)
}

fn message(
&self,
id_path: &[Id],
state: &mut Self::State,
message: Box<dyn std::any::Any>,
app_state: &mut T,
) -> MessageResult<A> {
self.element.message(id_path, state, message, app_state)
}
}

crate::interfaces::impl_dom_interfaces_for_ty!(Element, Class);
Loading