use super::attribute::{
maybe_next_attr_erasure_macros::next_attr_output_type, Attribute,
NextAttribute,
};
#[cfg(all(feature = "nightly", rustc_nightly))]
use crate::view::static_types::Static;
use crate::{
html::attribute::{
maybe_next_attr_erasure_macros::next_attr_combine, NamedAttributeKey,
},
renderer::{dom::CssStyleDeclaration, Rndr},
view::{Position, ToTemplate},
};
use std::{future::Future, sync::Arc};
#[inline(always)]
pub fn style<S>(style: S) -> Style<S>
where
S: IntoStyle,
{
Style { style }
}
#[derive(Debug)]
pub struct Style<S> {
style: S,
}
impl<S> Clone for Style<S>
where
S: Clone,
{
fn clone(&self) -> Self {
Self {
style: self.style.clone(),
}
}
}
impl<S> Attribute for Style<S>
where
S: IntoStyle,
{
const MIN_LENGTH: usize = 0;
type AsyncOutput = Style<S::AsyncOutput>;
type State = S::State;
type Cloneable = Style<S::Cloneable>;
type CloneableOwned = Style<S::CloneableOwned>;
#[inline(always)]
fn html_len(&self) -> usize {
0
}
fn to_html(
self,
_buf: &mut String,
_style: &mut String,
style: &mut String,
_inner_html: &mut String,
) {
self.style.to_html(style);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
self.style.hydrate::<FROM_SERVER>(el)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
self.style.build(el)
}
fn rebuild(self, state: &mut Self::State) {
self.style.rebuild(state)
}
fn into_cloneable(self) -> Self::Cloneable {
Style {
style: self.style.into_cloneable(),
}
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
Style {
style: self.style.into_cloneable_owned(),
}
}
fn dry_resolve(&mut self) {
self.style.dry_resolve();
}
async fn resolve(self) -> Self::AsyncOutput {
Style {
style: self.style.resolve().await,
}
}
fn keys(&self) -> Vec<NamedAttributeKey> {
vec![NamedAttributeKey::Attribute("style".into())]
}
}
impl<S> NextAttribute for Style<S>
where
S: IntoStyle,
{
next_attr_output_type!(Self, NewAttr);
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
next_attr_combine!(self, new_attr)
}
}
impl<S> ToTemplate for Style<S>
where
S: IntoStyle,
{
fn to_template(
_buf: &mut String,
_style: &mut String,
_class: &mut String,
_inner_html: &mut String,
_position: &mut Position,
) {
}
}
pub trait IntoStyle: Send {
type AsyncOutput: IntoStyle;
type State;
type Cloneable: IntoStyle + Clone;
type CloneableOwned: IntoStyle + Clone + 'static;
fn to_html(self, style: &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: IntoStyle> IntoStyle 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 to_html(self, style: &mut String) {
if let Some(t) = self {
t.to_html(style);
}
}
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<'a> IntoStyle for &'a str {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, &'a str);
type Cloneable = Self;
type CloneableOwned = Arc<str>;
fn to_html(self, style: &mut String) {
style.push_str(self);
style.push(';');
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "style", self);
(el.clone(), self)
}
fn rebuild(self, state: &mut Self::State) {
let (el, prev) = state;
if self != *prev {
Rndr::set_attribute(el, "style", 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, "style");
}
}
impl IntoStyle for Arc<str> {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Arc<str>);
type Cloneable = Self;
type CloneableOwned = Self;
fn to_html(self, style: &mut String) {
style.push_str(&self);
style.push(';');
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "style", &self);
(el.clone(), self)
}
fn rebuild(self, state: &mut Self::State) {
let (el, prev) = state;
if self != *prev {
Rndr::set_attribute(el, "style", &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, "style");
}
}
impl IntoStyle for String {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, String);
type Cloneable = Arc<str>;
type CloneableOwned = Arc<str>;
fn to_html(self, style: &mut String) {
style.push_str(&self);
style.push(';');
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
Rndr::set_attribute(el, "style", &self);
(el.clone(), self)
}
fn rebuild(self, state: &mut Self::State) {
let (el, prev) = state;
if self != *prev {
Rndr::set_attribute(el, "style", &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, "style");
}
}
pub trait IntoStyleValue: Send {
type AsyncOutput: IntoStyleValue;
type State;
type Cloneable: Clone + IntoStyleValue;
type CloneableOwned: Clone + IntoStyleValue + 'static;
fn to_html(self, name: &str, style: &mut String);
fn build(self, style: &CssStyleDeclaration, name: &str) -> Self::State;
fn rebuild(
self,
style: &CssStyleDeclaration,
name: &str,
state: &mut Self::State,
);
fn hydrate(self, style: &CssStyleDeclaration, name: &str) -> 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;
}
impl<K, V> IntoStyle for (K, V)
where
K: AsRef<str> + Clone + Send + 'static,
V: IntoStyleValue,
{
type AsyncOutput = (K, V::AsyncOutput);
type State = (crate::renderer::types::CssStyleDeclaration, K, V::State);
type Cloneable = (K, V::Cloneable);
type CloneableOwned = (K, V::CloneableOwned);
fn to_html(self, style: &mut String) {
let (name, value) = self;
value.to_html(name.as_ref(), style);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
let style = Rndr::style(el);
let state = self.1.hydrate(&style, self.0.as_ref());
(style, self.0, state)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
let (name, value) = self;
let style = Rndr::style(el);
let state = value.build(&style, name.as_ref());
(style, name, state)
}
fn rebuild(self, state: &mut Self::State) {
let (name, value) = self;
if name.as_ref() != state.1.as_ref() {
<Self as IntoStyle>::reset(state);
state.2 = value.build(&state.0, name.as_ref());
} else {
value.rebuild(&state.0, name.as_ref(), &mut state.2);
}
}
fn into_cloneable(self) -> Self::Cloneable {
(self.0, self.1.into_cloneable())
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
(self.0, self.1.into_cloneable_owned())
}
fn dry_resolve(&mut self) {
self.1.dry_resolve();
}
async fn resolve(self) -> Self::AsyncOutput {
(self.0, self.1.resolve().await)
}
fn reset(state: &mut Self::State) {
let (style, name, _value) = state;
Rndr::remove_css_property(style, name.as_ref());
}
}
macro_rules! impl_style_value {
($ty:ty) => {
impl IntoStyleValue for $ty {
type AsyncOutput = Self;
type State = Self;
type Cloneable = Self;
type CloneableOwned = Self;
fn to_html(self, name: &str, style: &mut String) {
style.push_str(name);
style.push(':');
style.push_str(&self);
style.push(';');
}
fn build(
self,
style: &CssStyleDeclaration,
name: &str,
) -> Self::State {
Rndr::set_css_property(style, name, &self);
self
}
fn rebuild(
self,
style: &CssStyleDeclaration,
name: &str,
state: &mut Self::State,
) {
if &self != &*state {
Rndr::set_css_property(style, name, &self);
}
*state = self;
}
fn hydrate(
self,
_style: &CssStyleDeclaration,
_name: &str,
) -> Self::State {
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
}
}
impl IntoStyleValue for Option<$ty> {
type AsyncOutput = Self;
type State = Self;
type Cloneable = Self;
type CloneableOwned = Self;
fn to_html(self, name: &str, style: &mut String) {
if let Some(value) = self {
style.push_str(name);
style.push(':');
style.push_str(&value);
style.push(';');
}
}
fn build(
self,
style: &CssStyleDeclaration,
name: &str,
) -> Self::State {
if let Some(value) = &self {
Rndr::set_css_property(style, name, &value);
}
self
}
fn rebuild(
self,
style: &CssStyleDeclaration,
name: &str,
state: &mut Self::State,
) {
match (&state, &self) {
(None, None) => {}
(Some(_), None) => Rndr::remove_css_property(style, name),
(None, Some(value)) => {
Rndr::set_css_property(style, name, &value)
}
(Some(old), Some(new)) => {
if new != &*old {
Rndr::set_css_property(style, name, &new);
}
}
}
*state = self;
}
fn hydrate(
self,
_style: &CssStyleDeclaration,
_name: &str,
) -> Self::State {
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
}
}
};
}
impl_style_value!(&'static str);
impl_style_value!(Arc<str>);
impl_style_value!(String);
#[cfg(feature = "oco")]
impl_style_value!(oco_ref::Oco<'static, str>);
#[cfg(all(feature = "nightly", rustc_nightly))]
impl<const V: &'static str> IntoStyleValue for Static<V> {
type AsyncOutput = Self;
type State = Self;
type Cloneable = Self;
type CloneableOwned = Self;
fn to_html(self, name: &str, style: &mut String) {
style.push_str(name);
style.push(':');
style.push_str(V);
style.push(';');
}
fn build(self, style: &CssStyleDeclaration, name: &str) -> Self::State {
Rndr::set_css_property(style, name, V);
self
}
fn rebuild(
self,
_style: &CssStyleDeclaration,
_name: &str,
_state: &mut Self::State,
) {
}
fn hydrate(self, _style: &CssStyleDeclaration, _name: &str) -> Self::State {
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
}
}
#[cfg(all(feature = "nightly", rustc_nightly))]
impl<const V: &'static str> IntoStyleValue for Option<Static<V>> {
type AsyncOutput = Self;
type State = Self;
type Cloneable = Self;
type CloneableOwned = Self;
fn to_html(self, name: &str, style: &mut String) {
if self.is_some() {
style.push_str(name);
style.push(':');
style.push_str(V);
style.push(';');
}
}
fn build(self, style: &CssStyleDeclaration, name: &str) -> Self::State {
if self.is_some() {
Rndr::set_css_property(style, name, V);
}
self
}
fn rebuild(
self,
style: &CssStyleDeclaration,
name: &str,
state: &mut Self::State,
) {
match (&state, &self) {
(None, None) => {}
(Some(_), None) => Rndr::remove_css_property(style, name),
(None, Some(_)) => Rndr::set_css_property(style, name, V),
(Some(_), Some(_)) => {}
}
*state = self;
}
fn hydrate(self, _style: &CssStyleDeclaration, _name: &str) -> Self::State {
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
}
}
#[cfg(all(feature = "nightly", rustc_nightly))]
impl<const V: &'static str> IntoStyle for crate::view::static_types::Static<V> {
type AsyncOutput = Self;
type State = ();
type Cloneable = Self;
type CloneableOwned = Self;
fn to_html(self, style: &mut String) {
style.push_str(V);
style.push(';');
}
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, "style", 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) {}
}