leptos/
attribute_interceptor.rs1use crate::attr::{
2 any_attribute::{AnyAttribute, IntoAnyAttribute},
3 Attribute, NextAttribute,
4};
5use leptos::prelude::*;
6
7type ChildBuilder<T> = dyn Fn(AnyAttribute) -> T + Send + Sync + 'static;
9
10#[component(transparent)]
39pub fn AttributeInterceptor<Chil, T>(
40 children: Chil,
43) -> impl IntoView
44where
45 Chil: Fn(AnyAttribute) -> T + Send + Sync + 'static,
46 T: IntoView + 'static,
47{
48 AttributeInterceptorInner::new(children)
49}
50
51struct AttributeInterceptorInner<T: IntoView, A> {
54 children_builder: Box<ChildBuilder<T>>,
55 children: T,
56 attributes: A,
57}
58
59impl<T: IntoView> AttributeInterceptorInner<T, ()> {
60 pub fn new<F>(children: F) -> Self
63 where
64 F: Fn(AnyAttribute) -> T + Send + Sync + 'static,
65 {
66 let children_builder = Box::new(children);
67 let children = children_builder(().into_any_attr());
68
69 Self {
70 children_builder,
71 children,
72 attributes: (),
73 }
74 }
75}
76
77impl<T: IntoView, A: Attribute> Render for AttributeInterceptorInner<T, A> {
78 type State = <T as Render>::State;
79
80 fn build(self) -> Self::State {
81 self.children.build()
82 }
83
84 fn rebuild(self, state: &mut Self::State) {
85 self.children.rebuild(state);
86 }
87}
88
89impl<T: IntoView + 'static, A> AddAnyAttr for AttributeInterceptorInner<T, A>
90where
91 A: Attribute,
92{
93 type Output<SomeNewAttr: leptos::attr::Attribute> =
94 AttributeInterceptorInner<T, <<A as NextAttribute>::Output<SomeNewAttr> as Attribute>::CloneableOwned>;
95
96 fn add_any_attr<NewAttr: leptos::attr::Attribute>(
97 self,
98 attr: NewAttr,
99 ) -> Self::Output<NewAttr>
100 where
101 Self::Output<NewAttr>: RenderHtml,
102 {
103 let attributes =
104 self.attributes.add_any_attr(attr).into_cloneable_owned();
105
106 let children =
107 (self.children_builder)(attributes.clone().into_any_attr());
108
109 AttributeInterceptorInner {
110 children_builder: self.children_builder,
111 children,
112 attributes,
113 }
114 }
115}
116
117impl<T: IntoView + 'static, A: Attribute> RenderHtml
118 for AttributeInterceptorInner<T, A>
119{
120 type AsyncOutput = T::AsyncOutput;
121 type Owned = AttributeInterceptorInner<T, A::CloneableOwned>;
122
123 const MIN_LENGTH: usize = T::MIN_LENGTH;
124
125 fn dry_resolve(&mut self) {
126 self.children.dry_resolve()
127 }
128
129 fn resolve(
130 self,
131 ) -> impl std::future::Future<Output = Self::AsyncOutput> + Send {
132 self.children.resolve()
133 }
134
135 fn to_html_with_buf(
136 self,
137 buf: &mut String,
138 position: &mut leptos::tachys::view::Position,
139 escape: bool,
140 mark_branches: bool,
141 _extra_attrs: Vec<AnyAttribute>,
142 ) {
143 self.children.to_html_with_buf(
144 buf,
145 position,
146 escape,
147 mark_branches,
148 vec![],
149 )
150 }
151
152 fn hydrate<const FROM_SERVER: bool>(
153 self,
154 cursor: &leptos::tachys::hydration::Cursor,
155 position: &leptos::tachys::view::PositionState,
156 ) -> Self::State {
157 self.children.hydrate::<FROM_SERVER>(cursor, position)
158 }
159
160 async fn hydrate_async(
161 self,
162 cursor: &leptos::tachys::hydration::Cursor,
163 position: &leptos::tachys::view::PositionState,
164 ) -> Self::State {
165 self.children.hydrate_async(cursor, position).await
166 }
167
168 fn into_owned(self) -> Self::Owned {
169 AttributeInterceptorInner {
170 children_builder: self.children_builder,
171 children: self.children,
172 attributes: self.attributes.into_cloneable_owned(),
173 }
174 }
175}