use super::attribute::{Attribute, NextAttribute};
use crate::{
renderer::Rndr,
view::{Position, ToTemplate},
};
use std::{future::Future, sync::Arc};
#[inline(always)]
pub fn class<C>(class: C) -> Class<C>
where
C: IntoClass,
{
Class { class }
}
#[derive(Debug)]
pub struct Class<C> {
class: C,
}
impl<C> Clone for Class<C>
where
C: Clone,
{
fn clone(&self) -> Self {
Self {
class: self.class.clone(),
}
}
}
impl<C> Attribute for Class<C>
where
C: IntoClass,
{
const MIN_LENGTH: usize = C::MIN_LENGTH;
type AsyncOutput = Class<C::AsyncOutput>;
type State = C::State;
type Cloneable = Class<C::Cloneable>;
type CloneableOwned = Class<C::CloneableOwned>;
fn html_len(&self) -> usize {
self.class.html_len() + 1
}
fn to_html(
self,
_buf: &mut String,
class: &mut String,
_style: &mut String,
_inner_html: &mut String,
) {
class.push(' ');
self.class.to_html(class);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
self.class.hydrate::<FROM_SERVER>(el)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
self.class.build(el)
}
fn rebuild(self, state: &mut Self::State) {
self.class.rebuild(state)
}
fn into_cloneable(self) -> Self::Cloneable {
Class {
class: self.class.into_cloneable(),
}
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
Class {
class: self.class.into_cloneable_owned(),
}
}
fn dry_resolve(&mut self) {
self.class.dry_resolve();
}
async fn resolve(self) -> Self::AsyncOutput {
Class {
class: self.class.resolve().await,
}
}
}
impl<C> NextAttribute for Class<C>
where
C: IntoClass,
{
type Output<NewAttr: Attribute> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
impl<C> ToTemplate for Class<C>
where
C: IntoClass,
{
const CLASS: &'static str = C::TEMPLATE;
fn to_template(
_buf: &mut String,
class: &mut String,
_style: &mut String,
_inner_html: &mut String,
_position: &mut Position,
) {
C::to_template(class);
}
}
pub trait IntoClass: Send {
const TEMPLATE: &'static str = "";
const MIN_LENGTH: usize = Self::TEMPLATE.len();
type AsyncOutput: IntoClass;
type State;
type Cloneable: IntoClass + Clone;
type CloneableOwned: IntoClass + Clone + 'static;
fn html_len(&self) -> usize;
fn to_html(self, class: &mut String);
#[allow(unused)] fn to_template(class: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State;
fn build(self, el: &crate::renderer::types::Element) -> Self::State;
fn rebuild(self, state: &mut Self::State);
fn into_cloneable(self) -> Self::Cloneable;
fn into_cloneable_owned(self) -> Self::CloneableOwned;
fn dry_resolve(&mut self);
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
fn reset(state: &mut Self::State);
}
impl<T: IntoClass> IntoClass for Option<T> {
type AsyncOutput = Option<T::AsyncOutput>;
type State = (crate::renderer::types::Element, Option<T::State>);
type Cloneable = Option<T::Cloneable>;
type CloneableOwned = Option<T::CloneableOwned>;
fn html_len(&self) -> usize {
self.as_ref().map_or(0, IntoClass::html_len)
}
fn to_html(self, class: &mut String) {
if let Some(t) = self {
t.to_html(class);
}
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
if let Some(t) = self {
(el.clone(), Some(t.hydrate::<FROM_SERVER>(el)))
} else {
(el.clone(), None)
}
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
if let Some(t) = self {
(el.clone(), Some(t.build(el)))
} else {
(el.clone(), None)
}
}
fn rebuild(self, state: &mut Self::State) {
let el = &state.0;
let prev_state = &mut state.1;
let maybe_next_t_state = match (prev_state.take(), self) {
(Some(mut prev_t_state), None) => {
T::reset(&mut prev_t_state);
Some(None)
}
(None, Some(t)) => Some(Some(t.build(el))),
(Some(mut prev_t_state), Some(t)) => {
t.rebuild(&mut prev_t_state);
Some(Some(prev_t_state))
}
(None, None) => Some(None),
};
if let Some(next_t_state) = maybe_next_t_state {
state.1 = next_t_state;
}
}
fn into_cloneable(self) -> Self::Cloneable {
self.map(|t| t.into_cloneable())
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.map(|t| t.into_cloneable_owned())
}
fn dry_resolve(&mut self) {
if let Some(t) = self {
t.dry_resolve();
}
}
async fn resolve(self) -> Self::AsyncOutput {
if let Some(t) = self {
Some(t.resolve().await)
} else {
None
}
}
fn reset(state: &mut Self::State) {
if let Some(prev_t_state) = &mut state.1 {
T::reset(prev_t_state);
}
}
}
impl IntoClass for &str {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Self;
type CloneableOwned = Arc<str>;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, class: &mut String) {
class.push_str(self);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
if !FROM_SERVER {
Rndr::set_attribute(el, "class", self);
}
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "class", self);
(el.clone(), self)
}
fn rebuild(self, state: &mut Self::State) {
let (el, prev) = state;
if self != *prev {
Rndr::set_attribute(el, "class", self);
}
*prev = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.into()
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn reset(state: &mut Self::State) {
let (el, _prev) = state;
Rndr::remove_attribute(el, "class");
}
}
impl IntoClass for String {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Arc<str>;
type CloneableOwned = Arc<str>;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, class: &mut String) {
IntoClass::to_html(self.as_str(), class);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
if !FROM_SERVER {
Rndr::set_attribute(el, "class", &self);
}
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "class", &self);
(el.clone(), self)
}
fn rebuild(self, state: &mut Self::State) {
let (el, prev) = state;
if self != *prev {
Rndr::set_attribute(el, "class", &self);
}
*prev = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self.into()
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.into()
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn reset(state: &mut Self::State) {
let (el, _prev) = state;
Rndr::remove_attribute(el, "class");
}
}
impl IntoClass for Arc<str> {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Self;
type CloneableOwned = Self;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, class: &mut String) {
IntoClass::to_html(self.as_ref(), class);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
if !FROM_SERVER {
Rndr::set_attribute(el, "class", &self);
}
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "class", &self);
(el.clone(), self)
}
fn rebuild(self, state: &mut Self::State) {
let (el, prev) = state;
if !Arc::ptr_eq(&self, prev) {
Rndr::set_attribute(el, "class", &self);
}
*prev = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn reset(state: &mut Self::State) {
let (el, _prev) = state;
Rndr::remove_attribute(el, "class");
}
}
impl IntoClass for (&'static str, bool) {
type AsyncOutput = Self;
type State = (crate::renderer::types::ClassList, bool, &'static str);
type Cloneable = Self;
type CloneableOwned = Self;
fn html_len(&self) -> usize {
self.0.len()
}
fn to_html(self, class: &mut String) {
let (name, include) = self;
if include {
class.push_str(name);
}
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
let (name, include) = self;
let class_list = Rndr::class_list(el);
if !FROM_SERVER && include {
Rndr::add_class(&class_list, name);
}
(class_list, self.1, name)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
let (name, include) = self;
let class_list = Rndr::class_list(el);
if include {
Rndr::add_class(&class_list, name);
}
(class_list, self.1, name)
}
fn rebuild(self, state: &mut Self::State) {
let (name, include) = self;
let (class_list, prev_include, prev_name) = state;
if include != *prev_include {
if include {
Rndr::add_class(class_list, name);
} else {
Rndr::remove_class(class_list, name);
}
}
*prev_include = include;
*prev_name = name;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::Cloneable {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn reset(state: &mut Self::State) {
let (class_list, _, name) = state;
Rndr::remove_class(class_list, name);
}
}
#[cfg(feature = "nightly")]
impl<const V: &'static str> IntoClass for crate::view::static_types::Static<V> {
const TEMPLATE: &'static str = V;
type AsyncOutput = Self;
type State = ();
type Cloneable = Self;
type CloneableOwned = Self;
fn html_len(&self) -> usize {
V.len()
}
fn to_html(self, class: &mut String) {
class.push_str(V);
}
fn to_template(class: &mut String) {
class.push_str(V);
}
fn hydrate<const FROM_SERVER: bool>(
self,
_el: &crate::renderer::types::Element,
) -> Self::State {
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "class", V);
}
fn rebuild(self, _state: &mut Self::State) {}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
fn reset(_state: &mut Self::State) {}
}