pass on: to components (and lay basis for passing all other attributes)

This commit is contained in:
Greg Johnston 2024-02-23 15:59:42 -05:00
parent 53703f208a
commit 9e276a8879
21 changed files with 644 additions and 435 deletions

View File

@ -5,7 +5,7 @@
- [x] todomvc
- [x] error_boundary
- [x] parent\_child
- [ ] on: on components
- [x] on: on components
- [ ] router
- [ ] slots
- [ ] hackernews

View File

@ -53,7 +53,6 @@ pub fn App() -> impl IntoView {
// Button B: pass a closure
<ButtonB on_click=move |_| set_right.update(|value| *value = !*value)/>
// TODO -- on:click on components
// Button C: use a regular event listener
// setting an event listener on a component like this applies it
// to each of the top-level elements the component returns
@ -99,10 +98,12 @@ where
}
}
use leptos::tachys::view::add_attr::AddAnyAttr;
/// Button C is a dummy: it renders a button but doesn't handle
/// its click. Instead, the parent component adds an event listener.
#[component]
pub fn ButtonC() -> impl IntoView {
pub fn ButtonC() -> impl IntoView + AddAnyAttr<Dom> {
view! {
<button>
"Toggle Italics"

View File

@ -1,23 +1,28 @@
use leptos_dom::events::EventDescriptor;
use leptos_dom::events::{on, EventDescriptor, On};
use tachys::{
html::attribute::global::OnAttribute,
html::attribute::{global::OnAttribute, Attribute},
hydration::Cursor,
renderer::{dom::Dom, Renderer},
renderer::{dom::Dom, DomRenderer, Renderer},
ssr::StreamBuilder,
view::{Mountable, Position, PositionState, Render, RenderHtml},
view::{
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
},
};
pub struct View<T>(T)
where
T: Sized;
pub trait IntoView: Sized + Render<Dom> + RenderHtml<Dom> {
pub trait IntoView:
Sized + Render<Dom> + RenderHtml<Dom> + AddAnyAttr<Dom>
{
fn into_view(self) -> View<Self>;
}
impl<T: Render<Dom> + RenderHtml<Dom>> IntoView for T
impl<T> IntoView for T
where
T: Sized,
T: Sized + Render<Dom> + RenderHtml<Dom> + AddAnyAttr<Dom>,
{
fn into_view(self) -> View<Self> {
View(self)
@ -74,66 +79,27 @@ impl<T: RenderHtml<Dom>> RenderHtml<Dom> for View<T> {
}
}
/*pub trait IntoView {
const MIN_HTML_LENGTH: usize;
impl<T: AddAnyAttr<Dom>> AddAnyAttr<Dom> for View<T> {
type Output<SomeNewAttr: Attribute<Dom>> =
<T as AddAnyAttr<Dom>>::Output<SomeNewAttr>;
type State: Mountable<Dom>;
fn build(self) -> Self::State;
fn rebuild(self, state: &mut Self::State);
fn to_html_with_buf(self, buf: &mut String, position: &mut Position);
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
fn add_any_attr<NewAttr: Attribute<Dom>>(
self,
buf: &mut StreamBuilder,
position: &mut Position,
);
fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &Cursor<Dom>,
position: &PositionState,
) -> Self::State;
}
impl<T: RenderHtml<Dom>> IntoView for T {}
impl<T: IntoView> Render<Dom> for T {
type State = <Self as IntoView>::State;
fn build(self) -> Self::State {
IntoView::build(self)
}
fn rebuild(self, state: &mut Self::State) {
IntoView::rebuild(self, state);
}
}
impl<T: IntoView> RenderHtml<Dom> for T {
const MIN_LENGTH: usize = T::MIN_HTML_LENGTH;
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
IntoView::to_html_with_buf(self, buf, position);
}
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
self,
buf: &mut StreamBuilder,
position: &mut Position,
) where
Self: Sized,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Dom>,
{
IntoView::to_html_async_with_buf::<OUT_OF_ORDER>(self, buf, position);
self.0.add_any_attr(attr)
}
fn hydrate<const FROM_SERVER: bool>(
fn add_any_attr_by_ref<NewAttr: Attribute<Dom>>(
self,
cursor: &Cursor<Dom>,
position: &PositionState,
) -> Self::State {
IntoView::hydrate::<FROM_SERVER>(self, cursor, position)
attr: &NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Dom>,
{
self.0.add_any_attr_by_ref(attr)
}
}*/
}

View File

@ -1,10 +1,10 @@
#![no_std]
#![allow(non_snake_case)]
pub trait TupleBuilder<Next> {
type Output;
pub trait TupleBuilder {
type Output<Next>;
fn next_tuple(self, next: Next) -> Self::Output;
fn next_tuple<Next>(self, next: Next) -> Self::Output<Next>;
}
pub trait ConcatTuples<Next> {
@ -14,11 +14,11 @@ pub trait ConcatTuples<Next> {
}
macro_rules! impl_tuple_builder {
($($ty:ident),* => $last:ident) => {
impl<$($ty),*, $last> TupleBuilder<$last> for ($($ty,)*) {
type Output = ($($ty,)* $last);
($($ty:ident),*) => {
impl<$($ty),*> TupleBuilder for ($($ty,)*) {
type Output<Next> = ($($ty,)* Next);
fn next_tuple(self, next: $last) -> Self::Output {
fn next_tuple<Next>(self, next: Next) -> Self::Output<Next> {
let ($($ty,)*) = self;
($($ty,)* next)
}
@ -26,51 +26,50 @@ macro_rules! impl_tuple_builder {
};
}
impl<A> TupleBuilder<A> for () {
type Output = (A,);
impl TupleBuilder for () {
type Output<Next> = (Next,);
fn next_tuple(self, next: A) -> Self::Output {
fn next_tuple<Next>(self, next: Next) -> Self::Output<Next> {
(next,)
}
}
impl_tuple_builder!(A => B);
impl_tuple_builder!(A, B => C);
impl_tuple_builder!(A, B, C => D);
impl_tuple_builder!(A, B, C, D => E);
impl_tuple_builder!(A, B, C, D, E => F);
impl_tuple_builder!(A, B, C, D, E, F => G);
impl_tuple_builder!(A, B, C, D, E, F, G => H);
impl_tuple_builder!(A, B, C, D, E, F, G, H => I);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I => J);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J => K);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K => L);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L => M);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M => N);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N => O);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O => P);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P => Q);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q => R);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R => S);
impl_tuple_builder!(A);
impl_tuple_builder!(A, B);
impl_tuple_builder!(A, B, C);
impl_tuple_builder!(A, B, C, D);
impl_tuple_builder!(A, B, C, D, E);
impl_tuple_builder!(A, B, C, D, E, F);
impl_tuple_builder!(A, B, C, D, E, F, G);
impl_tuple_builder!(A, B, C, D, E, F, G, H);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_tuple_builder!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S => T
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U
);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T => U
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V
);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U => V
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W
);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V => W
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X
);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W => X
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X => Y
);
impl_tuple_builder!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y =>
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,
Z
);

View File

@ -1,201 +1,156 @@
use crate::{
html::attribute::{Attr, *},
view::AddAttribute,
view::add_attr::AddAnyAttr,
};
pub trait AriaAttributes<Rndr, V>
where
Self: Sized
+ AddAttribute<Attr<AriaAtomic, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaBusy, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaControls, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaCurrent, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDescribedby, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDescription, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDetails, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDisabled, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDropeffect, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaErrormessage, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaFlowto, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaGrabbed, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaHaspopup, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaHidden, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaInvalid, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaKeyshortcuts, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaLabel, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaLabelledby, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaLive, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaOwns, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaRelevant, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaRoledescription, V, Rndr>, Rndr>,
Self: Sized + AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>,
Rndr: Renderer,
{
fn aria_atomic(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaAtomic, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_atomic(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaAtomic, V, Rndr>> {
self.add_any_attr(aria_atomic(value))
}
fn aria_busy(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaBusy, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_busy(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaBusy, V, Rndr>> {
self.add_any_attr(aria_busy(value))
}
fn aria_controls(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaControls, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_controls(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaControls, V, Rndr>> {
self.add_any_attr(aria_controls(value))
}
fn aria_current(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaCurrent, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_current(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaCurrent, V, Rndr>> {
self.add_any_attr(aria_current(value))
}
fn aria_describedby(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaDescribedby, V, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDescribedby, V, Rndr>>
{
self.add_attr(aria_describedby(value))
self.add_any_attr(aria_describedby(value))
}
fn aria_description(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaDescription, V, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDescription, V, Rndr>>
{
self.add_attr(aria_description(value))
self.add_any_attr(aria_description(value))
}
fn aria_details(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaDetails, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_details(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDetails, V, Rndr>> {
self.add_any_attr(aria_details(value))
}
fn aria_disabled(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaDisabled, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_disabled(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDisabled, V, Rndr>> {
self.add_any_attr(aria_disabled(value))
}
fn aria_dropeffect(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaDropeffect, V, Rndr>, Rndr>>::Output
{
self.add_attr(aria_dropeffect(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDropeffect, V, Rndr>> {
self.add_any_attr(aria_dropeffect(value))
}
fn aria_errormessage(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaErrormessage, V, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaErrormessage, V, Rndr>>
{
self.add_attr(aria_errormessage(value))
self.add_any_attr(aria_errormessage(value))
}
fn aria_flowto(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaFlowto, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_flowto(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaFlowto, V, Rndr>> {
self.add_any_attr(aria_flowto(value))
}
fn aria_grabbed(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaGrabbed, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_grabbed(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaGrabbed, V, Rndr>> {
self.add_any_attr(aria_grabbed(value))
}
fn aria_haspopup(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaHaspopup, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_haspopup(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaHaspopup, V, Rndr>> {
self.add_any_attr(aria_haspopup(value))
}
fn aria_hidden(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaHidden, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_hidden(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaHidden, V, Rndr>> {
self.add_any_attr(aria_hidden(value))
}
fn aria_invalid(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaInvalid, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_invalid(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaInvalid, V, Rndr>> {
self.add_any_attr(aria_invalid(value))
}
fn aria_keyshortcuts(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaKeyshortcuts, V, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaKeyshortcuts, V, Rndr>>
{
self.add_attr(aria_keyshortcuts(value))
self.add_any_attr(aria_keyshortcuts(value))
}
fn aria_label(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaLabel, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_label(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaLabel, V, Rndr>> {
self.add_any_attr(aria_label(value))
}
fn aria_labelledby(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaLabelledby, V, Rndr>, Rndr>>::Output
{
self.add_attr(aria_labelledby(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaLabelledby, V, Rndr>> {
self.add_any_attr(aria_labelledby(value))
}
fn aria_live(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaLive, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_live(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaLive, V, Rndr>> {
self.add_any_attr(aria_live(value))
}
fn aria_owns(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaOwns, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_owns(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaOwns, V, Rndr>> {
self.add_any_attr(aria_owns(value))
}
fn aria_relevant(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaRelevant, V, Rndr>, Rndr>>::Output {
self.add_attr(aria_relevant(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRelevant, V, Rndr>> {
self.add_any_attr(aria_relevant(value))
}
fn aria_roledescription(
self,
value: V,
) -> <Self as AddAttribute<Attr<AriaRoledescription, V, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRoledescription, V, Rndr>>
{
self.add_attr(aria_roledescription(value))
self.add_any_attr(aria_roledescription(value))
}
}
impl<T, Rndr, V> AriaAttributes<Rndr, V> for T
where
T: AddAttribute<Attr<AriaAtomic, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaBusy, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaControls, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaCurrent, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDescribedby, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDescription, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDetails, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDisabled, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaDropeffect, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaErrormessage, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaFlowto, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaGrabbed, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaHaspopup, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaHidden, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaInvalid, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaKeyshortcuts, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaLabel, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaLabelledby, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaLive, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaOwns, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaRelevant, V, Rndr>, Rndr>
+ AddAttribute<Attr<AriaRoledescription, V, Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>,
Rndr: Renderer,
{

View File

@ -1,7 +1,8 @@
use super::NextAttribute;
use crate::{
html::attribute::{Attribute, AttributeValue},
renderer::DomRenderer,
view::{AddAttribute, Position, ToTemplate},
view::{add_attr::AddAnyAttr, Position, ToTemplate},
};
use std::{borrow::Cow, marker::PhantomData, rc::Rc, sync::Arc};
@ -66,6 +67,22 @@ where
}
}
impl<K, V, R> NextAttribute<R> for CustomAttr<K, V, R>
where
K: CustomAttributeKey,
V: AttributeValue<R>,
R: DomRenderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
impl<K, V, R> ToTemplate for CustomAttr<K, V, R>
where
K: CustomAttributeKey,
@ -125,20 +142,20 @@ where
K: CustomAttributeKey,
V: AttributeValue<Rndr>,
Rndr: DomRenderer,
Self: Sized + AddAttribute<CustomAttr<K, V, Rndr>, Rndr>,
Self: Sized + AddAnyAttr<Rndr>,
{
fn attr(
self,
key: K,
value: V,
) -> <Self as AddAttribute<CustomAttr<K, V, Rndr>, Rndr>>::Output {
self.add_attr(custom_attribute(key, value))
) -> <Self as AddAnyAttr<Rndr>>::Output<CustomAttr<K, V, Rndr>> {
self.add_any_attr(custom_attribute(key, value))
}
}
impl<T, K, V, Rndr> CustomAttribute<K, V, Rndr> for T
where
T: AddAttribute<CustomAttr<K, V, Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
K: CustomAttributeKey,
V: AttributeValue<Rndr>,
Rndr: DomRenderer,

View File

@ -9,7 +9,7 @@ use crate::{
style::{style, IntoStyle, Style},
},
renderer::DomRenderer,
view::AddAttribute,
view::add_attr::AddAnyAttr,
};
use core::convert::From;
@ -25,14 +25,14 @@ where
impl<T, C, Rndr> ClassAttribute<C, Rndr> for T
where
T: AddAttribute<Class<C, Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
C: IntoClass<Rndr>,
Rndr: DomRenderer,
{
type Output = <Self as AddAttribute<Class<C, Rndr>, Rndr>>::Output;
type Output = <Self as AddAnyAttr<Rndr>>::Output<Class<C, Rndr>>;
fn class(self, value: C) -> Self::Output {
self.add_attr(class(value))
self.add_any_attr(class(value))
}
}
@ -48,14 +48,14 @@ where
impl<T, K, P, Rndr> PropAttribute<K, P, Rndr> for T
where
T: AddAttribute<Property<K, P, Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
K: AsRef<str>,
P: IntoProperty<Rndr>,
Rndr: DomRenderer,
{
type Output = <Self as AddAttribute<Property<K, P, Rndr>, Rndr>>::Output;
type Output = <Self as AddAnyAttr<Rndr>>::Output<Property<K, P, Rndr>>;
fn prop(self, key: K, value: P) -> Self::Output {
self.add_attr(property(key, value))
self.add_any_attr(property(key, value))
}
}
@ -71,14 +71,14 @@ where
impl<T, S, Rndr> StyleAttribute<S, Rndr> for T
where
T: AddAttribute<Style<S, Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
S: IntoStyle<Rndr>,
Rndr: DomRenderer,
{
type Output = <Self as AddAttribute<Style<S, Rndr>, Rndr>>::Output;
type Output = <Self as AddAnyAttr<Rndr>>::Output<Style<S, Rndr>>;
fn style(self, value: S) -> Self::Output {
self.add_attr(style(value))
self.add_any_attr(style(value))
}
}
@ -90,17 +90,17 @@ pub trait OnAttribute<E, F, Rndr> {
impl<T, E, F, Rndr> OnAttribute<E, F, Rndr> for T
where
T: AddAttribute<On<Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
E: EventDescriptor + 'static,
E::EventType: 'static,
E::EventType: From<Rndr::Event>,
F: FnMut(E::EventType) + 'static,
Rndr: DomRenderer,
{
type Output = <Self as AddAttribute<On<Rndr>, Rndr>>::Output;
type Output = <Self as AddAnyAttr<Rndr>>::Output<On<Rndr>>;
fn on(self, event: E, cb: F) -> Self::Output {
self.add_attr(on(event, cb))
self.add_any_attr(on(event, cb))
}
}
@ -113,7 +113,7 @@ pub trait OnTargetAttribute<E, F, T, Rndr> {
impl<T, E, F, Rndr> OnTargetAttribute<E, F, Self, Rndr> for T
where
Self: ElementType,
T: AddAttribute<On<Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
E: EventDescriptor + 'static,
E::EventType: 'static,
E::EventType: From<Rndr::Event>,
@ -121,276 +121,221 @@ where
+ 'static,
Rndr: DomRenderer,
{
type Output = <Self as AddAttribute<On<Rndr>, Rndr>>::Output;
fn on_target(self, event: E, cb: F) -> Self::Output
where {
self.add_attr(on_target(event, cb))
type Output = <Self as AddAnyAttr<Rndr>>::Output<On<Rndr>>;
fn on_target(self, event: E, cb: F) -> Self::Output {
self.add_any_attr(on_target(event, cb))
}
}
pub trait GlobalAttributes<Rndr, V>
where
Self: Sized
+ AddAttribute<Attr<Accesskey, V, Rndr>, Rndr>
+ AddAttribute<Attr<Autocapitalize, V, Rndr>, Rndr>
+ AddAttribute<Attr<Autofocus, V, Rndr>, Rndr>
+ AddAttribute<Attr<Contenteditable, V, Rndr>, Rndr>
+ AddAttribute<Attr<Dir, V, Rndr>, Rndr>
+ AddAttribute<Attr<Draggable, V, Rndr>, Rndr>
+ AddAttribute<Attr<Enterkeyhint, V, Rndr>, Rndr>
+ AddAttribute<Attr<Hidden, V, Rndr>, Rndr>
+ AddAttribute<Attr<Id, V, Rndr>, Rndr>
+ AddAttribute<Attr<Inert, V, Rndr>, Rndr>
+ AddAttribute<Attr<Inputmode, V, Rndr>, Rndr>
+ AddAttribute<Attr<Is, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemid, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemprop, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemref, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemscope, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemtype, V, Rndr>, Rndr>
+ AddAttribute<Attr<Lang, V, Rndr>, Rndr>
+ AddAttribute<Attr<Nonce, V, Rndr>, Rndr>
+ AddAttribute<Attr<Part, V, Rndr>, Rndr>
+ AddAttribute<Attr<Popover, V, Rndr>, Rndr>
+ AddAttribute<Attr<Role, V, Rndr>, Rndr>
+ AddAttribute<Attr<Slot, V, Rndr>, Rndr>
+ AddAttribute<Attr<Spellcheck, V, Rndr>, Rndr>
+ AddAttribute<Attr<Tabindex, V, Rndr>, Rndr>
+ AddAttribute<Attr<Title, V, Rndr>, Rndr>
+ AddAttribute<Attr<Translate, V, Rndr>, Rndr>
+ AddAttribute<Attr<Virtualkeyboardpolicy, V, Rndr>, Rndr>,
Self: Sized + AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>,
Rndr: Renderer,
{
fn accesskey(
self,
value: V,
) -> <Self as AddAttribute<Attr<Accesskey, V, Rndr>, Rndr>>::Output {
self.add_attr(accesskey(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Accesskey, V, Rndr>> {
self.add_any_attr(accesskey(value))
}
fn autocapitalize(
self,
value: V,
) -> <Self as AddAttribute<Attr<Autocapitalize, V, Rndr>, Rndr>>::Output
{
self.add_attr(autocapitalize(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Autocapitalize, V, Rndr>> {
self.add_any_attr(autocapitalize(value))
}
fn autofocus(
self,
value: V,
) -> <Self as AddAttribute<Attr<Autofocus, V, Rndr>, Rndr>>::Output {
self.add_attr(autofocus(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Autofocus, V, Rndr>> {
self.add_any_attr(autofocus(value))
}
fn contenteditable(
self,
value: V,
) -> <Self as AddAttribute<Attr<Contenteditable, V, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Contenteditable, V, Rndr>>
{
self.add_attr(contenteditable(value))
self.add_any_attr(contenteditable(value))
}
fn dir(
self,
value: V,
) -> <Self as AddAttribute<Attr<Dir, V, Rndr>, Rndr>>::Output {
self.add_attr(dir(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Dir, V, Rndr>> {
self.add_any_attr(dir(value))
}
fn draggable(
self,
value: V,
) -> <Self as AddAttribute<Attr<Draggable, V, Rndr>, Rndr>>::Output {
self.add_attr(draggable(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Draggable, V, Rndr>> {
self.add_any_attr(draggable(value))
}
fn enterkeyhint(
self,
value: V,
) -> <Self as AddAttribute<Attr<Enterkeyhint, V, Rndr>, Rndr>>::Output {
self.add_attr(enterkeyhint(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Enterkeyhint, V, Rndr>> {
self.add_any_attr(enterkeyhint(value))
}
fn hidden(
self,
value: V,
) -> <Self as AddAttribute<Attr<Hidden, V, Rndr>, Rndr>>::Output {
self.add_attr(hidden(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Hidden, V, Rndr>> {
self.add_any_attr(hidden(value))
}
fn id(
self,
value: V,
) -> <Self as AddAttribute<Attr<Id, V, Rndr>, Rndr>>::Output {
self.add_attr(id(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Id, V, Rndr>> {
self.add_any_attr(id(value))
}
fn inert(
self,
value: V,
) -> <Self as AddAttribute<Attr<Inert, V, Rndr>, Rndr>>::Output {
self.add_attr(inert(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Inert, V, Rndr>> {
self.add_any_attr(inert(value))
}
fn inputmode(
self,
value: V,
) -> <Self as AddAttribute<Attr<Inputmode, V, Rndr>, Rndr>>::Output {
self.add_attr(inputmode(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Inputmode, V, Rndr>> {
self.add_any_attr(inputmode(value))
}
fn is(
self,
value: V,
) -> <Self as AddAttribute<Attr<Is, V, Rndr>, Rndr>>::Output {
self.add_attr(is(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Is, V, Rndr>> {
self.add_any_attr(is(value))
}
fn itemid(
self,
value: V,
) -> <Self as AddAttribute<Attr<Itemid, V, Rndr>, Rndr>>::Output {
self.add_attr(itemid(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemid, V, Rndr>> {
self.add_any_attr(itemid(value))
}
fn itemprop(
self,
value: V,
) -> <Self as AddAttribute<Attr<Itemprop, V, Rndr>, Rndr>>::Output {
self.add_attr(itemprop(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemprop, V, Rndr>> {
self.add_any_attr(itemprop(value))
}
fn itemref(
self,
value: V,
) -> <Self as AddAttribute<Attr<Itemref, V, Rndr>, Rndr>>::Output {
self.add_attr(itemref(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemref, V, Rndr>> {
self.add_any_attr(itemref(value))
}
fn itemscope(
self,
value: V,
) -> <Self as AddAttribute<Attr<Itemscope, V, Rndr>, Rndr>>::Output {
self.add_attr(itemscope(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemscope, V, Rndr>> {
self.add_any_attr(itemscope(value))
}
fn itemtype(
self,
value: V,
) -> <Self as AddAttribute<Attr<Itemtype, V, Rndr>, Rndr>>::Output {
self.add_attr(itemtype(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemtype, V, Rndr>> {
self.add_any_attr(itemtype(value))
}
fn lang(
self,
value: V,
) -> <Self as AddAttribute<Attr<Lang, V, Rndr>, Rndr>>::Output {
self.add_attr(lang(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Lang, V, Rndr>> {
self.add_any_attr(lang(value))
}
fn nonce(
self,
value: V,
) -> <Self as AddAttribute<Attr<Nonce, V, Rndr>, Rndr>>::Output {
self.add_attr(nonce(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Nonce, V, Rndr>> {
self.add_any_attr(nonce(value))
}
fn part(
self,
value: V,
) -> <Self as AddAttribute<Attr<Part, V, Rndr>, Rndr>>::Output {
self.add_attr(part(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Part, V, Rndr>> {
self.add_any_attr(part(value))
}
fn popover(
self,
value: V,
) -> <Self as AddAttribute<Attr<Popover, V, Rndr>, Rndr>>::Output {
self.add_attr(popover(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Popover, V, Rndr>> {
self.add_any_attr(popover(value))
}
fn role(
self,
value: V,
) -> <Self as AddAttribute<Attr<Role, V, Rndr>, Rndr>>::Output {
self.add_attr(role(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Role, V, Rndr>> {
self.add_any_attr(role(value))
}
fn slot(
self,
value: V,
) -> <Self as AddAttribute<Attr<Slot, V, Rndr>, Rndr>>::Output {
self.add_attr(slot(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Slot, V, Rndr>> {
self.add_any_attr(slot(value))
}
fn spellcheck(
self,
value: V,
) -> <Self as AddAttribute<Attr<Spellcheck, V, Rndr>, Rndr>>::Output {
self.add_attr(spellcheck(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Spellcheck, V, Rndr>> {
self.add_any_attr(spellcheck(value))
}
fn tabindex(
self,
value: V,
) -> <Self as AddAttribute<Attr<Tabindex, V, Rndr>, Rndr>>::Output {
self.add_attr(tabindex(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Tabindex, V, Rndr>> {
self.add_any_attr(tabindex(value))
}
fn title(
self,
value: V,
) -> <Self as AddAttribute<Attr<Title, V, Rndr>, Rndr>>::Output {
self.add_attr(title(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Title, V, Rndr>> {
self.add_any_attr(title(value))
}
fn translate(
self,
value: V,
) -> <Self as AddAttribute<Attr<Translate, V, Rndr>, Rndr>>::Output {
self.add_attr(translate(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Translate, V, Rndr>> {
self.add_any_attr(translate(value))
}
fn virtualkeyboardpolicy(
self,
value: V,
) -> <Self as AddAttribute<Attr<Virtualkeyboardpolicy, V, Rndr>, Rndr>>::Output{
self.add_attr(virtualkeyboardpolicy(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Virtualkeyboardpolicy, V, Rndr>>
{
self.add_any_attr(virtualkeyboardpolicy(value))
}
}
impl<T, Rndr, V> GlobalAttributes<Rndr, V> for T
where
T: AddAttribute<Attr<Accesskey, V, Rndr>, Rndr>
+ AddAttribute<Attr<Autocapitalize, V, Rndr>, Rndr>
+ AddAttribute<Attr<Autofocus, V, Rndr>, Rndr>
+ AddAttribute<Attr<Contenteditable, V, Rndr>, Rndr>
+ AddAttribute<Attr<Dir, V, Rndr>, Rndr>
+ AddAttribute<Attr<Draggable, V, Rndr>, Rndr>
+ AddAttribute<Attr<Enterkeyhint, V, Rndr>, Rndr>
+ AddAttribute<Attr<Hidden, V, Rndr>, Rndr>
+ AddAttribute<Attr<Id, V, Rndr>, Rndr>
+ AddAttribute<Attr<Inert, V, Rndr>, Rndr>
+ AddAttribute<Attr<Inputmode, V, Rndr>, Rndr>
+ AddAttribute<Attr<Is, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemid, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemprop, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemref, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemscope, V, Rndr>, Rndr>
+ AddAttribute<Attr<Itemtype, V, Rndr>, Rndr>
+ AddAttribute<Attr<Lang, V, Rndr>, Rndr>
+ AddAttribute<Attr<Nonce, V, Rndr>, Rndr>
+ AddAttribute<Attr<Part, V, Rndr>, Rndr>
+ AddAttribute<Attr<Popover, V, Rndr>, Rndr>
+ AddAttribute<Attr<Role, V, Rndr>, Rndr>
+ AddAttribute<Attr<Slot, V, Rndr>, Rndr>
+ AddAttribute<Attr<Spellcheck, V, Rndr>, Rndr>
+ AddAttribute<Attr<Tabindex, V, Rndr>, Rndr>
+ AddAttribute<Attr<Title, V, Rndr>, Rndr>
+ AddAttribute<Attr<Translate, V, Rndr>, Rndr>
+ AddAttribute<Attr<Virtualkeyboardpolicy, V, Rndr>, Rndr>,
T: AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>,
Rndr: Renderer,
{

View File

@ -11,7 +11,7 @@ pub use key::*;
use std::{fmt::Debug, marker::PhantomData};
pub use value::*;
pub trait Attribute<R: Renderer> {
pub trait Attribute<R: Renderer>: NextAttribute<R> {
const MIN_LENGTH: usize;
type State;
@ -31,6 +31,15 @@ pub trait Attribute<R: Renderer> {
fn rebuild(self, state: &mut Self::State);
}
pub trait NextAttribute<R: Renderer> {
type Output<NewAttr: Attribute<R>>: Attribute<R>;
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr>;
}
impl<R> Attribute<R> for ()
where
R: Renderer,
@ -56,6 +65,20 @@ where
fn rebuild(self, _state: &mut Self::State) {}
}
impl<R> NextAttribute<R> for ()
where
R: Renderer,
{
type Output<NewAttr: Attribute<R>> = (NewAttr,);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(new_attr,)
}
}
#[derive(Debug)]
pub struct Attr<K, V, R>(pub K, pub V, PhantomData<R>)
where
@ -113,6 +136,22 @@ where
}
}
impl<K, V, R> NextAttribute<R> for Attr<K, V, R>
where
K: AttributeKey,
V: AttributeValue<R>,
R: Renderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
macro_rules! impl_attr_for_tuples {
($first:ident, $($ty:ident),* $(,)?) => {
impl<$first, $($ty),*, Rndr> Attribute<Rndr> for ($first, $($ty,)*)
@ -161,6 +200,93 @@ macro_rules! impl_attr_for_tuples {
$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
}
}
}
impl<$first, $($ty),*, Rndr> NextAttribute<Rndr> for ($first, $($ty,)*)
where
$first: Attribute<Rndr>,
$($ty: Attribute<Rndr>),*,
Rndr: Renderer
{
type Output<NewAttr: Attribute<Rndr>> = ($first, $($ty,)* NewAttr);
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
($first, $($ty,)* new_attr)
}
}
};
}
macro_rules! impl_attr_for_tuples_truncate_additional {
($first:ident, $($ty:ident),* $(,)?) => {
impl<$first, $($ty),*, Rndr> Attribute<Rndr> for ($first, $($ty,)*)
where
$first: Attribute<Rndr>,
$($ty: Attribute<Rndr>),*,
Rndr: Renderer
{
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
type State = ($first::State, $($ty::State,)*);
fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)* ) = self;
[<$first:lower>].to_html(buf, class, style, inner_html);
$([<$ty:lower>].to_html(buf, class, style, inner_html));*
}
}
fn hydrate<const FROM_SERVER: bool>(self, el: &Rndr::Element) -> Self::State {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)* ) = self;
(
[<$first:lower>].hydrate::<FROM_SERVER>(el),
$([<$ty:lower>].hydrate::<FROM_SERVER>(el)),*
)
}
}
fn build(self, el: &Rndr::Element) -> Self::State {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
(
[<$first:lower>].build(el),
$([<$ty:lower>].build(el)),*
)
}
}
fn rebuild(self, state: &mut Self::State) {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
[<$first:lower>].rebuild([<view_ $first:lower>]);
$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
}
}
}
impl<$first, $($ty),*, Rndr> NextAttribute<Rndr> for ($first, $($ty,)*)
where
$first: Attribute<Rndr>,
$($ty: Attribute<Rndr>),*,
Rndr: Renderer
{
type Output<NewAttr: Attribute<Rndr>> = ($first, $($ty,)*);
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
_new_attr: NewAttr,
) -> Self::Output<NewAttr> {
todo!("adding more than 26 attributes is not supported");
//($first, $($ty,)*)
}
}
};
}
@ -200,6 +326,21 @@ where
}
}
impl<A, Rndr> NextAttribute<Rndr> for (A,)
where
A: Attribute<Rndr>,
Rndr: Renderer,
{
type Output<NewAttr: Attribute<Rndr>> = (A, NewAttr);
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self.0, new_attr)
}
}
impl_attr_for_tuples!(A, B);
impl_attr_for_tuples!(A, B, C);
impl_attr_for_tuples!(A, B, C, D);
@ -236,7 +377,7 @@ impl_attr_for_tuples!(
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
);
impl_attr_for_tuples!(
impl_attr_for_tuples_truncate_additional!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,
Z
);

View File

@ -1,4 +1,4 @@
use super::attribute::Attribute;
use super::attribute::{Attribute, NextAttribute};
use crate::{
renderer::DomRenderer,
view::{Position, ToTemplate},
@ -59,6 +59,21 @@ where
}
}
impl<C, R> NextAttribute<R> for Class<C, R>
where
C: IntoClass<R>,
R: DomRenderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
impl<C, R> ToTemplate for Class<C, R>
where
C: IntoClass<R>,

View File

@ -49,13 +49,13 @@ macro_rules! html_elements {
#[doc = concat!("The [`", stringify!($attr), "`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/", stringify!($tag), "#", stringify!($attr) ,") attribute on `<", stringify!($tag), ">`.")]
pub fn $attr<V>(self, value: V) -> HtmlElement <
[<$tag:camel>],
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
Ch, Rndr
>
where
V: AttributeValue<Rndr>,
At: TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output: Attribute<Rndr>,
At: TupleBuilder,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
{
let HtmlElement { tag, rndr, children, attributes } = self;
HtmlElement {
@ -135,14 +135,15 @@ macro_rules! html_self_closing_elements {
#[doc = concat!("The [`", stringify!($attr), "`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/", stringify!($tag), "#", stringify!($attr) ,") attribute on `<", stringify!($tag), ">`.")]
pub fn $attr<V>(self, value: V) -> HtmlElement<
[<$tag:camel>],
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
(),
Rndr
>
where
V: AttributeValue<Rndr>,
At: TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output: Attribute<Rndr>,
At: TupleBuilder,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
{
let HtmlElement { tag, rndr, children, attributes } = self;
HtmlElement {

View File

@ -1,8 +1,9 @@
use super::{ElementWithChildren, HtmlElement};
use crate::{
html::{attribute::Attribute, element::AddAttribute},
html::attribute::{Attribute, NextAttribute},
prelude::Render,
renderer::{DomRenderer, Renderer},
view::add_attr::AddAnyAttr,
};
use std::marker::PhantomData;
@ -68,33 +69,48 @@ where
}
}
impl<T, R> NextAttribute<R> for InnerHtml<T, R>
where
T: AsRef<str> + PartialEq,
R: DomRenderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
pub trait InnerHtmlAttribute<T, Rndr>
where
T: AsRef<str>,
T: AsRef<str> + PartialEq,
Rndr: DomRenderer,
Self: Sized + AddAttribute<InnerHtml<T, Rndr>, Rndr>,
Self: Sized + AddAnyAttr<Rndr>,
{
fn inner_html(
self,
value: T,
) -> <Self as AddAttribute<InnerHtml<T, Rndr>, Rndr>>::Output {
self.add_attr(inner_html(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<InnerHtml<T, Rndr>> {
self.add_any_attr(inner_html(value))
}
}
impl<T, E, At, Rndr> InnerHtmlAttribute<T, Rndr>
for HtmlElement<E, At, (), Rndr>
where
Self: AddAttribute<InnerHtml<T, Rndr>, Rndr>,
Self: AddAnyAttr<Rndr>,
E: ElementWithChildren,
At: Attribute<Rndr>,
T: AsRef<str>,
T: AsRef<str> + PartialEq,
Rndr: DomRenderer,
{
fn inner_html(
self,
value: T,
) -> <Self as AddAttribute<InnerHtml<T, Rndr>, Rndr>>::Output {
self.add_attr(inner_html(value))
) -> <Self as AddAnyAttr<Rndr>>::Output<InnerHtml<T, Rndr>> {
self.add_any_attr(inner_html(value))
}
}

View File

@ -4,8 +4,8 @@ use crate::{
renderer::{CastFrom, Renderer},
ssr::StreamBuilder,
view::{
AddAttribute, Mountable, Position, PositionState, Render, RenderHtml,
ToTemplate,
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml, ToTemplate,
},
};
use const_str_slice_concat::{
@ -17,17 +17,13 @@ use std::marker::PhantomData;
mod custom;
mod elements;
mod inner_html;
use super::attribute::NextAttribute;
pub use custom::*;
pub use elements::*;
pub use inner_html::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HtmlElement<E, At, Ch, Rndr>
where
At: Attribute<Rndr>,
Ch: Render<Rndr>,
Rndr: Renderer,
{
pub struct HtmlElement<E, At, Ch, Rndr> {
pub(crate) tag: E,
pub(crate) rndr: PhantomData<Rndr>,
pub(crate) attributes: At,
@ -37,9 +33,6 @@ where
impl<E, At, Ch, Rndr> ElementType for HtmlElement<E, At, Ch, Rndr>
where
E: ElementType,
At: Attribute<Rndr>,
Ch: Render<Rndr>,
Rndr: Renderer,
{
type Output = E::Output;
@ -52,12 +45,7 @@ where
}
}
impl<E, At, Ch, Rndr> HtmlElement<E, At, Ch, Rndr>
where
At: Attribute<Rndr>,
Ch: Render<Rndr>,
Rndr: Renderer,
{
impl<E, At, Ch, Rndr> HtmlElement<E, At, Ch, Rndr> {
pub fn children(&self) -> &Ch {
&self.children
}
@ -79,14 +67,13 @@ impl<E, At, Ch, NewChild, Rndr> ElementChild<Rndr, NewChild>
for HtmlElement<E, At, Ch, Rndr>
where
E: ElementWithChildren,
At: Attribute<Rndr>,
Ch: Render<Rndr> + TupleBuilder<NewChild>,
<Ch as TupleBuilder<NewChild>>::Output: Render<Rndr>,
Ch: Render<Rndr> + TupleBuilder,
<Ch as TupleBuilder>::Output<NewChild>: Render<Rndr>,
Rndr: Renderer,
NewChild: Render<Rndr>,
{
type Output =
HtmlElement<E, At, <Ch as TupleBuilder<NewChild>>::Output, Rndr>;
HtmlElement<E, At, <Ch as TupleBuilder>::Output<NewChild>, Rndr>;
fn child(self, child: NewChild) -> Self::Output {
let HtmlElement {
@ -104,19 +91,25 @@ where
}
}
impl<E, At, Ch, Rndr, NewAttr> AddAttribute<NewAttr, Rndr>
for HtmlElement<E, At, Ch, Rndr>
impl<E, At, Ch, Rndr> AddAnyAttr<Rndr> for HtmlElement<E, At, Ch, Rndr>
where
E: ElementType,
At: Attribute<Rndr> + TupleBuilder<NewAttr>,
<At as TupleBuilder<NewAttr>>::Output: Attribute<Rndr>,
Ch: Render<Rndr>,
E: ElementType + CreateElement<Rndr>,
Ch: RenderHtml<Rndr>,
Rndr: Renderer,
At: Attribute<Rndr>,
Rndr: Renderer,
{
type Output =
HtmlElement<E, <At as TupleBuilder<NewAttr>>::Output, Ch, Rndr>;
type Output<SomeNewAttr: Attribute<Rndr>> = HtmlElement<
E,
<At as NextAttribute<Rndr>>::Output<SomeNewAttr>,
Ch,
Rndr,
>;
fn add_attr(self, attr: NewAttr) -> Self::Output {
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr> {
let HtmlElement {
tag,
attributes,
@ -125,11 +118,18 @@ where
} = self;
HtmlElement {
tag,
attributes: attributes.next_tuple(attr),
attributes: attributes.add_any_attr(attr),
children,
rndr,
}
}
fn add_any_attr_by_ref<NewAttr: Attribute<Rndr>>(
self,
attr: &NewAttr,
) -> Self::Output<NewAttr> {
todo!()
}
}
pub trait ElementChild<Rndr, NewChild>

View File

@ -157,6 +157,20 @@ where
}
}
impl<R> NextAttribute<R> for On<R>
where
R: DomRenderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
impl<R> ToTemplate for On<R>
where
R: DomRenderer,
@ -441,6 +455,7 @@ generate_event_types! {
}
// Export `web_sys` event types
use super::attribute::NextAttribute;
pub use web_sys::{
AnimationEvent, BeforeUnloadEvent, CompositionEvent, CustomEvent,
DeviceMotionEvent, DeviceOrientationEvent, DragEvent, ErrorEvent, Event,

View File

@ -1,7 +1,10 @@
use super::{attribute::Attribute, element::ElementType};
use super::{
attribute::{Attribute, NextAttribute},
element::ElementType,
};
use crate::{
html::element::HtmlElement, prelude::Render, renderer::Renderer,
view::AddAttribute,
view::add_attr::AddAnyAttr,
};
use std::marker::PhantomData;
@ -70,22 +73,40 @@ where
fn rebuild(self, _state: &mut Self::State) {}
}
impl<E, C, Rndr> NextAttribute<Rndr> for NodeRefAttr<E, C, Rndr>
where
E: ElementType,
C: NodeRefContainer<E, Rndr>,
Rndr: Renderer,
Rndr::Element: PartialEq,
{
type Output<NewAttr: Attribute<Rndr>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
pub trait NodeRefAttribute<E, C, Rndr>
where
E: ElementType,
C: NodeRefContainer<E, Rndr>,
Rndr: Renderer,
Rndr::Element: PartialEq,
{
fn node_ref(
self,
container: C,
) -> <Self as AddAttribute<NodeRefAttr<E, C, Rndr>, Rndr>>::Output
) -> <Self as AddAnyAttr<Rndr>>::Output<NodeRefAttr<E, C, Rndr>>
where
Self: Sized + AddAttribute<NodeRefAttr<E, C, Rndr>, Rndr>,
<Self as AddAttribute<NodeRefAttr<E, C, Rndr>, Rndr>>::Output:
Self: Sized + AddAnyAttr<Rndr>,
<Self as AddAnyAttr<Rndr>>::Output<NodeRefAttr<E, C, Rndr>>:
Render<Rndr>,
{
self.add_attr(node_ref(container))
self.add_any_attr(node_ref(container))
}
}
@ -97,5 +118,6 @@ where
Ch: Render<Rndr>,
C: NodeRefContainer<E, Rndr>,
Rndr: Renderer,
Rndr::Element: PartialEq,
{
}

View File

@ -1,4 +1,4 @@
use super::attribute::Attribute;
use super::attribute::{Attribute, NextAttribute};
use crate::{
renderer::DomRenderer,
view::{Position, ToTemplate},
@ -62,6 +62,22 @@ where
}
}
impl<K, P, R> NextAttribute<R> for Property<K, P, R>
where
K: AsRef<str>,
P: IntoProperty<R>,
R: DomRenderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
impl<K, P, R> ToTemplate for Property<K, P, R>
where
K: AsRef<str>,

View File

@ -1,4 +1,4 @@
use super::attribute::Attribute;
use super::attribute::{Attribute, NextAttribute};
use crate::{
renderer::DomRenderer,
view::{Position, ToTemplate},
@ -60,6 +60,21 @@ where
}
}
impl<S, R> NextAttribute<R> for Style<S, R>
where
S: IntoStyle<R>,
R: DomRenderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(self, new_attr)
}
}
impl<S, R> ToTemplate for Style<S, R>
where
S: IntoStyle<R>,

View File

@ -17,13 +17,13 @@ macro_rules! mathml_global {
paste::paste! {
pub fn $attr<V>(self, value: V) -> HtmlElement <
[<$tag:camel>],
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
Ch, Rndr
>
where
V: AttributeValue<Rndr>,
At: TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output: Attribute<Rndr>,
At: TupleBuilder,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
{
let HtmlElement { tag, rndr, children, attributes } = self;
HtmlElement {
@ -75,13 +75,13 @@ macro_rules! mathml_elements {
$(
pub fn $attr<V>(self, value: V) -> HtmlElement <
[<$tag:camel>],
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
Ch, Rndr
>
where
V: AttributeValue<Rndr>,
At: TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
<At as TupleBuilder<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output: Attribute<Rndr>,
At: TupleBuilder,
<At as TupleBuilder>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
{
let HtmlElement { tag, rndr, children, attributes } = self;
HtmlElement {

View File

@ -0,0 +1,30 @@
use super::RenderHtml;
use crate::{html::attribute::Attribute, renderer::Renderer};
/// Allows adding a new attribute to some type, before it is rendered.
/// This takes place at compile time as part of the builder syntax for creating a statically typed
/// view tree.
///
/// Normally, this is used to add an attribute to an HTML element. But it is required to be
/// implemented for all types that implement [`RenderHtml`], so that attributes can be spread onto
/// other structures like the return type of a component.
pub trait AddAnyAttr<Rndr>
where
Rndr: Renderer,
{
type Output<SomeNewAttr: Attribute<Rndr>>: RenderHtml<Rndr>;
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>;
fn add_any_attr_by_ref<NewAttr: Attribute<Rndr>>(
self,
attr: &NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>;
}

View File

@ -2,6 +2,7 @@ use crate::{hydration::Cursor, renderer::Renderer, ssr::StreamBuilder};
use parking_lot::RwLock;
use std::sync::Arc;
pub mod add_attr;
pub mod any_view;
pub mod either;
pub mod error_boundary;
@ -51,32 +52,6 @@ impl core::fmt::Display for NeverError {
impl std::error::Error for NeverError {}
/// The `AddAttr` trait allows adding a new attribute to some type, before it is rendered.
/// This takes place at compile time as part of the builder syntax for creating a statically typed
/// view tree.
///
/// Normally, this is used to add an attribute to an HTML element. But it is required to be
/// implemented for all types that implement [`RenderHtml`], so that attributes can be spread onto
/// other structures like the return type of a component.
pub trait AddAttribute<NewAttr, Rndr>: Sized
where
Rndr: Renderer,
{
/// The type of the modified view after the attribute has been added.
type Output;
/// Adds an owned attribute to the view.
fn add_attr(self, attr: NewAttr) -> Self::Output;
/// Adds the attribute to the view by reference.
fn add_attr_by_ref(self, attr: &NewAttr) -> Self::Output
where
NewAttr: Sized + Clone,
{
self.add_attr(attr.clone())
}
}
/// The `RenderHtml` trait allows rendering something to HTML, and transforming
/// that HTML into an interactive interface.
///

View File

@ -4,7 +4,7 @@ use super::{
};
use crate::{
html::{
attribute::{Attribute, AttributeKey, AttributeValue},
attribute::{Attribute, AttributeKey, AttributeValue, NextAttribute},
class::IntoClass,
style::IntoStyle,
},
@ -92,6 +92,21 @@ where
fn rebuild(self, _state: &mut Self::State) {}
}
impl<K, const V: &'static str, R> NextAttribute<R> for StaticAttr<K, V>
where
K: AttributeKey,
R: Renderer,
{
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
(StaticAttr::<K, V> { ty: PhantomData }, new_attr)
}
}
#[derive(Debug)]
pub struct Static<const V: &'static str>;

View File

@ -3,8 +3,9 @@ use super::{
ToTemplate,
};
use crate::{
html::attribute::Attribute,
hydration::Cursor,
view::{AddAttribute, StreamBuilder},
view::{add_attr::AddAnyAttr, StreamBuilder},
};
use const_str_slice_concat::{
const_concat, const_concat_with_separator, str_from_buffer,
@ -46,6 +47,31 @@ where
}
}
impl<Rndr> AddAnyAttr<Rndr> for ()
where
Rndr: Renderer,
{
type Output<SomeNewAttr: Attribute<Rndr>> = ();
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
}
fn add_any_attr_by_ref<NewAttr: Attribute<Rndr>>(
self,
attr: &NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
}
}
impl<R: Renderer> Mountable<R> for () {
fn unmount(&mut self) {}
@ -143,6 +169,34 @@ impl<A: ToTemplate> ToTemplate for (A,) {
}
}
impl<A, Rndr> AddAnyAttr<Rndr> for (A,)
where
A: AddAnyAttr<Rndr>,
Rndr: Renderer,
{
type Output<SomeNewAttr: Attribute<Rndr>> = (A::Output<SomeNewAttr>,);
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
(self.0.add_any_attr(attr),)
}
fn add_any_attr_by_ref<NewAttr: Attribute<Rndr>>(
self,
attr: &NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
(self.0.add_any_attr_by_ref(attr),)
}
}
macro_rules! impl_view_for_tuples {
($first:ident, $($ty:ident),* $(,)?) => {
impl<$first, $($ty),*, Rndr> Render<Rndr> for ($first, $($ty,)*)
@ -295,25 +349,36 @@ macro_rules! impl_view_for_tuples {
}
}
impl<NewAttr, $first, $($ty),*, Rndr> AddAttribute<NewAttr, Rndr> for ($first, $($ty,)*)
where
$first: AddAttribute<NewAttr, Rndr>,
$($ty: AddAttribute<NewAttr, Rndr>),*,
NewAttr: Sized + Clone,
Rndr: Renderer
impl<$first, $($ty,)* Rndr> AddAnyAttr<Rndr> for ($first, $($ty,)*)
where
$first: AddAnyAttr<Rndr>,
$($ty: AddAnyAttr<Rndr>),*,
Rndr: Renderer,
{
type Output = (<$first as AddAttribute<NewAttr, Rndr>>::Output, $(<$ty as AddAttribute<NewAttr, Rndr>>::Output,)*);
type Output<SomeNewAttr: Attribute<Rndr>> = ($first::Output<SomeNewAttr>, $($ty::Output<SomeNewAttr>,)*);
fn add_attr(self, attr: NewAttr) -> Self::Output {
self.add_attr_by_ref(&attr)
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
self.add_any_attr_by_ref(&attr)
}
fn add_attr_by_ref(self, attr: &NewAttr) -> Self::Output where NewAttr: Sized + Clone {
fn add_any_attr_by_ref<NewAttr: Attribute<Rndr>>(
self,
attr: &NewAttr,
) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml<Rndr>,
{
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.add_attr_by_ref(attr),
$($ty.add_attr_by_ref(attr)),*
$first.add_any_attr_by_ref(&attr),
$($ty.add_any_attr_by_ref(&attr)),*
)
}
}